From aa67bd31f5a2cb897676c0d5939f214f342324ff Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Mon, 19 Oct 2026 21:10:08 +0200 Subject: [PATCH] wip(L6, side branch): screen-space far gate; no-op cutoff removed; cost within noise vs L4 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ED33VnqepD4YY6gDAAA832 --- .../include/VoxelForgeCore/VoxelForgeCore.h | 76 +--- Standalone/Core/src/FloatingIslandsInternal.h | 7 - Standalone/Core/src/VoxelForgeCore.cpp | 83 ++--- Standalone/Core/src/WorldAssemblerInternal.h | 303 ++-------------- .../WorldAssemblerM3/Linux6/REPORT.md | 102 ++++++ .../Linux6/captures/lod6_to_5_max_pop.jpg | Bin 0 -> 159560 bytes Standalone/Source/LinuxOffscreenCapture.cpp | 12 +- Standalone/Source/LinuxScreenSpaceGate.h | 335 ++++++++++++++---- Standalone/Source/StreamBenchmarkHeadless.cpp | 5 +- Standalone/Source/StreamBenchmarkPipeline.h | 121 +------ Standalone/Source/StreamingWorld.h | 34 +- .../Private/VoxelMarchingCubesMesher.cpp | 10 +- .../Public/VoxelMarchingCubesMesher.h | 1 + 13 files changed, 474 insertions(+), 615 deletions(-) create mode 100644 Standalone/Research/WorldAssemblerM3/Linux6/REPORT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux6/captures/lod6_to_5_max_pop.jpg diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h index 2ad491c..b6caf5f 100644 --- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h @@ -137,6 +137,7 @@ namespace VoxelForgeCore double marchingCubesGradientNormalsMilliseconds = 0.0; double marchingCubesEdgeInterpolationMilliseconds = 0.0; double marchingCubesVertexDedupStreamMilliseconds = 0.0; + double marchingCubesSkirtMilliseconds = 0.0; double marchingCubesOtherMilliseconds = 0.0; std::uint64_t marchingCubesGradientFallbackDensityCalls = 0; std::uint64_t marchingCubesEdgeMidpointDensityCalls = 0; @@ -146,11 +147,6 @@ namespace VoxelForgeCore double meshAssemblyMilliseconds = 0.0; double meshingMilliseconds = 0.0; double featureListMilliseconds = 0.0; - std::array visualDetailOctaveEvaluations{}; - std::array visualDetailOctaveBudgetEligible{}; - std::array visualDetailOctaveSkips{}; - std::array visualDetailOctaveMilliseconds{}; - std::array visualDetailOctaveEligibleMilliseconds{}; double farShellMilliseconds = 0.0; double farStratumShellMilliseconds = 0.0; double farConnectorShellMilliseconds = 0.0; @@ -585,8 +581,6 @@ namespace VoxelForgeCore struct FieldInterval; struct FieldDetailEnvelope; struct FieldBlockContract; - struct VisualDetailRecipeContractV1; - struct VisualDetailCutoffV1; class VFCORE_API FloatingIslandFieldV1 { @@ -609,7 +603,6 @@ namespace VoxelForgeCore std::int64_t maximumZSubvoxels) const noexcept; FloatingIslandInfluenceBounds influenceBounds() const noexcept; FieldDetailEnvelope detailEnvelope() const noexcept; - VisualDetailRecipeContractV1 visualDetailContract() const; FieldBlockContract contract() const noexcept; const FloatingIslandRecipeV1* recipe() const noexcept { return recipe_; } const FloatingIslandPlanV1* plan() const noexcept { return plan_; } @@ -659,12 +652,6 @@ namespace VoxelForgeCore std::uint64_t columnMisses = 0; std::uint64_t columnConflictMisses = 0; double columnComputeMilliseconds = 0.0; - std::array visualDetailOctaveEvaluations{}; - std::array visualDetailOctaveBudgetEligible{}; - std::array visualDetailOctaveSkips{}; - std::array visualDetailOctaveMilliseconds{}; - std::array visualDetailOctaveEligibleMilliseconds{}; - bool profileVisualDetailOctaves = false; bool captureCoordinates = false; bool coordinateCaptureComplete = true; std::vector> evaluatedCoordinates; @@ -684,44 +671,6 @@ namespace VoxelForgeCore std::int64_t minimumFeatureSizeSubvoxels = 1; }; - // A recipe may opt its display-only noise terms into this shared octave contract. - // A missing declaration keeps the exact evaluator. The per-call amplitude includes - // every downstream scale, so wavelength alone can never authorize dropping a term. - struct VisualDetailOctaveTermV1 - { - std::uint64_t stableId = 0; - std::int64_t baseWavelengthSubvoxels = 0; - std::int64_t maximumDisplacementSubvoxels = 0; - std::uint32_t octaveCount = 0; - std::uint32_t maximumNoiseAbsQ16 = 2u * 65536u; - }; - - inline constexpr std::uint32_t CurrentVisualDetailCutoffContractVersion = 2; - - struct VisualDetailRecipeContractV1 - { - std::uint32_t version = CurrentVisualDetailCutoffContractVersion; - std::vector terms; - }; - - struct VisualDetailCutoffV1 - { - std::int64_t pixelBudgetSubvoxels = 0; - std::int64_t sampleStepSubvoxels = 0; - std::uint32_t callBudgetDivisor = 64; - bool apply = false; - }; - - struct VisualDetailCutoffResultV1 - { - std::uint32_t firstSkippedOctave = 0; - std::int64_t maximumErrorSubvoxels = 0; - }; - - VFCORE_API VisualDetailCutoffResultV1 FindVisualDetailTailCutoffV1( - const VisualDetailOctaveTermV1& term, - const VisualDetailCutoffV1& cutoff) noexcept; - struct FieldBlockContract { std::uint32_t readsChannels = 0; @@ -756,6 +705,10 @@ namespace VoxelForgeCore std::int64_t maximumZSubvoxels, std::int32_t stepVoxels) const noexcept; static void resetThreadColumnCacheStats(bool captureCoordinates = false, bool clearCache = false, std::uint32_t octaveDrop = 0) noexcept; + static void setThreadColumnOctaveDrop(std::uint32_t octaveDrop) noexcept; + static std::uint32_t threadColumnOctaveDrop() noexcept; + static void setThreadColumnInterpolationStrideSubvoxels(std::int64_t stride) noexcept; + static std::int64_t threadColumnInterpolationStrideSubvoxels() noexcept; static void setThreadColumnCacheLegacyDirectMapped(bool enabled) noexcept; static const HeightfieldColumnCacheStats& threadColumnCacheStats() noexcept; FieldInterval interval(std::int64_t minimumXSubvoxels, @@ -766,15 +719,6 @@ namespace VoxelForgeCore std::int64_t maximumZSubvoxels) const noexcept; FieldInfluenceBounds influenceBounds() const noexcept; FieldDetailEnvelope detailEnvelope() const noexcept; - VisualDetailRecipeContractV1 visualDetailContract() const; - static void setThreadVisualDetailCutoff(VisualDetailCutoffV1 cutoff) noexcept; - static VisualDetailCutoffV1 threadVisualDetailCutoff() noexcept; - static void setThreadColumnOctaveDrop(std::uint32_t octaveDrop) noexcept; - static std::uint32_t threadColumnOctaveDrop() noexcept; - static void setThreadVisualDetailProfileEnabled(bool enabled) noexcept; - static bool threadVisualDetailProfileEnabled() noexcept; - static void setThreadColumnInterpolationStrideSubvoxels(std::int64_t stride) noexcept; - static std::int64_t threadColumnInterpolationStrideSubvoxels() noexcept; FieldBlockContract contract() const noexcept; const WorldAssemblyRecipeV1* recipe() const noexcept { return recipe_; } const WorldAssemblyStratumPlanV1* plan() const noexcept { return plan_; } @@ -1111,15 +1055,7 @@ namespace VoxelForgeCore // Incremente quand le plafond lointain change. Les hashes LOD4-8 incluent cette version; // densite et hashes LOD0-3 restent inchanges. std::uint32_t farHorizonVersion = 3; - // Display mesh only. Zero preserves the exact field. Collision, queries and edits - // never set this option. - std::int64_t visualDetailPixelBudgetSubvoxels = 0; - std::int32_t visualDetailSampleStepVoxels = 0; - std::uint32_t visualDetailCallBudgetDivisor = 64; - bool profileVisualDetailOctaves = false; - bool visualDetailCutoffEnabled = false; - std::uint32_t visualDetailContractVersion = 0; - // Diagnostic controls used only by the Linux gate's historical negative controls. + // Linux research controls for reproducing L5 negative controls. Defaults preserve output. std::uint32_t diagnosticGlobalOctaveDrop = 0; std::uint32_t diagnosticCellStride = 1; std::uint32_t diagnosticColumnInterpolationStride = 1; diff --git a/Standalone/Core/src/FloatingIslandsInternal.h b/Standalone/Core/src/FloatingIslandsInternal.h index 0003056..3e54f35 100644 --- a/Standalone/Core/src/FloatingIslandsInternal.h +++ b/Standalone/Core/src/FloatingIslandsInternal.h @@ -1905,13 +1905,6 @@ FieldDetailEnvelope FloatingIslandFieldV1::detailEnvelope() const noexcept return result; } -VisualDetailRecipeContractV1 FloatingIslandFieldV1::visualDetailContract() const -{ - // Current island geometry uses shape and connector terms without an octave declaration. - // Returning an empty shared contract deliberately selects exact sampling. - return {}; -} - FieldBlockContract FloatingIslandFieldV1::contract() const noexcept { FieldBlockContract result; diff --git a/Standalone/Core/src/VoxelForgeCore.cpp b/Standalone/Core/src/VoxelForgeCore.cpp index 0c174a3..58763cf 100644 --- a/Standalone/Core/src/VoxelForgeCore.cpp +++ b/Standalone/Core/src/VoxelForgeCore.cpp @@ -220,13 +220,6 @@ namespace VoxelForgeCore return Hash; } - std::uint64_t SaltVisualDetailHash(std::uint64_t Hash,std::uint32_t Version) - { - AddWord(Hash,0x5649534445544149ull,8); - AddWord(Hash,Version,4); - return Hash; - } - FStandaloneShapeWorld::EDetailVariant ToNative(DetailVariant value) { switch(value) @@ -1337,25 +1330,6 @@ namespace VoxelForgeCore std::optional World::generateTile(const TileKey& key,const TileGenerationOptions& options, std::string& error) const { - struct FScopedHeightfieldVisualDetailState - { - VisualDetailCutoffV1 Cutoff=Heightfield2D::threadVisualDetailCutoff(); - std::uint32_t OctaveDrop=Heightfield2D::threadColumnOctaveDrop(); - bool bProfile=Heightfield2D::threadVisualDetailProfileEnabled(); - std::int64_t InterpolationStride= - Heightfield2D::threadColumnInterpolationStrideSubvoxels(); - ~FScopedHeightfieldVisualDetailState() - { - Heightfield2D::setThreadVisualDetailCutoff(Cutoff); - Heightfield2D::setThreadColumnOctaveDrop(OctaveDrop); - Heightfield2D::setThreadVisualDetailProfileEnabled(bProfile); - Heightfield2D::setThreadColumnInterpolationStrideSubvoxels(InterpolationStride); - } - } VisualDetailState; - Heightfield2D::setThreadVisualDetailProfileEnabled(options.profileVisualDetailOctaves); - if(options.visualDetailCutoffEnabled - &&options.visualDetailContractVersion!=CurrentVisualDetailCutoffContractVersion) - {error="visual-detail recipe contract version is unsupported";return std::nullopt;} // Fine owner tiles had zero direct-map conflict evictions in the cold audit. Keep their // single-probe path and use associativity for the coarse coordinate families that collide. // Les tuiles proches n'avaient aucun conflit; garder leur acces simple et associer les LOD grossiers. @@ -1712,19 +1686,20 @@ namespace VoxelForgeCore out.diagnostics.featureListMilliseconds=std::chrono::duration( std::chrono::steady_clock::now()-FeatureListStart).count(); const ScopedWorldAssemblyFeatureQueriesV1 featureScope(featureQueries); - VisualDetailCutoffV1 DisplayCutoff; - DisplayCutoff.pixelBudgetSubvoxels=options.visualDetailPixelBudgetSubvoxels; - DisplayCutoff.sampleStepSubvoxels=DisplayCutoff.pixelBudgetSubvoxels>0 - ?static_cast(MeshStep)*WorldAssemblySubvoxelsPerVoxel:0; - DisplayCutoff.callBudgetDivisor=std::max(1, - options.visualDetailCallBudgetDivisor); - DisplayCutoff.apply=options.visualDetailCutoffEnabled; - Heightfield2D::setThreadVisualDetailCutoff(DisplayCutoff); - Heightfield2D::setThreadColumnOctaveDrop(options.diagnosticGlobalOctaveDrop); - Heightfield2D::setThreadColumnInterpolationStrideSubvoxels( - options.diagnosticColumnInterpolationStride>1&&level>=3 - ?static_cast(MeshStep)*WorldAssemblySubvoxelsPerVoxel - *options.diagnosticColumnInterpolationStride:0); + const bool bDiagnosticColumnSampling=options.diagnosticGlobalOctaveDrop!=0 + ||options.diagnosticColumnInterpolationStride>1; + const std::uint32_t PreviousOctaveDrop=bDiagnosticColumnSampling + ?Heightfield2D::threadColumnOctaveDrop():0; + const std::int64_t PreviousInterpolationStride=bDiagnosticColumnSampling + ?Heightfield2D::threadColumnInterpolationStrideSubvoxels():0; + if(bDiagnosticColumnSampling) + { + Heightfield2D::setThreadColumnOctaveDrop(options.diagnosticGlobalOctaveDrop); + Heightfield2D::setThreadColumnInterpolationStrideSubvoxels( + options.diagnosticColumnInterpolationStride>1&&level>=3 + ?static_cast(MeshStep)*WorldAssemblySubvoxelsPerVoxel + *options.diagnosticColumnInterpolationStride:0); + } const double ColumnMillisecondsBeforeMeshing= VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds; const auto MarchingCubesStart=std::chrono::steady_clock::now(); @@ -1732,6 +1707,11 @@ namespace VoxelForgeCore MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr, (options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr, options.profileMesherStages?&McStageTimings:nullptr); + if(bDiagnosticColumnSampling) + { + Heightfield2D::setThreadColumnOctaveDrop(PreviousOctaveDrop); + Heightfield2D::setThreadColumnInterpolationStrideSubvoxels(PreviousInterpolationStride); + } const double MarchingCubesWallMilliseconds=std::chrono::duration( std::chrono::steady_clock::now()-MarchingCubesStart).count(); const double ColumnMillisecondsAfterMeshing= @@ -1742,6 +1722,10 @@ namespace VoxelForgeCore ColumnMillisecondsAfterMeshing-ColumnMillisecondsBeforeMeshing); if(options.profileMesherStages) { + out.diagnostics.marchingCubesSkirtMilliseconds=McStageTimings.SkirtMilliseconds; + out.diagnostics.marchingCubesExclusiveMilliseconds=std::max(0.0, + out.diagnostics.marchingCubesExclusiveMilliseconds + -out.diagnostics.marchingCubesSkirtMilliseconds); out.diagnostics.marchingCubesDensityGridMilliseconds=McStageTimings.DensityGridMilliseconds; out.diagnostics.marchingCubesCellClassificationMilliseconds= McStageTimings.CellClassificationMilliseconds; @@ -1845,12 +1829,6 @@ namespace VoxelForgeCore out.diagnostics.columnCacheHits=Columns.columnHits; out.diagnostics.columnCacheMisses=Columns.columnMisses; out.diagnostics.columnCacheConflictMisses=Columns.columnConflictMisses; - out.diagnostics.visualDetailOctaveEvaluations=Columns.visualDetailOctaveEvaluations; - out.diagnostics.visualDetailOctaveBudgetEligible=Columns.visualDetailOctaveBudgetEligible; - out.diagnostics.visualDetailOctaveSkips=Columns.visualDetailOctaveSkips; - out.diagnostics.visualDetailOctaveMilliseconds=Columns.visualDetailOctaveMilliseconds; - out.diagnostics.visualDetailOctaveEligibleMilliseconds= - Columns.visualDetailOctaveEligibleMilliseconds; out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds; out.diagnostics.columnEvaluationExclusiveMilliseconds=Columns.columnComputeMilliseconds; out.diagnostics.classificationMilliseconds=Counts.Milliseconds; @@ -1920,21 +1898,6 @@ namespace VoxelForgeCore out.payloadHash=SaltFarHorizonHash(out.payloadHash,options.farHorizonVersion); out.topologyHash=SaltFarHorizonHash(out.topologyHash,options.farHorizonVersion); } - if(key.lod>=3&&options.visualDetailCutoffEnabled - &&options.visualDetailPixelBudgetSubvoxels>0) - { - const auto& DetailStats=Heightfield2D::threadColumnCacheStats(); - const bool bVisualDetailChanged=std::any_of( - DetailStats.visualDetailOctaveSkips.begin(), - DetailStats.visualDetailOctaveSkips.end(), - [](std::uint64_t Skips){return Skips!=0;}); - if(bVisualDetailChanged) - { - out.meshHash=SaltVisualDetailHash(out.meshHash,options.visualDetailContractVersion); - out.payloadHash=SaltVisualDetailHash(out.payloadHash,options.visualDetailContractVersion); - out.topologyHash=SaltVisualDetailHash(out.topologyHash,options.visualDetailContractVersion); - } - } out.hasDensityHash=out.hasMeshHash=out.hasPayloadHash=out.hasTopologyHash=true; } out.diagnostics.meshAssemblyMilliseconds=std::chrono::duration( diff --git a/Standalone/Core/src/WorldAssemblerInternal.h b/Standalone/Core/src/WorldAssemblerInternal.h index a49924b..5ce67d4 100644 --- a/Standalone/Core/src/WorldAssemblerInternal.h +++ b/Standalone/Core/src/WorldAssemblerInternal.h @@ -1031,91 +1031,19 @@ namespace world_assembly_detail // Audit-only override; normal tile generation resets this to zero before evaluating a recipe. // Surcharge reservee aux mesures; la generation remet zero avant d'evaluer une recette. inline thread_local std::uint32_t HeightfieldColumnOctaveDrop = 0; - inline thread_local VisualDetailCutoffV1 HeightfieldVisualDetailCutoff{}; - inline thread_local bool HeightfieldVisualDetailProfileEnabled = false; inline thread_local std::int64_t HeightfieldColumnInterpolationStrideSubvoxels = 0; - inline thread_local HeightfieldColumnCacheStats HeightfieldColumnStats{}; inline std::int32_t FractalNoiseQ16(std::int64_t xQ16, std::int64_t yQ16, - std::int64_t zQ16, std::uint32_t octaves, bool ridged = false, - std::int64_t baseWavelengthSubvoxels = 0, - std::int64_t maximumDisplacementSubvoxels = 0) + std::int64_t zQ16, std::uint32_t octaves, bool ridged = false) { - if(HeightfieldVisualDetailCutoff.pixelBudgetSubvoxels<=0 - &&HeightfieldColumnOctaveDrop==0&&!HeightfieldVisualDetailProfileEnabled) - { - std::int64_t ExactTotal=0,ExactWeightSum=0,ExactWeight=Q16,ExactRidgeWeight=Q16; - for(std::uint32_t Octave=0;Octave(Value*2,0,Q16); - } - ExactTotal+=MulQ16(Value,ExactWeight); - ExactWeightSum+=ExactWeight; - ExactWeight/=2; - } - if(ExactWeightSum==0)return 0; - const std::int64_t ExactAverage=(ExactTotal*Q16)/ExactWeightSum; - return static_cast(ridged?ExactAverage*2-Q16:ExactAverage); - } std::int64_t total = 0, weightSum = 0, weight = Q16, ridgeWeight = Q16; std::uint32_t effectiveOctaves=octaves; if(HeightfieldColumnOctaveDrop!=0&&octaves!=0) effectiveOctaves=std::max(1u,octaves-std::min(octaves-1,HeightfieldColumnOctaveDrop)); - VisualDetailCutoffResultV1 DetailCutoff; - const bool bDeclaredVisualTerm=baseWavelengthSubvoxels>0 - &&maximumDisplacementSubvoxels>0; - if(HeightfieldVisualDetailCutoff.pixelBudgetSubvoxels>0 - &&HeightfieldColumnOctaveDrop==0&&baseWavelengthSubvoxels>0 - &&maximumDisplacementSubvoxels>0) - { - const VisualDetailOctaveTermV1 Term{0,baseWavelengthSubvoxels, - maximumDisplacementSubvoxels,octaves,2u*static_cast(Q16)}; - DetailCutoff=FindVisualDetailTailCutoffV1(Term,HeightfieldVisualDetailCutoff); - if(DetailCutoff.firstSkippedOctave>0&&HeightfieldVisualDetailCutoff.apply) - effectiveOctaves=std::min(effectiveOctaves,DetailCutoff.firstSkippedOctave); - } - if(HeightfieldVisualDetailProfileEnabled&&bDeclaredVisualTerm - &&DetailCutoff.firstSkippedOctave>0) - for(std::uint32_t octave=DetailCutoff.firstSkippedOctave; - octave0&&HeightfieldVisualDetailCutoff.apply) - { - std::int64_t fullWeight=Q16; - for(std::uint32_t octave=0;octave( - std::chrono::steady_clock::now()-OctaveStart).count(); - ++HeightfieldColumnStats.visualDetailOctaveEvaluations[octave]; - HeightfieldColumnStats.visualDetailOctaveMilliseconds[octave]+=OctaveMilliseconds; - if(DetailCutoff.firstSkippedOctave>0&&octave>=DetailCutoff.firstSkippedOctave) - HeightfieldColumnStats.visualDetailOctaveEligibleMilliseconds[octave] - +=OctaveMilliseconds; - } std::int64_t value = noise; if (ridged) { @@ -1129,24 +1057,17 @@ namespace world_assembly_detail weight /= 2; } if (weightSum == 0) return 0; - // The cutoff retains the original denominator: omitted zero-mean terms do not - // renormalize the surviving terrain. The historical diagnostic drop above keeps - // its old renormalized path so the quality gate can reproduce S13. - const std::int64_t denominator=HeightfieldColumnOctaveDrop==0 - &&DetailCutoff.firstSkippedOctave>0&&HeightfieldVisualDetailCutoff.apply - ?fullWeightSum:weightSum; - const std::int64_t average = (total * Q16) / denominator; + const std::int64_t average = (total * Q16) / weightSum; return static_cast(ridged ? average * 2 - Q16 : average); } inline std::int32_t ReliefQ16(std::uint32_t seed, std::int64_t x, - std::int64_t y, std::int64_t wavelength, std::int64_t contrastQ16, - std::int64_t maximumDisplacementSubvoxels=0) + std::int64_t y, std::int64_t wavelength, std::int64_t contrastQ16) { const std::int32_t noise = FractalNoiseQ16( NoiseCoordinateQ16(x,wavelength)+SeedOffsetQ16(seed,730), NoiseCoordinateQ16(y,wavelength)+SeedOffsetQ16(seed,210), - SeedOffsetQ16(seed,50),2,false,wavelength,maximumDisplacementSubvoxels); + SeedOffsetQ16(seed,50),2); const std::int64_t raw = Q16/2 + noise/2; const std::int64_t contrasted = Q16/2 + MulQ16(raw-Q16/2,contrastQ16); return static_cast(SmoothQ16(static_cast( @@ -1182,22 +1103,15 @@ namespace world_assembly_detail qx+=WarpDisplacement(wx,biome.heightWarpStrengthSubvoxels); qy+=WarpDisplacement(wy,biome.heightWarpStrengthSubvoxels); } - const bool bDetailContractSafe=biome.cliffStrengthQ16==0; - const std::int64_t reliefInfluence=(std::abs(biome.mountainAmplitudeSubvoxels) - +std::abs(biome.continentAmplitudeSubvoxels)*11/40) - *std::clamp(biome.reliefStrengthQ16,0,Q16) - *std::max(0,biome.reliefContrastQ16)*3/(4*Q16*Q16); const std::int32_t relief=ReliefQ16(seed,x,y,biome.reliefWavelengthSubvoxels, - biome.reliefContrastQ16,bDetailContractSafe?reliefInfluence:0); + biome.reliefContrastQ16); const std::int32_t mountainous=static_cast(Q16 +MulQ16(relief-Q16,biome.reliefStrengthQ16)); if(outRelief)*outRelief=mountainous; const std::int64_t contX=NoiseCoordinateQ16(qx,biome.continentWavelengthSubvoxels)+SeedOffsetQ16(seed,310); const std::int64_t contY=NoiseCoordinateQ16(qy,biome.continentWavelengthSubvoxels)+SeedOffsetQ16(seed,570); const std::int64_t contZ=SeedOffsetQ16(seed,70); - const std::int64_t continentInfluence=(std::abs(biome.continentAmplitudeSubvoxels)*775+999)/1000; - const std::int32_t continent=FractalNoiseQ16(contX,contY,contZ,4,false, - biome.continentWavelengthSubvoxels,bDetailContractSafe?continentInfluence:0); + const std::int32_t continent=FractalNoiseQ16(contX,contY,contZ,4); const std::int64_t continentScale=Q16*45/100+MulQ16(mountainous,Q16*55/100); std::int64_t height=biome.baseHeightSubvoxels +MulQ16(MulQ16(continent,biome.continentAmplitudeSubvoxels),continentScale)/2; @@ -1206,19 +1120,14 @@ namespace world_assembly_detail const std::int64_t mountainX=NoiseCoordinateQ16(qx,biome.mountainWavelengthSubvoxels)+SeedOffsetQ16(seed,990); const std::int64_t mountainY=NoiseCoordinateQ16(qy,biome.mountainWavelengthSubvoxels)+SeedOffsetQ16(seed,770); const std::int64_t mountainZ=SeedOffsetQ16(seed,90); - const std::int32_t ridge=FractalNoiseQ16(mountainX,mountainY,mountainZ,4,true, - biome.mountainWavelengthSubvoxels, - bDetailContractSafe?std::abs(biome.mountainAmplitudeSubvoxels)*2:0); + const std::int32_t ridge=FractalNoiseQ16(mountainX,mountainY,mountainZ,4,true); const std::int64_t ridgeHeight=std::clamp((ridge+Q16)/2,0,Q16); height+=MulQ16(MulQ16(ridgeHeight,biome.mountainAmplitudeSubvoxels),mountainous); } const std::int64_t detailX=NoiseCoordinateQ16(x,biome.detailWavelengthSubvoxels)+11*Q16; const std::int64_t detailY=NoiseCoordinateQ16(y,biome.detailWavelengthSubvoxels)+22*Q16; const std::int64_t detailZ=SeedOffsetQ16(seed,130); - const std::int32_t detailNoise=FractalNoiseQ16(detailX,detailY,detailZ,3,false, - biome.detailWavelengthSubvoxels, - bDetailContractSafe?std::abs(biome.detailAmplitudeSubvoxels):0); - height+=MulQ16(detailNoise,biome.detailAmplitudeSubvoxels); + height+=MulQ16(FractalNoiseQ16(detailX,detailY,detailZ,3),biome.detailAmplitudeSubvoxels); return height; } @@ -1415,24 +1324,21 @@ namespace world_assembly_detail { const std::int64_t nx=NoiseCoordinateQ16(qx,swellWave)+SeedOffsetQ16(recipe.seed,190); const std::int64_t ny=NoiseCoordinateQ16(qy,swellWave)+13*Q16; - const std::int32_t swell=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,50),3, - false,swellWave,(std::abs(undulation)*5+3)/4); + const std::int32_t swell=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,50),3); cap+=WarpDisplacement(swell,undulation); } if(detail>0) { const std::int64_t nx=NoiseCoordinateQ16(x,detailWave)+5*Q16; const std::int64_t ny=NoiseCoordinateQ16(y,detailWave)+6*Q16; - const std::int32_t noise=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,210),3, - false,detailWave,(std::abs(detail)*5+3)/4); + const std::int32_t noise=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,210),3); cap-=WarpDisplacement(std::abs(noise),detail); } if(ridge>0) { const std::int64_t nx=NoiseCoordinateQ16(qx,ridgeWave)+31*Q16; const std::int64_t ny=NoiseCoordinateQ16(qy,ridgeWave)+47*Q16; - const std::int32_t ridgeNoise=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,110),4, - true,ridgeWave,std::abs(ridge)*2); + const std::int32_t ridgeNoise=FractalNoiseQ16(nx,ny,SeedOffsetQ16(recipe.seed,110),4,true); const std::int64_t hang=std::clamp((ridgeNoise+Q16)/2,0,Q16); cap-=MulQ16(hang,ridge); } @@ -1455,10 +1361,6 @@ namespace world_assembly_detail const WorldAssemblyStratumPlanV1* plan = nullptr; std::uint64_t recipeHash = 0, planHash = 0; std::int64_t x = 0, y = 0; - std::int64_t detailBudgetSubvoxels = 0,detailStepSubvoxels = 0; - std::uint32_t detailCallBudgetDivisor = 0; - bool detailApply = false; - std::uint32_t octaveDrop = 0; HeightfieldPointSample column{}; }; // Keep 65,536 entries in each worker cache; four ways tolerate coordinate families that @@ -1471,6 +1373,7 @@ namespace world_assembly_detail std::array> HeightfieldColumnCache; inline thread_local VoxelForgeCoreThreadLocal::FLazy< std::array> HeightfieldColumnCacheNextWay; + inline thread_local HeightfieldColumnCacheStats HeightfieldColumnStats{}; inline thread_local bool HeightfieldColumnCacheLegacyDirectMapped=false; inline std::size_t HeightfieldColumnCacheSet(const WorldAssemblyRecipeV1& recipe, @@ -1480,19 +1383,9 @@ namespace world_assembly_detail // entropy from widely spaced coarse LOD coordinates in its low 16 bits. // Melanger tous les bits de coordonnee avant le masque; l'ancien hash perdait de // l'entropie sur les coordonnees espacees des LOD grossiers. - const std::uint64_t DetailContext=HeightfieldVisualDetailCutoff.pixelBudgetSubvoxels>0 - ?static_cast(HeightfieldVisualDetailCutoff.pixelBudgetSubvoxels) - ^std::rotl(static_cast( - HeightfieldVisualDetailCutoff.sampleStepSubvoxels),11) - ^(static_cast( - HeightfieldVisualDetailCutoff.callBudgetDivisor)<<19) - ^(static_cast(HeightfieldVisualDetailCutoff.apply)<<53) - :0; std::uint64_t mixed=static_cast(x)*0x9E3779B185EBCA87ull ^std::rotl(static_cast(y)*0xC2B2AE3D27D4EB4Full,29) - ^recipe.contentHash^std::rotl(plan.contentHash,17) - ^DetailContext - ^(static_cast(HeightfieldColumnOctaveDrop)<<37); + ^recipe.contentHash^std::rotl(plan.contentHash,17); mixed^=mixed>>30; mixed*=0xBF58476D1CE4E5B9ull; mixed^=mixed>>27; @@ -1538,8 +1431,7 @@ namespace world_assembly_detail const std::int64_t nx=NoiseCoordinateQ16(x,wave)+SeedOffsetQ16(featureSeed,1700); const std::int64_t ny=NoiseCoordinateQ16(y,wave)+SeedOffsetQ16(featureSeed,2900); const std::int32_t noise=FractalNoiseQ16(nx,ny, - SeedOffsetQ16(featureSeed,4100),3,false,wave, - std::abs(feature.parametersSubvoxels[0])); + SeedOffsetQ16(featureSeed,4100),3); cap+=MulQ16(MulQ16(noise,feature.parametersSubvoxels[0]),fade); } } @@ -1650,14 +1542,8 @@ namespace world_assembly_detail C11.capHeightSubvoxels); return Out; } - const std::int64_t DetailBudget=HeightfieldVisualDetailCutoff.pixelBudgetSubvoxels; - const bool bVisualContextActive=DetailBudget>0; - const std::int64_t DetailStep=DetailBudget>0 - ?HeightfieldVisualDetailCutoff.sampleStepSubvoxels:0; - const std::uint32_t DetailDivisor=DetailBudget>0 - ?HeightfieldVisualDetailCutoff.callBudgetDivisor:0; - const bool DetailApply=DetailBudget>0&&HeightfieldVisualDetailCutoff.apply; - const std::uint32_t OctaveDrop=HeightfieldColumnOctaveDrop; + if(HeightfieldColumnOctaveDrop!=0) + return EvaluateColumn(recipe,plan,x,y); auto& cache=HeightfieldColumnCache.Get(); auto& nextWay=HeightfieldColumnCacheNextWay.Get(); HeightfieldColumnCacheEntry* replacement=nullptr; @@ -1668,23 +1554,11 @@ namespace world_assembly_detail const std::uint64_t mixed=(static_cast(x)*0x9E3779B185EBCA87ull) ^std::rotl(static_cast(y)*0xC2B2AE3D27D4EB4Full,23) ^static_cast(reinterpret_cast(&recipe)) - ^std::rotl(static_cast(reinterpret_cast(&plan)),41) - ^static_cast(DetailBudget) - ^std::rotl(static_cast(DetailStep),11) - ^(static_cast(DetailDivisor)<<19) - ^(static_cast(DetailApply)<<53) - ^(static_cast(OctaveDrop)<<37); + ^std::rotl(static_cast(reinterpret_cast(&plan)),41); replacement=&cache[static_cast(mixed)&(cache.size()-1)]; if(replacement->recipe==&recipe&&replacement->plan==&plan &&replacement->recipeHash==recipe.contentHash - &&replacement->planHash==plan.contentHash&&replacement->x==x&&replacement->y==y - &&replacement->octaveDrop==OctaveDrop - &&(bVisualContextActive - ?replacement->detailBudgetSubvoxels==DetailBudget - &&replacement->detailStepSubvoxels==DetailStep - &&replacement->detailCallBudgetDivisor==DetailDivisor - &&replacement->detailApply==DetailApply - :replacement->detailBudgetSubvoxels==0)) + &&replacement->planHash==plan.contentHash&&replacement->x==x&&replacement->y==y) {++HeightfieldColumnStats.columnHits;return replacement->column;} if(replacement->recipe!=nullptr)++HeightfieldColumnStats.columnConflictMisses; } @@ -1698,14 +1572,7 @@ namespace world_assembly_detail auto& candidate=cache[first+way]; if(candidate.recipe==&recipe&&candidate.plan==&plan &&candidate.recipeHash==recipe.contentHash&&candidate.planHash==plan.contentHash - &&candidate.x==x&&candidate.y==y - &&candidate.octaveDrop==OctaveDrop - &&(bVisualContextActive - ?candidate.detailBudgetSubvoxels==DetailBudget - &&candidate.detailStepSubvoxels==DetailStep - &&candidate.detailCallBudgetDivisor==DetailDivisor - &&candidate.detailApply==DetailApply - :candidate.detailBudgetSubvoxels==0)) + &&candidate.x==x&&candidate.y==y) {++HeightfieldColumnStats.columnHits;return candidate.column;} if(candidate.recipe==nullptr&&replacementWay==HeightfieldColumnCacheWays) replacementWay=way; @@ -1736,8 +1603,7 @@ namespace world_assembly_detail HeightfieldColumnStats.coordinateCaptureComplete=false; } } - entry={&recipe,&plan,recipe.contentHash,plan.contentHash,x,y, - DetailBudget,DetailStep,DetailDivisor,DetailApply,OctaveDrop,column}; + entry={&recipe,&plan,recipe.contentHash,plan.contentHash,x,y,column}; return column; } @@ -1827,48 +1693,6 @@ namespace detail_world_assembly } } -VisualDetailCutoffResultV1 FindVisualDetailTailCutoffV1( - const VisualDetailOctaveTermV1& Term,const VisualDetailCutoffV1& Cutoff) noexcept -{ - VisualDetailCutoffResultV1 Result; - if(Term.octaveCount<2||Term.baseWavelengthSubvoxels<=0 - ||Term.maximumDisplacementSubvoxels<=0||Cutoff.pixelBudgetSubvoxels<=0 - ||Cutoff.sampleStepSubvoxels<=0||Cutoff.callBudgetDivisor==0)return Result; - std::int64_t TotalWeight=0,Weight=65536; - for(std::uint32_t Octave=0;Octave(1, - Cutoff.pixelBudgetSubvoxels/Cutoff.callBudgetDivisor); - for(std::uint32_t First=1;First=First) - { - const std::int64_t Wave=Term.baseWavelengthSubvoxels - /static_cast(1ull<(Octave,62)); - bSubSampled=bSubSampled&&Wave<=2*Cutoff.sampleStepSubvoxels; - TailWeight+=Weight; - } - Weight/=2; - } - if(!bSubSampled)continue; - const long double Bound=static_cast(Term.maximumDisplacementSubvoxels) - *Term.maximumNoiseAbsQ16*TailWeight - /(65536.0L*TotalWeight); - const std::int64_t Error=static_cast(std::ceil(Bound)); - if(Error<=PerCallBudget) - { - Result.firstSkippedOctave=First; - Result.maximumErrorSubvoxels=Error; - break; - } - } - return Result; -} - std::optional WorldAssemblyManifest::FromJson( std::string_view json, std::string& error) { @@ -2264,11 +2088,6 @@ void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates, bool cl Stats.columnMisses=0; Stats.columnConflictMisses=0; Stats.columnComputeMilliseconds=0.0; - Stats.visualDetailOctaveEvaluations.fill(0); - Stats.visualDetailOctaveBudgetEligible.fill(0); - Stats.visualDetailOctaveSkips.fill(0); - Stats.visualDetailOctaveMilliseconds.fill(0.0); - Stats.visualDetailOctaveEligibleMilliseconds.fill(0.0); Stats.captureCoordinates=captureCoordinates; Stats.coordinateCaptureComplete=true; Stats.evaluatedCoordinates.clear(); @@ -2277,32 +2096,18 @@ void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates, bool cl void Heightfield2D::setThreadColumnCacheLegacyDirectMapped(bool enabled) noexcept { world_assembly_detail::HeightfieldColumnCacheLegacyDirectMapped=enabled; } -void Heightfield2D::setThreadVisualDetailCutoff(VisualDetailCutoffV1 cutoff) noexcept -{world_assembly_detail::HeightfieldVisualDetailCutoff=cutoff;} - -VisualDetailCutoffV1 Heightfield2D::threadVisualDetailCutoff() noexcept -{return world_assembly_detail::HeightfieldVisualDetailCutoff;} - void Heightfield2D::setThreadColumnOctaveDrop(std::uint32_t octaveDrop) noexcept -{world_assembly_detail::HeightfieldColumnOctaveDrop=octaveDrop;} +{ world_assembly_detail::HeightfieldColumnOctaveDrop=octaveDrop; } std::uint32_t Heightfield2D::threadColumnOctaveDrop() noexcept -{return world_assembly_detail::HeightfieldColumnOctaveDrop;} - -void Heightfield2D::setThreadVisualDetailProfileEnabled(bool enabled) noexcept -{ - world_assembly_detail::HeightfieldVisualDetailProfileEnabled=enabled; - world_assembly_detail::HeightfieldColumnStats.profileVisualDetailOctaves=enabled; -} - -bool Heightfield2D::threadVisualDetailProfileEnabled() noexcept -{return world_assembly_detail::HeightfieldVisualDetailProfileEnabled;} +{ return world_assembly_detail::HeightfieldColumnOctaveDrop; } void Heightfield2D::setThreadColumnInterpolationStrideSubvoxels(std::int64_t stride) noexcept -{world_assembly_detail::HeightfieldColumnInterpolationStrideSubvoxels=std::max(0,stride);} +{ world_assembly_detail::HeightfieldColumnInterpolationStrideSubvoxels=std::max(0,stride); } std::int64_t Heightfield2D::threadColumnInterpolationStrideSubvoxels() noexcept -{return world_assembly_detail::HeightfieldColumnInterpolationStrideSubvoxels;} +{ return world_assembly_detail::HeightfieldColumnInterpolationStrideSubvoxels; } + const HeightfieldColumnCacheStats& Heightfield2D::threadColumnCacheStats() noexcept { return world_assembly_detail::HeightfieldColumnStats; } @@ -2500,64 +2305,6 @@ FieldDetailEnvelope Heightfield2D::detailEnvelope() const noexcept return out; } -VisualDetailRecipeContractV1 Heightfield2D::visualDetailContract() const -{ - VisualDetailRecipeContractV1 Contract; - if(!isValid()||recipe_->evaluatorVersion<2)return Contract; - const auto Add=[&](std::uint64_t Id,std::int64_t Wavelength, - std::int64_t Amplitude,std::uint32_t Octaves) - { - if(Wavelength>0&&Amplitude>0) - Contract.terms.push_back({Id,Wavelength,Amplitude,Octaves, - 2u*static_cast(world_assembly_detail::Q16)}); - }; - for(const auto& Biome:recipe_->biomes) - { - if(Biome.cliffStrengthQ16==0) - { - Add((static_cast(Biome.stableId)<<8)|1u, - Biome.continentWavelengthSubvoxels, - (std::abs(Biome.continentAmplitudeSubvoxels)*775+999)/1000,4); - Add((static_cast(Biome.stableId)<<8)|2u, - Biome.mountainWavelengthSubvoxels, - std::abs(Biome.mountainAmplitudeSubvoxels)*2,4); - Add((static_cast(Biome.stableId)<<8)|3u, - Biome.detailWavelengthSubvoxels, - std::abs(Biome.detailAmplitudeSubvoxels),3); - const std::int64_t ReliefInfluence=(std::abs(Biome.mountainAmplitudeSubvoxels) - +std::abs(Biome.continentAmplitudeSubvoxels)*11/40) - *std::clamp(Biome.reliefStrengthQ16,0,world_assembly_detail::Q16) - *std::max(0,Biome.reliefContrastQ16)*3 - /(4*world_assembly_detail::Q16*world_assembly_detail::Q16); - Add((static_cast(Biome.stableId)<<8)|4u, - Biome.reliefWavelengthSubvoxels,ReliefInfluence,2); - } - const std::int64_t CapUndulation=Biome.hasCapProfile - ?Biome.capUndulationSubvoxels:plan_->capUndulationSubvoxels; - const std::int64_t CapWave=Biome.hasCapProfile - ?Biome.capWavelengthSubvoxels:plan_->capWavelengthSubvoxels; - const std::int64_t CapDetail=Biome.hasCapProfile - ?Biome.capDetailAmplitudeSubvoxels:plan_->capDetailAmplitudeSubvoxels; - const std::int64_t CapDetailWave=Biome.hasCapProfile - ?Biome.capDetailWavelengthSubvoxels:plan_->capDetailWavelengthSubvoxels; - const std::int64_t CapRidge=Biome.hasCapProfile - ?Biome.capRidgeAmplitudeSubvoxels:plan_->capRidgeAmplitudeSubvoxels; - const std::int64_t CapRidgeWave=Biome.hasCapProfile - ?Biome.capRidgeWavelengthSubvoxels:plan_->capRidgeWavelengthSubvoxels; - Add((static_cast(Biome.stableId)<<8)|5u,CapWave, - (std::abs(CapUndulation)*5+3)/4,3); - Add((static_cast(Biome.stableId)<<8)|6u,CapDetailWave, - (std::abs(CapDetail)*5+3)/4,3); - Add((static_cast(Biome.stableId)<<8)|7u,CapRidgeWave, - std::abs(CapRidge)*2,4); - } - for(const auto& Feature:plan_->features) - if(Feature.type==3) - Add(Feature.stableId,Feature.parametersSubvoxels[1], - std::abs(Feature.parametersSubvoxels[0]),3); - return Contract; -} - FieldBlockContract Heightfield2D::contract() const noexcept { return {ChannelBit(FieldChannel::biome), diff --git a/Standalone/Research/WorldAssemblerM3/Linux6/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux6/REPORT.md new file mode 100644 index 0000000..1000d52 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux6/REPORT.md @@ -0,0 +1,102 @@ +# Linux6 report + +## Result + +The screen-space gate rejects today’s far meshes at every sampled LOD3–8: the largest measured same-field error is 92.0 px, and the largest sampled swap pop is 70.3 px at LOD6→5. Same-LOD regeneration is exact, the reported swaps have no cracks or floaters, and no display mesh changed in this round. + +Source base: `wip/l5-far-cutoff` at `b21a19d`; L4 reference: `7770074`. + +## Screen-space method + +For one surface tile nearest the L1 camera at each LOD, the gate rasterizes triangles on a 65×65 XY grid, excluding skirts. It compares the mesh hit to direct `Heightfield2D` ground/cap roots; slopes use ±1-voxel finite differences. For vertical residual `Δz` and slopes `sx,sy`, it computes normal distance `|Δz|/sqrt(1+sx²+sy²)`, projects that normal onto the camera’s screen plane, then divides by the one-pixel world distance at that LOD’s closest display distance. The view uses 90° horizontal FOV, 2560 px, yaw 0°, pitch −22°; one pixel is 0.000613592 rad. Same-LOD regeneration is the mesh reference for swaps; direct field roots are the reference for true surface error. + +The known failure control is a 1 m cell on an 89.427° plane, shifted sideways by 5 mm (0.005 cell): its vertical metric says 8.488 px, while its true normal distance is 5 mm and projected screen error is 0.0849 px. This confirms that vertical height can badly overstate visible displacement. + +| LOD | Closest distance (m) | Field error p50 / p99 / max (px) | Max normal distance (m) | +|---:|---:|---:|---:| +| 3 | 96 | 0.81 / 4.16 / 6.00 | 0.423 | +| 4 | 192 | 1.01 / 5.12 / 7.93 | 0.952 | +| 5 | 384 | 0.79 / 16.37 / 92.02 | 25.122 | +| 6 | 768 | 0.51 / 9.75 / 70.34 | 37.644 | +| 7 | 1,536 | 0.29 / 9.04 / 42.36 | 42.714 | +| 8 | 3,072 | 0.83 / 18.03 / 47.18 | 93.175 | + +| Swap | Distance (m) | Pop p50 / p99 / max (px) | Max normal distance (m) | +|---|---:|---:|---:| +| 3→2 | 96 | 0.61 / 3.77 / 6.05 | 0.367 | +| 4→3 | 192 | 0.75 / 4.63 / 7.51 | 0.896 | +| 5→4 | 384 | 0.00 / 9.58 / 31.78 | 8.139 | +| 6→5 | 768 | 0.00 / 9.75 / 70.34 | 37.644 | +| 7→6 | 1,536 | 0.00 / 9.04 / 42.36 | 42.714 | +| 8→7 | 3,072 | 0.83 / 18.03 / 47.18 | 93.175 | + +The tails are not confined to cliffs. The field maximum slopes at LOD5–8 are 8.34°, 14.09°, 6.76°, and 9.33°; LOD5’s p99 is 19.8 px below 30° and 21.1 px at 45–60°. The sampled LOD6→5 maximum is at `(-472, -1432, -463.151) m`; [the marked offscreen capture](captures/lod6_to_5_max_pop.jpg) shows that location at the 768 m swap distance. This is a sampled-tile result, not a whole-world supremum. + +All same-LOD mesh comparisons at LOD3–8 pass with zero error. The S13 octave-drop and corrected stride-2 column-interpolation controls are rejected at every LOD3–8, as are missing displayed surface, 2 m floating surface, and missing fine-ring injections. The measured swap rows have zero cracks and floaters; they reject solely on displacement. + +## Far worker time, W12 + +The L1 view has 2553 tiles. Clean-run phase totals for LOD3–8 are shown below. A separate temporary timer measured complete `FractalNoiseQ16` calls; “column outside” is clean column time minus that subtotal. The timer added 3–6% to worker time, so the split between those two column subphases is approximate; it has been removed from the final tree. + +| LOD | Worker (s) | Column outside noise (ms) | Fractal/noise calls (ms) | MC (ms) | Ground refine (ms) | Cap refine (ms) | Skirts (ms) | Assembly (ms) | +|---:|---:|---:|---:|---:|---:|---:|---:|---:| +| 3 | 1.084 | 171 | 276 | 473 | 0 | 0 | 118 | 33 | +| 4 | 2.581 | 1,000 | 534 | 440 | 0 | 304 | 196 | 102 | +| 5 | 1.829 | 658 | 358 | 100 | 464 | 22 | 161 | 70 | +| 6 | 2.761 | 1,608 | 284 | 59 | 385 | 58 | 162 | 70 | +| 7 | 1.192 | 234 | 198 | 32 | 275 | 202 | 153 | 96 | +| 8 | 2.224 | 264 | 469 | 256 | 424 | 299 | 333 | 177 | +| **Total** | **11.671** | **3,934** | **2,119** | **1,360** | **1,548** | **885** | **1,123** | **548** | + +Column work outside the fractal calls is the largest single phase (33.7% of far worker time); all column work is 51.9%. Ground and cap refinement total 2.43 worker-s, skirts 1.12 worker-s, MC 1.36 worker-s, and assembly 0.55 worker-s. The earlier 7–13% profile timed individual Perlin calls; the L6 subtotal times complete fractal calls, including their octave accumulation. + +The listed subphases total 11.52 worker-s; about 0.15 worker-s remains in timer and per-tile residuals. + +The overlap scan counted about 411,000 cross-worker neighboring duplicate XY evaluations at LOD3–8 (about 166,000 at LOD4 alone). Exact column reuse is the largest plausible lever because it targets the largest phase. I did not build it: no candidate has yet shown a screen-gated, greater-than-3% whole-view win. Stride-2 interpolation is not a safe substitute; its control fails at every far LOD. + +## Cost with the feature off + +Three interleaved W12 runs per binary, same L1 camera and forced generation. `whole_view_ready` is the comparison metric; the six whole-view hashes matched exactly. + +| Binary | Runs (s) | Median | +|---|---|---:| +| L4 reference | 2.082, 2.180, 2.183 | 2.180 s | +| L6 cleaned tree | 2.127, 2.185, 2.223 | 2.185 s (+0.2%) | + +Run-to-run spread was about 0.10 s, so the 5 ms median difference is within noise. The no-op visual cutoff contract, plan, and CLI flag were removed. The opt-in research gate retains its negative controls; skirt timing uses the existing optional stage-timing pointer, which is null on the default path. Governor stayed `powersave`; runs used CPUs 0–11, W12, 6 GiB address-space limits, and normal priority. Peak runtime RSS was 403 MiB; peak build process RSS was 874 MiB (GCC). + +## Determinism and hashes + +Clang 19 `-ffp-contract=off` W1/W12 and GCC default W1/W12 produced the same hashes. LOD0–2, exact queries, and all whole-view hashes equal L4; no recipe bump is needed. + +- Recipe / plan / manifest: `83b616a3f4fe781d` / `1ca84619c8600ee5` / `e55991385bf0ca29` (recipe version 1). +- Whole view (density / mesh / payload / topology): `c10697a8f48380c7` / `e75c9119a1498cd5` / `49a9a8d1835a6c8c` / `edcb23288b5e8724`. +- Full detail (density / mesh / payload / topology): `0ee0579bd15cbbe4` / `32e5d4964d610fee` / `1932b96ee4b1d910` / `69743c6bad3447dc`. +- Exact queries (density / unclamped / detail / collision): `e4d4a8eb95b35933` / `e4d4a8eb95b35933` / `efcba20c0c70c415` / `e604823a249029bf`. +- SHA-256 L4 binary / Clang L6 binary / GCC L6 binary: `6efb8f19a41ca3cd23aca9294ceee1e9cdd1d3dc8da23b3ee61ef8ae943aa763` / `3f5a4f88b0d911e8e66af9ef72e294926a5507a73d44fcfe868df27c666bf01e` / `de872e8fdaa9f40e8a6c096700fd998bcf35f232b06f2b4e35a374b4b0e9fa07`. +- Capture JPEG SHA-256: `483b434f3ad752dd09b82cd1c78fe81ee8cd31803ecf4dcd3c2918a8ae5d66b1` (156 KiB). + +## Failed or retried runs + +- First Clang build failed on a missing view argument in `LinuxScreenSpaceGate.h`; fixed and rebuilt successfully. +- First gate pass paired extra mesh hits incorrectly and reported same-LOD floaters at LOD5–8. The reference pairing was corrected; the final gate reports zero same-LOD displacement and no floaters. +- A `/usr/bin/time -v` wrapper attempt exited 127 because that binary is unavailable; the runs were retried with `/proc` RSS monitoring. +- The final quality gates exit 6 by design because measured displacement exceeds the 1 px rule. Builds, captures, determinism runs, and cost runs completed successfully. EGL could not write `~/.cache` in this environment, disabled shader caching, and rendered successfully on llvmpipe. + +## Note for the owner + +Today’s `assembler/world` remains at L4; this sampled view measures up to 92.0 px from the field and a 70.3 px LOD6→5 pop at the tested display distances, with the largest point marked in the capture. This round changes no default mesh or recipe output, removes the no-op cutoff, and leaves W12 cost within run-to-run noise. + +## Orchestrator review (Claude) + +- Cost re-run by me (interleaved W12, L1 view): L4 2.098/2.138/2.171 s vs L6 2.116/2.150/2.197 s + (median +0.6%, within noise), density `c10697a8f48380c7`; the L6 binary SHA matches the report. + Merged into `assembler/world` with L5 squashed in. +- The marked LOD6→5 maximum sits on the **rim of a flat-topped hill (mesa edge)**, not on open gentle + ground: the "8–14° slope" is the field slope at the sample, on the plateau top beside the drop. The + coarse mesh rounds the rim by about a cell (16–32 m at LOD5–6), so the pop is a real **silhouette snap + at sharp rims** (tens of px at 768 m), while p50 stays at 0 px. That is ordinary LOD-switch behaviour + without geomorphing or cross-fade, so a fix belongs in rendering (Unreal), not in generation. The + owner decides whether it is visible enough to matter. +- Next speed lever, per the far breakdown: exact cross-tile column reuse (column work outside noise is + 34% of far worker time; ~411k duplicate XY evaluations at LOD3–8). diff --git a/Standalone/Research/WorldAssemblerM3/Linux6/captures/lod6_to_5_max_pop.jpg b/Standalone/Research/WorldAssemblerM3/Linux6/captures/lod6_to_5_max_pop.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6c16c9f5dfa8bd82c58668e9792c81cd5289c238 GIT binary patch literal 159560 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�bj}rLoJLQWZ1vG@!t}NzeRj-aU zFAho1yk&ptCc?=;g-P$XSe?}pRbYO`$1XfTktQje#v_O{DA9f zoSPr8SodRr8J1?5zp_48(YZTc79K<+$Hws;-97lIKrbf%+?BJwB|%K>+#=XIhg{@W zdOzg)JR!kDuMnhX3S&8&w0dn1a zMS5}2QSFv|KowteR;Tgv&S;Z4x;rwno#-3Cc4QQVV%nGobZrL6GoL%gRuc7km7ARY zLoDo5yf?@8QgmNd5!KBHzeHg06ZV%dzIf$g~UafmrWJ|^$9CfDMc6L<+OM!cTjZtKFuD}CnB>gIbx z-S}j!Gf4G&oLRKC)uLj1z9${?R#bHqPl8F!R}%p-=;WWb$(K?ru*p^shtJNtC~zcM z%Qx3G*)`@*&F1ouS$5!GQAV2?nNM+=Hcjm9rQLp2BJf<$lVKME8fr zu5Iw2rE06ixnEX}Qsa|Xi5ED8p#?4zp!Q`+LU+DV26FcLUYJ+NIcyG{{k@ke0zOAO zX?snDd#&LIra}SjvyS>gX(zYP53++F&`P;&%@5csup5!+_BDRj)`80?ieczcvC-?c zKRuBj&;vE)k~O&?0QFAoq3dag>I@q2?PWw}og0tOSd7#J4Ux0!b2CS(=ls_W_*VNd zM)O`kEia?qfauV-=~QPe2bM{f?p9Yur!evHrY+)hRpo^z`{5wUwqiPP4GcZ0`}l@M zN@O_eOmlk4(+_WSVs@HRWxY{Z{#R?P_SC|_VoK~OF$J(1^_mw zU+bRv(*L{&lGeP&Aonl%TE*-ed-W41{i_qt_*fB>u zLFllp5L_*NFf)H_xW|k$V@SJLjL2{LLNpXCnBd`$hGb4SQzD^L38ZF$DABd|KPLV~ z)BwEb_qP)bz21Jk)hocSs_Z6!8^4JVJwQuUW7(?NYpH$s6^Fk>mp_y91ugA>;u?}hHEl2V$g1sjdzFpR7l%R@c32zloCU?5nabQB1A-Q0g%vhhZdn6hV{&si|q& z!oH)4xWfi|^Uf)I#zp9h^o)2e=ybw$hk{fCJYV4!KmqU;}kIt)L303E+6`JR2#;4bl==J7a0%OOu}Q; zTB$llujg@F7)+@9)}jX&A4Ep1?C_av1@9BOVtb@fY1Mj6B!0@2Y^RY8=sGcILe0Kv z3Rp%kOf=0X6la%k-{!5k0>Qg^R{z)x!GYQTef*$JCe&mkYdJpNaFLcQXPE%1a*Kr% zj4yCN{L<2s^-HPelUKcO*G$B4>CSliIa3?gWm4erE$P@ZExX)aJsVnyfTO(@8@0*g z*X1P+6Xm=wyWNR+wriGj;1T1qpJ1RjHr&fEA8{V76>;~BuF?gFZG5f2qt1{&A6CpSVWej#Cl(f#TWX79RTReI>@l4I7J0d1Zpq1jhwX0>H zWjm+PW860Wt>BI;mdu6hy~|9vXFAh`x}Q)p%r#Dk47Hd@LF)D|mTB3V_HKC6A9U}B z!=fxHFyo+sN&hFIH&5`vTpLQ9dVSeXU6WLaKf~Zjr z*{}by9kDyzG&f_YWcEKqBT;=TNm`tudztALOQ8h))>V^ubCmsP`Ri=kJF*#)gz&zH zlE|bjLqnd$drJ91EY|DzVbR$&c#xfmQNE&a@CVX7nmGd<9S3%KdyD@@7c{h2E?Zxj zub8UIZ8op0_xyRcc$wMyS`$r|%YLd_KlV zg=5?vu?s7O6>s z$5%NX+n|K1tq5)8qrld>E5N&H0gv{4btrvhTK!m=36)f`cqs6b{3T z;}919(cbkLcl`4_nq|k&=Vi8oDk}N?rq96p8mO&IAlxHMZ7AxY?Cms|4~9^Y$b>k(kU5F!bi=}n%46L zPD6)E7mz$oP-*LYiZ)le?Kh|W?+j^0$YIpBKAOTM_;aDNR}TRvm*unk0UU$ZA$drV zKYs4T`i9m}%n0Uj0;7hJf4_ zuV?a+>j#vIDX(|$vLR|rvD;)Ykr`V zJpP7Zy10&wQ?k^nEUA>ml?(EJ9v#1?VO5zc%bMmlpA6_7qmY5dGI5$z$~*XF&(O}P zfqvb^BH*~^y)BCw-Zg352{>~6m8aE+-w9GSWjwrH{6w;J0nHlAd7Qq^j!xQb7eZrWg`nj2c=)rDHEAb-ij0gjw$w0V zS8{mi9NEb{>@e>5H0L-HkL{SE>QA6j7W@hyO6IaqSYha1oL(0x=FMNkh8zzcCQ*rZr~sL_3Snb4in@VeuyTms$MV*&B$ZV1Qt!N z2ex<{61!(7mGh!a5&RKS^OH=$McW%$ghFSS@HxYjnbp0lL~+EH6&G9%i*Z#l%b(&F zTe5&}KU6Vtp;YzR%gW^_L)=)PwiyNIII68Dhk3S0mvohNCrhkS?Id!AxCpbboi|{j zHlH=yvc6Cz+b&n#?#8s>2b(LVdl^f2HGs|KUhEq#i8>jNJXx3C_Dk5;eh*_IXT_0+qg$WU9*T8Eyj30L(jsrqZolN3I)l9$6#`*<25XCp>n#pg2c+*NG9b9dV|^BIWQX|DzOZ z&gZgnvvniB?myvu~zOVb0?sg2}sMiqNFQh z`9%ThBMvA#?S@Funav2ihEqBzi?*eDcwK)Px39D^5{aYVDNuaZMN%45IoR^_-9EVY zm4hcKI-!U|uo#Rm%^J0$Q8hF~7>kx_(zyy^Um9F~_t37~4R7$8rZ9O-jsI31$FNKC z4y&SsSh(ZEAcdiWHj!l_h4BX3rFSrpr)%#LJ{XxiT*8T_Nb2Hte~+h&CPE980dH>h z$Fm0pBAwAbz5Wdn85i-oV_q!feiduNNxbAu{42{ulOY#(v2_Rc!ce3E-1d#>@adn5 zucksd*PircWjjIfsSwILTwYroChWHx(IXP`!s482jG>1||ZOc7r`ZPF|oF<5YY&4Z8lC|el@^lB!ClkKEug4V8IyK@rp zEj$Uy4Je}!&EDnGv~yh?nN7IW^`-bEQhBHpIYc@?x0CdY>*m!TI5E&eI4UyUDe{5Y zNBMv(ps{r}!qB;!*}PrVwPsmwV=1DJWq}P3TkC_SOP|or1b=-0r9Ld>`g^1u5?YyL zcg)F{j_{n=v|nC=`^JWvfc3_M9ceWv`B3s&Gk~8p2R)f^`*iHNv#RY_rX3lS7yXoH zpX(fQP8wCeeyRp~?+B8hfHmcPi}k4deJ7;QV6&8PTk4>G>03U|ExZ@N zw(r=}G;=~#SD#736n=oYmma*1m@>ZolrB0XeqRCrOL-Y*KRdcl$v8RRZlO+|JSM^jP#d3)Ul)rXok@qZmRT1eE9!?k%$v7t zsl(4sMHFT4t@A65+I_dCym7fFz50%oY_||Q>G+`6n`Ze*zZGK;9_Z!wU=j^kIB;9S z4zY>1v94(s%pFYCUAN(P`L7F3Iq*@^fo4}+2d6tqdmtSIo*O(&EYJ2x@_WL@o~*^F z3eBWhrg43fzB??Lpd$$;L*Hq}Qa;B&nO|tRia)&d+qP-=E-N9(aN+XSlhCDsPf>2* zAq_h!@P_ESjB$DIpCU_+BdzffNB^oIdh1wrN2{6#n7ZfPBhW`Qy&aqj3l>Q~ zwLjx~-4FwXL)ij}3CMZJlTh5WL$NhFT()mNysx%fpmf!eSAT*?erB-41FFSwoEHL5 zGnap6V3`v+_S!Ss^(VD$n+daj2RnrH62C~DT4nQ1Y}eOe{fA)>{qRVP#K{S*>~#-* zpqFyo;36onT@O{2&o)FL+h@6rv9RCPx-H+io?LeHGz$hK{|YP2kI47*UuCySHER>? z=FdJwQ;-E&U^E`R`0my5+l0n|Wp%<)UrIPM6z`$u!X8$!jE%9~I_ojt*;W2u-YWE^ zxt&IWOFK!qin@neqMN$Eh8T==PCjZseHeN^2qLrnsO)OvdXabJy|Q{j!hl18POc6& zY<@gmlcuPTvSCjqK!P1$almKM%4cXV0|>|3XCO+OJD|5R%YUtUY|-BRL6cfNRFq^5 z+ZIZqJNmf3OB4v^6GI;{r7LmJJWe*!e2RVlFdR`6tjO${0)!d3J#yIpMS4OW-`JE0 z$w@FPwOBm)M5;%7He|RuIq9*->?1U4ThuM5aHVwWF@v0^3U(pZIU)6+V>uG@V&j>+9Hc_p5AXK( z#T23XQ?3QbJB|)~(n3(N62e?E<{p3K%Yq%4U~voQcwq5BoxkjTBDkPXs+T>)(;Pw~Fn4pNtl%>3UxDIbD=F zKYd^>4+mZ6w4bLpOi37&^@U6A-k0A(Qmom>-L!{cv~=v&N)0=eqr80*v93291Qu4d zk|Il3X8F76*Ng7VpPdJU-!8_Zc7ncq9~@QRiq+Q3OV2&R?d5iE_cg0l6^ZAAAQibO zx5|u_pDzu9oUO!hkfY%g>1Sah8j8HY2n7nzpk(Im+0Wxc)3=*y_MSDff7A4= zemmI#i)g3bzY`#OY`ycpQAn$jJOe*WR28PBTl#DtPG~|K)mzYJ>OqFHQCz6frSvLwfMHOU*bb)X?|0 zOpKBzRw*%}c;mh#TwfTc!$T>&Pz`ce_Ky zbcdCiBQe>?@%_3LRcTlC5y#e*<9MrpXjo&?mwVsFwq+g34`aeT){j)H_Z=%vg6x+g zfOgScT2}cTm|O49-h;I)A~FQ{qv>KZY%Kt6bb54JxwCtdgstKqp|@i0xs3@hvJ8%d zn2xef^;kfjcn2My&+h&$8Y;Lz5Chc)txl#WW+Z?sKPB* z7aZ!Wu*dz%a%VyRvPSu;E8dA{dl!rXO@{XGH93+iiUQs`lIOv|U@)|+g~~GU0^QbH z|3!0>`zG8i3uTsDROh-Ajc!L8eaSTH7d<5o>bv&S9KyZjxq zD=YGyew~%nzYTtKEIa=ej!SmgNCBJ_*v4PyKNUu|C_1^g4bEl<4dQ1PRf)3qNB13E+uiOkPqHyp zN5SN*uT>>#DIv)+DT0E&QuCN1d;+*IsOB2CFh46=RJs&j%!&gmYtn!@x(5Bin#x(U z!%c@6k9^Mp{kBH!CCbCDsrFgrNQ@y=6X^z@8CAZIHH0_3@MgpC^ z7LF;x7;gsxJRBzWg?hKX?}1zxd5uM4-t^Qq$h6d+f>}1sH>pMy)9v1mTddv~Ad#ci zujC{niiwj6K&PJDj-qyL>$ccJ+Vi%Nav=}88-=|suqT)?gWzw-Jd8>GYkwZr5qD5= zrV7`x;>w{k`vcES@DA@qk|3kTj=atM!@k3LKEBu@kwa?Ds3ZAg-aoc>XPPM<@O znu4cNF{z427dr;X^u~HS6kcUPHdRm!{E_6Tx!8^V@1v7T@<7)6O5{+diFAH~*Kh^n z-7r*Otgtj+Ltl()X0{H5Y;A0~>6bCCgK5l);%}wrG@gySPnNTfxkB-#<_G=R_ro*m zF~jLuc7+}$ivRrJw$lo87;(r+NX$LTA79=!SNZZ>lv$qUlud z`=m`r=K>@gtZuM`!dw-F!1f+uSI(!BJ{lEQ{Q)C2C}Yyrnaj^wroEg)j$8! z40_8hy`lgiyPd5T6)#`%UVhgs=ju9p0{L_Th$jvnE!6j;z@yQT)JYKj9uw1*KX$Ju4K??3MPeHag zvmI$GsoEB5KpM2VC3wd~fwQY-3hI+~%MF2sMa1hRnJHc}(`J+`OCOdjw12A^F4AkZ zyLqV8#%yzJAzS@1-;F>SUvxbKSih|h$lqfI|?~#l=U&cib!@T%oPw+W(zYyF;+&=x+AXf%Nbm0%82Q z$g7ZXTpx~_F&Hl?Zz&>yq9(^LjRDZ zPNyZ8-of>+ERh)yiK;+*!Pz+rha&#Oanu_6(9!ACK2AN3P^2V{{ z#ENLr_XwvVMOE3b#Kf65*TXO6CNFxPva|E8pE( z3DT%5aeWRwHNch`vClV|fILcY&6h>^o#^-b+j<3tP zm7keD=g&>V?6Y%h8zMPaLFras32W~sL!5&P#W)0&{NqCxl8$z<^PyTw9aI^o-z`vg5iL}|+#&l+&@b}DMt&I~(n-`s81)s1K-*LpC}}!VWkDY}MknB-Q_`cempe*xjGvE!m*Zs76iQzi(K?NNf){~uKG}S7K0Jfo(f*Jb(;1=<_GQJTw$l@7qk8?ovVKvll!rq34$>>0@T1-VtO?;6FIIPU1 z)mkd@__`MS7~`#hiXxmkpsjVZN;rna`Oo)oDU?%#8(lKam9AAE)LSiJrln@l)wB1? zly!De z)kZS)3HOp)&i8mR&tu25F68IfTW;8UEN;*+QzT^#uP0bYSHxvq||kZsBP(c1Uelb|Gi%fEW5v^ zb;dy!Ip-Jy`$|7;jc4=OTqkXne28GvGO2X!br9Xrzx03izKsVPboHqw7pb*{=M*Z> zJabuA>0DHbzV>d@0-pOD;#4nmIA{5dkQeNyAAUM{3wi~+oh&EsuzTriw$zx4VWHSI zYQ@w#N$m?qQhYo4F7Tg79NSM7E3EkH}e<0@&#R}z==L7sy0rhbq;z^ zF?-FEsOBI=ldWhyZBs5OAPa@VFGbw_2qZEQQxf4-GR)ETW|V%}a{YehuyutZI+KZ{hh0=$jKUMer1k!towAB%}+!NTc z`xP^d5Qr*oeD~4Ua3}!m9GaV8JNz8=WN7Hs%dX!|Qyh?igH6e)W*jQA{g+r$*Uq+0 z=#$+bez72~yQ0=C_X)C(53Q>1z%GcBtY}Frx1peejR!`UBHG&;aDQCGgm{G#ji`s+ zki;?#4oidcec>a^*Y{R-by-dGOoMpJb`{P8*`M@A_o(O&9r=@ff@?D~(ER_kb&D(W zBZx3~=CA+!@Z(qtakO${XkXjiy`p{mvc*A3gjE9z^gvqF30ND+?Fb3H9w*#Y^wYlQ z6dIt0MQ7O7oVhO^Nn>KGs1G7@{PCJJPyK$CU;E)8E;1vN+&T4Y-llV4%1&WU+xWel z=H1gJp37)ZZt&~MMJ6%IPq$_%yp|&SuG0cd z;dG=f@VW*P$&O$XV&rloMS}A(0Yv7vjb}8TgU>?}-e{x-OS9%6k%@d?Q-g1}xaTmL#DNo~gv7xxe zxSH(=Z7JsHy~dBg|o72`{%MoU{s zujA(P3VQ!LcUG8v&RTetR@&-SF?;?(m3xQupxv=ZKB8E?H3PJcAi0|*!+uaA+2w!`6t8Jn7fxKGnAk zz&{TwwCy~dyU4j4(-sqS_Vl%%c%Fm6)>bqG@tRtqR$Ji^?@@p&!XY<;t)nzHYn@aep#X|g`{J$Oj@_x=*~%B5*P zdfqwOSQ}PRToU5u-=X)xm_fV*49s=8nFGwY)QlgjC0-n_T7dlf{}$ zNQ}c3ugDk=ot7AdBe(3?t0)`Oez%L%Nz|@nd&uwahuhCOIOc0XqzIURv?4D|;$M^8 z$#|zK2l{*_2|NzB&}BZbyld&z#6>i-oQW>osQ$_YEjNSaHbF7gujg(VSac1p1>5+v zT>#i_o64v!U$9M^ZVTlhHaoBO`W_O_2RG~NaJsouHXOpsM#A}wOB_Ikhh3^#S_D-) zHmqC9Eo=Gub+OtBf?dExWT6Gnn&q94Q9o!h|0DJ;>ZkQ`T%D!I>yrFvT@Aza{b{ia97@HTWW$A#%+?FDb(6o1T$^~;B*QZtU-07fhPe@Bb zPC^c@bq&L|vvH76Uo^WfHp>PSixW=<@7w0jGzO{!Um|Z4a|@tR4gGGDMX9ZHaFM_) zm#j(De!o~VT)(RQm2NU}N-STkStts`(X0j(yJ|{~q7{{8Kbr6}eK%%Vv!7{pl8C2h z__T_iMSu6qtH6Ff$-fn26|(%Gnb<97PYbCnVtDGmI2lMxsd}yh*x8CZJ?^+OAFM=R z1YXpTFHw`JaO-vP9vR_R50|`H0}AJxM|LZ(u>rYrUh1!6EF0y$t{XWD@Q>792iU2IpA{dNCtlZbfY=Nm zNv5l7joWdv@q?V@S|a#{Bwy8YRs5V7syg$4Wpja7@t^$UJEN`7N?#v-+K;9r{3&uQ z#3Lh$O7ox--{)g}zf)mQH_k)>Fv;6pwV)PD{V))LC0?9vHR5-;?+k^AcYcr=V62JCY2exMKTlzdu{f;WR$w!>k=mgMqj$?wXmUCwWm|? zRrG|C7a{~|q*^M*n`$;S%)cAll}m8{LsO7pk3sA*D$8F5^mp;_#eFU)lyX`eI&|G8fo-n`d{KzQMtzyd4)bT^w zjl3X9g0Vx{Xt68pENbc(&2FLvGFj#3YS6f+eF?F%&7Op?ukY6g-grT-917Bs+!#DN zUeDfh_AW&4xh)-EZEqUK1=;5(%K>9rE4-$TvIl6AQJ(=gCL-u~f)?ve-^lp+;hT*9O+}3hr(=cC$ zUZDGjlgNoYjVzn?7CB&?m(xIr14-T@%YM|^IVt{=ftir~7x&Iei4K-ELf?4%>fC~7 znjM+;-Xno-nRUMKJXh~r!x^x`#DUjEw*&u{otXZ>?lHOeXE^#Ews1-Is3v!>OSknK zuAuYmLjTd(X~$uh+CJid* zzp$5nst;G#|1}S$t5#Q_<&+vb|Eg^(xXau?!ku{+qC|MxTUnwm#?OaS#ot#&3DHmI z^wseqJQ_G5yB_=mtET1GfXo=$@`Z#VZZTZu7AUA1sHd{~KR@II#rNC&NGcQ=jdu>- z8v_aa6IxRoUd%nXS7IGLnGg6xQ9Uf{{;(o&9th1DmGBn6DB1Q77Fz zu&`#7a&Ymy8Lg$uy;Yu*IlsE){lFFsV9A0aQOQ5M%%2tRO~e!qBP6*nzqzF3mlD+P zz)A7=1PTU<4G5U1H^lx`ydb-0=r2;+A@Yx2+vBSF+mDCt@3y{j=nBv$zNyNK^X|Lj zl18)DyB%~)R8TMd+%5fUNx0Y5KjAL~Y5)@2ca7_P3w58be!O}e4GeknLPjjC1LApzc>NOaBf)MKku$+wY5^JYA8p|AFH|8@@`X$aU z>yN=vM1bM;7RLMt+(`cVrUkd!65$p2Cr-)~^%f~Da)+O7uvyAscCu*Wk`NODZ{4WdBy9IG;s?=BH24H@|mIZjkDFf<= zN4Xl#K;CVytOB%Q;~Nm8vG-@EJzp);5m2+RAeh*&A<1=ks^9*v0*CkNg1ivt<9=q0 z^5*!fUaFvFqi!tdm$k7fW)f+T@Syr$)-b|Lzjl9{hhE;cuRO^tru2JyzpL;IOw_2Q zR}n9?S~*XC%ee~~36N?_kA(E?Dr@7+#NUvkh7E&-mT2f}g%DX)bsE8}P;z1GHI(VJ zI0I7a+A_t7oT#AT)bc_m6BF}2plqelEn1F}i!wL2%M1K9W$I=+I7_O2pJM$X0j|p= z121X?YUE)!pV#z-6OMV&R8Zv^IB0g)V&DxeJ`-c^ir#C^GOpfb6}l_KTyVp5$yK@-z|CXpNrWdfns_~o~12BLUs5}1%aeDJ8L|E87|;VdP7Sr z0u#YO`*jKEI#P4KnigpL(av5)d2T(#M?m-<-Owc+yc%}`H>5aW7pE6zs&^4PZgM_} zy7<18t81|-*fEw0MompcmCFFTuMPrF_Hm}(M;-P87OyU7+)mQ*d6+R4e4?T4bXyk^ z%+;saOrH)ubO4d6^3^VD^alR@EybD~tgRIuk#YpLgpXjP_+SU7Gcu&_{mefeZS{gxBYdRNzc*CPtTedu%cb=nSpu z^$IqepHToZBHhQhgO2SL0$uMl!rr#IbPQkffeArfINK$6XK8w^q|H+0+{kx(FwR(= z9auifx@jl3zF)FPF}E%FhHwiA?)kjBvLi$=z4kvZ2i>lWtT^JP?}v3FUV{0#NUuhV$Nz(r; z=H#O6e7Z_0jzes`ebKGnJ{hs?SwiHOxrZ1{utUgJV7zkH!x6%|MU)6+xG~4b+ZepQ zvEkhMVY*vvR~8xGR7uouhFSf*(K+BkEV|`kfJw}dWOZzdX;Iq)$LoL7^___(rVnm~ zVvGDrizf%Y8rsO}GgtIxZZ|etyszK6w6lZ*j}TN?(x$F4KHj&o+~eGDBJvIEU0UwY z{4MpNR3VzbIVTdFGJBC8fr9a8obiWc?L_6y)HjckWSV$wloX1~+ zPwi`FDk!}H(i2%RBO{p5(96xT^Go8G<9BXW1u)rrp_kV|?aLo=h5Bv(TW)M{AO8+= zbk%`;kgm}0p`yYQVHPWn?GdrWoz@AynEbFS)i}g5l&m@Gr8aDQ#c)GpONHFbmfe8I zp1Ry=a9uM@5Vqa?tFUu9JEb2jv*e@HIAc<=TEkyt57(TS4Wp*@_)`pFJtP#Ge4294B}8j< zyc??@y@(ukEe0#+Ay^Q@`4;whjeyu`bNXeoom1c_RUMys59!*RoY9bRt$g!U$Y56c zHFc6Jd1UpY(p$MsS~)h{?oRE@{`cJ>tA z4>39HD4Y+@nqgJGBP&L<=j$N{|EAeylK0uP>Mdf96N>aCDk8;96yMVV5w0(?hsD6s1cYtNh*~tkcx|UUzL9P zZM+{NrzXn~jEL=KLJLhBPoKTwfS+ui>cuvzFDSotO%6;Z?XZ__e=u=G4tbg<>S06& zPs>|=B^|xiZjNpF=LfvOfl@##PK7M|1+L;Ou?|Sh_A2U!PlHb&-@hh3sGhQXbDo}V zpQ8|-oRk**pM2T>n0=yR_4P?jgXJUCR{mfRc|f;``>DlS zryvDT2?hWK?cN|%m79H@=?W)sm2 z7d%Y6G$4(JZf52~oF$oJ$z`pB^Nk)@#nWwQ7jY(Dw=c8mTPC42)%CAICvZYV$(KyZ zS0W7m+F4p9W@;!zLB4UcmY*FzLDfBtbJ|xx_r|u}Inevq>&%B%my5gjYP`X~I^Kt_ z`(0pDPYbU2F5Uhl<#BpHbGz=V6AJB|tmSTV*ifp~mc3lElOvoSTkc6W6!MJ$ORWIq zBdLW8lL4Q&6@p-l^Eit=v@#%AIV!{*ZFt;(@R}0g7OWow?UrEF?;4@yX8ine>HEM> zjk)QqLus3&ICDZ_9B9r&fra%@)YKdn2E2=LW#XQShGDQ0h42{zxl?zBfgFa4GC{(XGP`Ne1%kmN|`vZsg_-uEenHc;r0B&}S+oGA~yD z?=^=Z?yI$~xZ+F{JkMzGHk$g$lH z;Kf>R1Gn=vs;dGwGowL^np3*7eq#S<>)y-&!=<;WydIJ(LLmy+9VkO4|4u<_QIcLc z>kjRLM*sYwhj1)aKCH3GA#sm=J|&JpXgMURx9M2hJ&Vu`33en$XI!`8{@J+lyhNM51f}&^2OOMHR#U^Uo%*Rlhz*M3wN}qxT9J=2!D-3%Ht(2tmy#7nLKav|74!8bzP7BCS)OtW<2Jx*SshdU@y4i>$8 zD@nwVu%v6eUzxeNKEUaO6n5jrmgs|ipI03CXcW~p^@9Fck+or1vw08>qs*%z-aeRj zjcN+E8m?~I%;8i=8!POJXB?-^Vm57WqieLRFGApdk*T>^ZgCdJ$HOOMvtsk)0z~oi ze(LUBuh`=0CTmq%`>vUSrs)GBk27+>zQHb4Q1%;JK~8c^raUc-US*Ahb_MoqnT+f? z1?WfnJaKEC86Ny^OM#lmaR03~>i&KG^RU1lJy%*ZG=+c7!c2&zEJ0Goy)oF`N|O3W zD65^Vvum#6iXdJpeBXVSs&kjyIrE-|%(r`!wp6(krA2M|(4|RbGM84WPFZ9^#Jf&xIQ6W<5VkV3B(eC zs67MHwuV5GoO)=_Er@X`=#q0Fq)z)Iro_dMmK6;8!L*tyD-Kbjp04NYXHi1?d}zn- z<5@N)^IpmybuC>U&g7szd0qTgf{a1L$DIR(&xnUIqAk91#B2ZNo$~ZRsr-KUTLzm zqf43qnI&x2*BK8oWv@TlXUaSC!G#TOD`TiVE7guCh5M)j!Ded_MJS}luVW)qepD0I zdIMqWdqz)aR=1aR-j%Y(|F!*s;~20 zhg;z4H3RMwg&w7JSZ|zGyOlPc*n56+l$kH=cqG^jXwfmJ^CS&hq z*v1%3DvEacWtn8|n|=+AVe8;lAM`K#__cs8Qw#bxUlMTV1RR3B9UHm)o|zesBfJ&P zd0Mx|q2uAg)4m%-Xkfs%^ermVUqKAuRO~?WRbi!aH47IUB+n6)ksk2Y$0J==6xdeW zKIx?mcP~H+7pm7MUJARzJ0*!xAF%yM<6_Aahmu{k9`2<-;k1`G254y%ef3b=`M;#2 ztma-x47qL1Cz_TY+QO6Uj4%L`IL*E#+2wv5k)5QKiTYWV z1zdhgIJ$}N-nO|!9my9_NV)0Bx4rLG?!-C|PXp@~pdXuWdG4Erro5f>f4bL5u`ZB_ zo8WURX>(q2MOwkTjh@8Vj;+*e1~~31^&CR6f*z{DQexCkjv><#-hHa?U`)8wojJX_ z1zuy|HFtT7r{jf|KqyYPb4W`uXNlLmOFY)>uzo~v^2+$w=_y!XCBiz6ytw~ALuVe& zX1@Oao-@;#R<|>?ul>%{zP9$gr|qDki6$h7ZBQkY*i-v&+G@vsNP6JU)C&6 z4;po6_9Fv*WK^EJGWKpbQ!eV#Xg?Px+N&#EbF(t=E90iY7L&y`!T;1(GB){Yz_S{n*={xVFpAm`$mpPZBGCE<_;g ztS7&a;6}WtO)HC9(49v3=Wh?k)h)arUf}_G?y@fHC+~8G}*q zuJlzdL|^!9>)oqN&^6ti1_BB*hR^gV|@ zpxE1%EWf94wA`s)Go_W1<$u|}Y0m#pVF*SpwO2kFSCT9qs_k9|S4Qp2JF;Bb^Izul z38={_sFknjQI<{*kE6V_Lo7t^?VmNa;Cm9i&6*?Mzsw7E#T$nd!}V^*J4T z|CWj;asF-B+Ut)LYyboeBZ&Sh4`uB9=iMtTJ#z13RmjFR9tUN23)uE{o0D_B?B41p9vSNSVSQ41vr&j;rUoUWvL$sd_KEoG-9 z**yr~lvH`>axWRI+beN?S;no!13IwNLe@>8UdJ<)`ww@4-`cL$WLgp7Wyw!_!AG%?IuuSz85r&snDgx&&+6f%#`8(q z+gZ|qQ+@Wf^u34BC6wtbW2z%R{KD62VA?z974$~1^`(U zDIepq78+~#m(HeRQ5|kWSad|2Qi^G_)zP+6;ivvcvv_i5gNkc_KZ;dP+++Zk8aE!H zNr$nr2M@D3>tsuEKPGxVEbv1AHa`8y(>WJ|FHwB! zf;3~8d3q#P!!x=0zg3m5{Q-AdnbcpOVnBLT%Q6D4S+oCb9!dr_orHvs&3J2J9m4q( z>E8VPhKxUIe=7{l*&0}@{rY-sjWU}~{{dQxvp8ToTBu-!sGs?BPC+xh@t>SejT7U} zt``LXZh~$-E*N-o97Vrw5$)So?g3rIejOW0GlU1_y3cuL763BJrhDfTm2itEEJQoM z#Di%iSGl}x1JGZ8T020okh9!FLdhnQH~u}?EkAp-z7%2yecFJ~ZqL7mfLFW%2R9Oy z_<9oIi4U1)pCPXk;>g;>@tEE9(rIERTf<16EZjOU7`H`G;{6W1b?xNmplkgKTb<#_ zM>Z8Pd1ZGT6!^=+ZOik^V+VD1$C$~~C5_|gxObt3Gc775F`@)$G-BlUlC7n_Ip?=6 zxjXYEn>~R~&<(Hg)&q&eXSG#`HRC$7&L_arL{92wsDk#2z5R2vTmY6Gcb!)c9CMz8 zXWFgpB;I)VC+JXX|M|!Jf3eepFQ12ev4GDg1RIsZnQR(00%dd-|}Oxw4jZd;0VB}R3g_Q zNDJskh74`Qc>e&%iQ=Aoa(*=>Z+X_KQY|0!pEI0XY;-1vCBYKOk|SSFN=Nbce}bMh zZpzwkZHEh_H0()31f{EOPUU~+!P4fA&~XfJ2bu(=qN;_4T8PuV)LPkmuFUzrK7;%$ zFO3fAACcyD;KmS{vlX(?@T9pLwYKz1%-O?(C@<`^e{uQP#VR>zLI;4Uvo+*~HVt(O z39j~S4A+tfj|?uXyaI0@FfnkzQMNRyi|1r@%OEN}2gRC(^_F{zd$-)eY2rbc^(h;Z zBulz-Fi78xcc@Zk@_O8m01r=08*C^T9RtEpUQ03O0UH-DtM?5k3@uqFjz`@WIEQtx>A;yO)por0>i!HnW}+1+&t6J#(N zf0^-wgE*+BJw3?RlY3F;)`Fbx{;;lQ+vum!bvG~nja&N;Jh{c;D$nPi>5Qo5L)x?_ zJ5*sszE6XrT2Dv3Y?!U=R==;_KbT#>IH7oI7|E)9>^lmJ#i@hxg0`Cmr&rgK@)2M) zb=rt@|HFcN@p7*INiGc*5uma*aAMj};&TKnWp%MNy(zYcvuuuIVw3A#S&X9kbjLv- z(LET59V{1;Tu=~#hiD2#x0OK~Ya&<3yTPHU#9zQ7`CPGsOY)yJC2p6sxdAjO@5(`7 z1Dh#E8@e3rVD96t}47%$VIR3T^eqdF|wm)$#(|2v+UT(Q-QPIhv74Ajv_8Xj7EyhbPhzGMQT|L^++1^DqbFWr9t!;6nF zxqloLBX19M6_KDloP!u0BtBqNZ~yv)HtH9P%9=l0W8d6#EuUNn-?XQHrqEger<_<> z8EAFy#mBhgf4j|OE)BGZZ^+$kyMmklHEg~@#xjNYK1;Qs-e?M1PxHxRm~#JsX7*uc zls#XM7k4w)eY3USWmg<(G}lz(@!<3-3O08u{;&C$jRXaB4*e4kIx|jsfL5rLA*;+m z@g^d)uRF?P{Wx#hdP&RH7~@*Ofnhp_g$$A)#Fto@AzIY(KG@I!FkespV5HuUEI@C{ z+F*xu-P}K{FhP=)9R|l(5P~~qc=Cf>mGcKG7M56;U|>2|S5sT|QBww<2k8+3p@SFn z(?hoxu@=e96UC-|LXbs~sEke_gxpF+3|Y1mO^trP;J7FKhNtVgIBXD?jG42xDS;9< z!Gz}ad>XiyeZ&kpUR^ky8rW+U%c3xT$?ySdxIfb)qpZ#d=EuBabZb4Ni5s{Sr?-~5 zII{sY&xs)2|JF9%y+unZ(R(*Kef(@JKuyzd%Y@usvpKOr?qttn$r4+v-me5{?BP8F zllEGO?#;sR(8I94$@PlI{Sr;Cviw86o`#gj>mJss*P1A}R@V@N)+^|i-lU@c+;lAY zOS|>Ad)<=Bjk7My>a-*?UA)NU@>Xx#fOvTdBJJ;)~v6BI&x&`%AD%g5kjx#P z^FZbEKUdOr$Fek(D>fR&XeWXC{Nx8t0DMlyw!00M?$3BHN*=w|{rSi(Xz<{=clG`E9{cfKCtQ$Fl5PJ(_(e=(g0q{X{C94HkW>xv{AHvCeXK37)h8~xrSt+YfX z&l@%l{tj%X7d1k_k)4j;Si{5IrahgxSsM`3{wYCgt-|9^@3FvcuxX!ewO??b#bRRC zgp2knRdV4t$Km>^m}xlO1hr&6G9K*0eh>-BD6b*t#Q89p#P$HBX?&yQv(@n7eqqh;kc#wY-KKc zAIq9LZ-wQVDr0JKZKnF1#$~*;`Fo;lK)iby@>T5AjR)-#s@M?P8xNJ}^2JH49y15+ zUMtfAFa~OoBSs9L5P1Uw3&cOY69sJ`V_xPXX!j2#Zr+I?~HZ4*ty=;&@l4J@SYQuqKR-Y3%Fd3 zYn=%K;U3u`;U%ZX9Laf=nChdonIQ-UrP;Kt`k^M%vZh5ss=m=;&rk?p7^g!T+S&>6 z{%y}};Y210V-Ku0t`O$-G@aM?BJIl87&t|iiZT}>e4U%qh4%Te&dbeO^Pnyn36WGM zk7a}}g2INp^xp2T?%YkvgoRH(NqO|gfs%%!&eK}8u7Ti^9|&u_n)#N8-IsjxTPHV= zKQF*`m}Ul0?GPt=#~ex5AzsT-`p0F8$}RFP`||>mpt6WKgvr=7=v-$IE@In;*BC=J zo26?9;%}I*tLEDgHnwP~P>7d|O2`X^D*1kI4fvD4XYhI81S2F!`b*=7b=4L1bDk_D zD;PDoaZx*b0GafQja+C2Q|wL*3F1x7b5zCtL)RaQcD!MubYvWfLMPt`fN_M@ne(wQ zVGL3v_0Gc4oW})I~b2}G^As!I2n8Mc#0{r4u&tA+B(Ag<4EDrv_-k-vaK&yuO)H zL?^jzCJ2oPDG&NdyPW^xZC~GRVQY@7)|%0j%cxsz{Wku2A-T!TP&HZ0q6l;9J{oG| z_%+4Nbb;i?(H3T?MCEw}l=g%-N>YqKQD`JXB0Qp%c z5x<)AWq}{?@@9g&T&9Yd`^zi;jYwy#`WydMD;Y1EBh~@cw^_^_X9R;N?I z-&DhH#mWzmk#-M*JCZK7_(XNuYh#Ca6G-a3XPdbAC)sk(#Ef zKgfceov22Ob5n8~&9m6G${J+GNcvxz%)mq3RAdhoS_9`23EBVMvawi4zET9cTkcP5 zIb3ab&}$n^X;rlHD$Z-o233dB$WO`FHN^?4EMYe~zCC-T4%ACK!TYp`(EI)wRXJ`kWMH{L_4!{hQT_W{vr8 zCV{)+63Nm%irkhIa-{b!^$Gu)akO3t7LwnRj9P)`MfS59TN6bWjK%O zjY5Dh64~nxG9z-vFV#Ef%(S4RH90K_vK$=R?P5`lmh!az2}V1SKO5HBDnA8#i%%WO z#!E4yr6Y~%Zx3<@u^03AKi5qX+J5jJHA_;xyyH#u$)-Aah!k5AU~21K1x;@=Qbf-A zax)&efzbTl_X`?f&mZ4Co!IH=d6F-z=`rFm&p!Q5d_LpyxbS#J*Fz$yVzd^Zb@ms1 z`kAr2GTqU@3{uHCr6gnIJkKT!@ktu7|K7wO8-NIA$>sU{6H#7csK8+bb`zsHQM6w$Ay*M>$=|mb*paRf70coBqrD0@s4olDc)TD ze&MIbut}ln-&_6ZPl4MHXhyWIp2hrE*UARw9yg0i|LVEl@H_hwMOi*ltnJ$rZrU)z z9|E|W!d1=jU=Q4Q8UP6+Ty`!(`&??V?~w4VVGA#Wk&Gl2*!4qm8|X@x>OvwQ`sN&y zahmN@#vCKqIJs5YU7H*?DdL{G>0XjFkC|YWV<{o9~?>Q7SPHf2u$00Oq z^;{utPOm(}R&<}gS5s%iy!ee7@vW%Qg*T{FJ+4T#s`eg049`5<6-<5>#>L$LYgs(F z+oWNY{&MXN~cMGjI`X-nc#(iGjR0lU~iZU z(H5$RIjZj**rXX5k@(v=nV9??H;Kk$Q*vKgUcCQcVZHllx-->qw?z};CY&0g{}Zym zTwUZ32e*#iH@c-YyPxjj=M*=?h3gN#8a-ptt<@^Oj+N5>-q_Z|0#BLwS8JHv?9T+J za2XXFHA%&Y_0L@uY0mx%K>I<9gih7*pgmp8#Ch(yIb7BFQX#YtP#fw@PYp_e+J(qKy0L9Ynh?BG?~L6 zI|dbN#eWd#>J1vbEmo#-wW_GjGuurc=3Lx#UE`B3RwA|?I`ibc1_V3tNwy7y`;g3g zA)1-fQXLz65efP6Qqeboz3r&(n9kI&!`vx~d5frf@+{IR!VYr~BPG?Inuet&6s}gd z7LRK9E9h=RE)_FIontPoeTl^TV`F1?HjJl2v=-1cIZG4vVVZsM8AbxWM9udLhe>lb z(53^EJ(PTb8vw{QKDp^}^V4Lc({7u2ZtfYyBOj`yUHZ`eDH&Kf7?#g-gLP4p2%OK06I{es)lwu>AIO zYsc{7{IO!Ks&NC)uN?J#iA1~589W5r?gCP*WXJM?inLd|)fQrgJ$oHP$vF7?k~T78 zI-K?s)wZn_y#_5O9kNj(randceQN=4m9do(iz0@n}a=MgzO}RRB1~&N~IAR z$dUp^Sk+Qa_#163V7k0P%Y`!*s*AA*9LsCvwMy+<VLx+l=#@@D9 zPYgb52c4Tsx8e975p%IEf~o+8g~PeE1B2=_z?<-t?^&|VWy*tWe73sZxO-WDAPZlkSku~I*8cVanCrj-HQIAqk0N;9E7uKBQ;aB0GInP z*1Es|tuXtig@Yzv2x8Hsekqv&2W@Fi;zWE608_RwZW+_JHK=V>RJ~)qGb7R|7pA){ zQ2BlPYtKLXG*ohb26q_~CBD_BD^uf)Cp;SUNsUn}W_Btd{b+F*LxUDQ?%LrP2?P)p z1?E{G(7B}4oQJS}$B&t)Dn#E};;#5z1cjI+9JN>;wr{}puJvg44Ll$MGCe!PJKsKt zTBRvGNe81G{yk7r;63=DpK$EmCKv5#r*24;1bJOO+NO)+6uAI>w7PQ`BpqnW6Fd8@ z-VytmdFGNA|6_^<6HK+aL@0Ch>PYD zq^{#R24iv{`rXQbkyT3tUnOZhY^mGL2p)l+E+@RX+j%e$@*#L_HkMP#uZs}g$(l<6 z+yS6H-RvX?Seh{gGZZ-K?DJe~-DR?L>}y=siujXC7L?tZK3IV$Y8HsvBztW+YHwm0 zjG(@9WdO-3`D@)lWzT-UAf$=?f|hm{!W3p{#oTsjO&_&U4C?G?F5+Hp5iQaxNiOH1 zc1(G#3nmm zTqElh&)j0PH~y$GmVtCyi1Du@HoqV$wpdIJ6P&($s9^Q|0%2p(>6(eD;VHnlduG#*pynV^WlDU?j_|lC z(g&92DPuSemFNCeg|jepv6*d#IPxXZI7jfA?!w|Zy@)d{oG#}J^)r})07$r(mk!@7 z>g+nkCNoq!|7XdhQ}1`}i)xDuwDO5}j>i=iLF;@%#>IJaS9+GhS8=+ia!4$USxSLw z0$%X6I^Cr|;7Q{^+GUy}`X&msfH;l|dm|R2vX_hqMn1Ufwa_?$BB|vxwXPa3vMR^Y zQ!HAxfbhjp_VYPer<_`|)xEYQs(JLRS)YEMy!PDU>(#;QX4)D8$M}5(9v~{fwC^BR zuTjuf?cV66O9|{8J+m_3T~s5pu9roGHD!(D-zH!CT|Q4ahx^Zx<}ZsPc922}4IS2a z>e6w9bDP8ogNM|%ZHDG%Srw%hlggOvFF^vSL|^W$Q@FVv^b zBHz-`FR7Co?S^)t#(^5T%UF}*gBv3t^vNG-|Jx-vSOC6XU?80WfGe`j-Ydw3U^vC| zf$PjH-EgL!e}_~y4jDF@ZNJ|%&EG%Rp9h(Q9BNdOJG+A{_>|`z{xWnS0?8=F$W(KY{Sc^=Fj4_)I zlJJp~pnDg5VFkuHNLn-t=N9=M`}rmoL%o13r`RpPzC)QAI?+BzzyQ-i6BM0bdIr)yx}d`2?8f$wg*KmS3U^(gx>?8_$M^iN z0Y8>7Hgj~|;Hly|{uWe$I5n+Nb3wLcEt|O6CyXwXd_6RH3xOK%Ndk~p%QgpA67+c` z60wY3E-mk;_G6qV|Npfrb>Oy<42CLJgT=oB^3%PYW9}ivznD3^Khf#E#+MmS?@k~{ zA3miz3VGRjY7vW5KypQiS7j6a$4F@0SPWq9nLftu)2ew?S#5mLMsAy)i15_E4cu3z zTi_leLN)q|rrQa6@(Lq}4u=Se1Yc5;8&Qr@_$8v4@mk4#U@!Okg{SaHb%wz!!N8I6 z6u>#XcqyRDlXN?zy3V!5wn*ps<;X`3CbCE^Ghg?OBmK8o7YqJd2f7w$WxU3#Rl>V5 zQCY$7UQtq33bNZ$;ck&~&fY$Fg4}51yLSJ}+UY}(aVHU$>D$rP62mIG<7p4UD?Y9Y zoSSV##E#}p2YX((-uYvzaWOT~#m_>|65Fc9+ttnFUO$aBn)70gv0BK0fgOTL`3*?>-@;#khO>viB} zm(Dxw{3kObiie3my#0WyG>iPzK(a~Ue;s)Lk=`F=(~LI1fU((kURnRY?vt-3|I)vF z`43N_Snm;*{|Eind&O4Wg@2K^y#G6s5aI37?pl1ItCJ^q_%GXgsMR-gVHO!^y@5^G zEcq+{7v^gWL1NP3=^cvN$DhvLQqO15RjYT0LBR3i5Kiu;7b55seZ?f6j4J|_2 zO9uDd?LBquIag`fyd88hI`S%J{LS5%(c+}C?QE6UpVWt`E|(4rF!j^cYQ2Rafqm59 ziD+cW4*;=#2WHdVnMK@VQrAHF#?tIYF6~OdlI(9)a3T)ScK+V>F7MbwPD^dYi^x1GpJs=E-|9GKN!-Qp<$u%jH^1E*Pa=qTE@9;j&*Dff6Y~s zSJPs+ZSnP|2LRw~C%F3||5uH%*Anb-Vqkyw2vE9bIi%F-3M$-1l;0}yv6H4s{@AMb zxtzx;qR9B27t*B#xX_|M@RtZO)j94t9$6){2WTXu?CrK2%5K?mgA53Tpo<9FGsTo0 zGu@mt2t`F{rX+?CvavnBUN92W9$BP(XlGC-`4n~A3E|WYs+KmhxJO!|iHEvL`@qpy zmlttUskF>@{ZU4k#a2V{Q-LrsJ^X8qmG`>UJ*3eBCZ<-zXaaj7y>Aabsq#LH(y+T` z$Q>f9aC`cvEpdV4Owjn+;Oox(rGq53D$!`s?`654X-AHW-52A%K%~Y3o0V;Pt}gPm zObRWpnnYJZ`)$if1L5g)L0LL|5tVI*w0AGFE=K2C?w7a#U;y3ICL9S-dPUv#jz?LJ z_Ydq)<8de(4dDq7i+!`sqko&VXM|2OfOJw2_2)}1#^|_N*LeYAWM;gLTljp1Ay?2Z zf$+(GruRb){(06+aEQj>k7SbLkcf!dTe=+JBFWKbhvLr)fzkKz3dVNVf=J*OPl@Tm zZ+Ut@`_;nIa$DAwrV0LOx-v=ort@AwEAR20T_2jP6fw_TtMkq5lVhS|n4e0ocRb|) zow7cM?&SLp#N_){XZ0=AutQl;7Z^VF8|-!R=X2!kR2YRD-V~+HhaJY(&cW^E2%60H zk|Ykmp^a}|BNLDsgTu}3TvLyCo*f&in4R<)ObPD4A~d%Uvk0NOiDo{MHyiTu9kn{; zFxGLQHwHJ%rW5rbiuSteGcDWZTlkCeHtxGgW2MyD;Uo?2s8xLQu>#l2{^)(O^D%Ap zX%qW7m?SOhTuFH<+P>*lZ<{qz9b0WJ%69FCAMT(+Pb0zJm&!UcgEVZkbpC31IV61` z|5n3zmLnCxbLkkl2WuxodsW%FZgra4Mz=o=cVZ>yUk05PvyRHTKDW{4SZwEmU$v~Y zUU|w6L$B{ogNvM%bKMlqD9Lh`Tu-U0o8cLERRja@q42hW;?#fkofBOGOf^7N>B)xv zJG!5>4Eg|+EcVxvOm$*KjoZDUglmE014>?0E1+@_US5bw>W%F1O3P_*d3+#Y!ZsI% zXAnYLw9aEJV>pp2MV?ssGr6ia>Y{BtnFNj}C~ul(Fzm!?2JDIF=M8rgtbUAD5=2QX zo*3t^EXQ`xs-3gnN_Nu4Q`~)v8^VLPhTM`{R@X;My?ag7bZNogU|u93$`53}led{N zq!|atw)?9)46pygr#~)ghmho>>^VFrVI5@Lhd143d=~0Y*;%wDi*=kHDq>GGL)vwE z_nX?@!)oPWo7M?0bT3{;7)g|~>S9Sxhr9-_ZO(g2H7y_I`*pIRY2m@vQbVFd56h|4 z;p)w{Z=Kq6N`(*z>Yr9?dVz1b8mz2a!#BY}PUa7nri6n6QLo1g1(^C~C0B)7^`=<1 zCi~{iiIX8F8PDfH6Z}NCnduFy{FrU)YO>QqCpl0=IQwbsLJSO^BzMpA@52wdE86W>SXlRsXcMKD<`)!x&8HPk$PBe-SVCTpuSHPMx38!k8zoi+mja45f6LS@4~9oZ zOy`QbL&*TeDLcdHyafzgYJX9d^0fKvjl8*Wr}h|YF!9WcnijoU@%LQVRwJ#eR z#7qEsCj*mJbpW!t<@|a=ma9|1)${vyA|{_v9=#s-`*H^Zw7oznx@Aqq?wA(Z$bjwu z2K;@864Co7h3E3;X9R5KgGOZ`q?PYo$>ntb(%PLu!A1V^dJ90#r-k9YU!T^#8EN0# z5Y-zrBcHSv2#(y)+`6`o?hG7PbEyx-PWl6A9GE;2k>yUzY- z%KGQB6jwH4<@<#a2=U@cYfeJWsT)^8am4+`nr3ccKcq;?ZT9r$of%%wTxcc0yCD~I zceloM2g4)S`0ijZl|w@zH0B7qte%6|5w@`&O=u|N8AY8+CsY=nHOb z%Ggb-hRlrI{eIz7l?buD?l45pp)I_i+B#alZAoUL;WXY&hu40EV=&HAMtSo1qQ=-4t?2+DuIZ^ym zZ%nows63?co1Q>X8^)t?QxZ1)`kaZ%ag%3it+@?2`Dh@Q>q{dxmX{a=dEr{hCI`-M ze*siAEyuQq5`2C2YonzQr@Zy%@{(t2e*IObJiCVM-M7?5QZrFTYi=|*!s`Wecy9RM z`vvmA<+?~bOg|?hFVl-L(V)DN>|n?{a9Lf5+L$(Oofr-wlk z$A*UnXTO2L#4pn)8uZ|4GDbendPBGv{D|a%Qy}Bwqu3%)iZ;b(hMf)skaYW zBs)b1=!wJDg)3eA=Ulg{7dg1Nu%)H?B^8}%l1BV{{FSGRlC{C74F**Nuke1;uDhdA z9yz9xh8TPM3YyRFc*(q&9jkoxha_mPQPN58{W6^7AI*g1p{OHF2oB_RgZ^ZDqkG5%?43rgKu~hHgG44mvk<|W$__lRDv~9@N`0(qx9zfJ|2*TaW z;$Jv>r}F01qQde?6aDZ`y=l8(pr^ZrbJgsIpuS(^_bQ}7LxY?n`qk7w_Dy!Rc`{Ga zhc&BLy{7Kd%Lz-d(?aYEbdmdX`D8a@k`)&%4b%L^A;$eYR$vt^bHD#eT8@W;)A5$6 zM2A;BKEQZIYlSAs=pRm`Wq6f6K zJmHv)SmWLw5JBQF*$#jXOHdkV(L;PFLbxVUO0HK$g7`iS^nU$@N**i+=elTb|Iy7Y z;hsVICZI-8-aMWsc^-iLvv+rDJSa3nQCli^bKrg_%;ON(VVsE|nW|UMa@&%57S^?* z2v46yzFa5&TM11XqB&xuhNb=RioPdSh~=S@Bm;$>@ouX`-V?&Zo8M-_*jjDg*(%Am z|K!QPp1YJ#HxSuWJ5<1&WRWod(9MR~x%63_FiEAR_%vrrE0j*`NcBpIi$=@_=>0f0 zqITkDW1dA;F&?3C$Zw_Xopy5m_@a$Bi1;2ty} z_~?GX^i+c=84I7_MD$l2gP8ezR70j4Lhf?A7P(SLSH}H6o=@l+F;VU_--Yws|7x^~ zM$;^5K4&NQ&~-uJm(_V`HC%+HOoJ+spDgXD;) z%0e>u{zKlKE2=k-;N#M;6C~_OM&(0x_9HU87f+ykL2_=4d#HwPvEz&VPG!=jr@c zs;v@^?yLbyd9+ZzZSR_@7&WvJV+{4?`5Z`_r7Lo&zSPhc@}Y?g7!zmuz6ARu|>J8J>|8LmsyF~FM})F zfK0Lte?=|TvkSsMLe>;4yW-b)lzVfN(YiZpMl${lTAW6*&hJtDLC}Z69JhPCti^)% zy-6DBM6nSu<-e%`h}|BZ5SmAP)!8$;#!0hO=A_;0)=B<#KQ}EhMLj-9r@yxdyN>!O zlqwcDtEod@)yIr@t9oY>Rs+?{9`H;;p~}+^StEabr10PcX3^X%y(lS%LztKLw~^y) z)&azMjWHZzdx!92A@b!o=ks16()KH#l?=4EbvR!fPM9+bn6dbKraq);*8@nJ98q4Y zs7K?UZFZd=@9IUbPP`^Qcglj`zF06Gb$tK8)AbD9dw+<)BZ`j9rtk`Dp(x1$R zo=uv5d!fI-w5ScsmsY42Zs@^w+Zfly(H{U#;o}Toc&zLsJ3jKYsO||Lb8*~aAPk&m z6MOujU1CxVsQPX~3rBy?+oc3-TZ}DC**Ykszl51>v(_Y^hgM4~Y$l1Zn{H>?eHkki zjL(fjMb+LfuJP=)kt{nX-=sP01S)4gJh2xNg6vwz*}*i3AeKh9 zzr6OcSFhS+ z!vLuk=?*~E?it3aTCoabT4im9rWpny$vU5TUEuuG%Gu`KPaSeqMq!<{QHVX?WnnIg zuE%;pwsm4J!kZAy)vF-G@ghirXY9V{@tsMYOR3)Ba)s|z9QW_Kh7GDtN*(3TYXhw> z+CZG=LRW5fSem{Y$B0G4lqnGe^J4A`YsUoyqzK_SOGN8d*Tx5w#*|0uX9lRRNR{X+ z3|mio{Zs;BPkqbnR#6#`UX3!@B~9RE?M0`OR^-^hvhF#!=r*LV9X^82`4TO$RAYmB z_ReJ{YTF%#Fn4uo=_N{#_XB2-aBPMK%LBP3ixmFmXjuTMUYbLiRQlgiij@K3aR^{r zgCQZ-({NX7{AH0~ViBJ971Qu(ZG+6fT~#&I&$xAU!@abf{3AcXWVo*$zY&6&hzxE~ z$aJZ2LKopB*q_%JCd;V(;wl!TklM^P$c_!u8Dae&E6HEi>Uz>b5%=Wxc4MQG zRz3Gpl78}^Ujf@NJ4cpP+-O)?;d$&i*t=l)pa$#`tGEgB+C_6xqjGNS9!I7RmcD!= z2TAoK1~N_o7+#A@y7#}mA&HVW{M$X7 z^UKBdDv3uix4S3K^EnT!FKK35;?>VXl;&w)22l_^QO?dWFI8)4#cUnIm}BVbyb^D*Fc#Rrb2?l6|I!_8d$0trE_h z>_o>r{ndHl;8MYo&hqyQLH+-aeJpj3|9*jAWs__q`pvbY3k0STF&4cgb%rHUA5Ndg z$q#1K^OFmB`Q$)CiPx3kwL`;L*rCJxhNaW)wcUt))f4T>xBx26Fm~WLX+``DUE*J| zj5;0LJk18XuyzeHaY4u+PSoiQP0kPfd@K26+X@xia6YwDt?tpvu_G_WNVKK)UbUJz zy3(^&S*vRoN~;DpWWX$|XX}GgdwVFk!JijKcZ2eK%}iIKa)+$#o(&&+)E)m-n{W4V zOTSTv()Q3uik&tfNbCF^jO1naylb_$pQX4rJq?NSZ6VXe9EhNgX3GE6G+B??JgDXe z(&qzpM@}yB#oz$17-6hU9?i{xEe>=bJw()`F`Re~Zyok4b9Pf}=^Rui@6JKh*(&_hlkG6lf zvR2lwX_j=5?Xf8L9UaJoG)>trm*t0s2pkG53^b45?3WNjxNyKet(o@KClsVcB%DzRMFDBi@wH;;r)?v_C?Ikv-IG-OfWG%kP(=EJ zXmj$ehlQqGX)miDi+v_IOpV{&5FeNO>XiPvZWyRsp5r-qkF@M3zw}{N060MASlQfo zh$nO_b2YqG_w5D?m*bZCg&ld6)ptuKqKZ5^nDkLarm~#5Xfjf^D!d>2X_Jc~Z|O6kdCP_SlID#Ztpx;;`1@hFr7fFP#f+k8{m~ohGQNNI);Mw> z|3Z})h&{FB$midtr6>1{CH}SG;3DVwb;z_f+HPBKWNfu!<_`?ML3_lxDGT4L4tHFR zIBijQ6gQ6gkzl-OZ8ohM#%55ofeR$bCy@dI>i8 z=YIkTnsnXNsI{+QE4*>}s<9?mwr={4pHu!C$p)aKOvy{xyTpjox{vt0V#5$^CoMHS z%U|nRbtFENfB;UcAMv3@G5iNQHiH?|fWqi1fr9c98{V|nShN;Fa|bCj zch+d>Gln@-*yvc}h7(r;d%bn`-7OrRFbWgql3!X_a9r>-g7$yB>Pw zzgE;4TcAENwmx!=XDbHO{$+$0y>&Q#s5fW~ucSv_5+LtM!D_v)vXV9H=oO)fMNavt z45W9uxQ~N`Q`|j0j=|vZ+X^~(&rGWs%m%+~T|bb{tKJ2dz5qGEv&98l4 z6Eilq1E=OG!a>3@A|gUdVkJ1Gs9h2(5i_Ai?7jNS??1>RkH`1({eIr>*X#LQ@O;PP zh}&}Q2!pVo=)X4eY57M{Vb@&|*@c&>TExS=DAXjZSc#R&6X|#b!dKcpKaa9+U7dHNV39MR=UYezeCKy z3g5*^69!nysTlT-YLEKDg2Nq_P)1Vy4R&}%1Nb@{bB!ET;LxaxXCZ%dpySKSvpu{! z0jif-43x{fkCmSMi!4WNfMqwV4Q0K+IsUKx6*U24amk!-|+phu}It5P%k z6LKkn)@YbCJf7fS+jql3?elaJ9xqyXXjihv7>jyYr?DqRfj=l)=jKDgk0rW{SFuZp z>S?2-Lbu|EOoF^}p@bImGP82yld`2@OX=7f3@jcWmR&#d`WmC6Z$C`VP-AlmACxl^ zH??$Pf*KiAvsA6B{$`ghp1lRS&e=d=M;WA(38eLbPg5DQyr3<|`LtKO%?fW;rUbEg zv`Ei_=IXKxG&tV9&^JUq2Tw(}tzLy?eX_%tF~|6I>G0TIN}de2#Fd2XX6sA{-*;Y(6<=>4NrNY z`D9n0imbXU-j|WkVu$H6!F5vjMR}yg(uy}OG&jeyS+Klnz0hI)ke|`cv4J++)(B>0#=x| z3(=m#XTbo(%L>N+!etsM*X8xPlvRt9otp%0ODC*ICA$AZuO6VK>E5Wk?p%=LK@Sn- z>h-=Eb61e+017USG=sn#DrSsF$H_zp+Cg4#`bwk-z-9_@ujZ0pAg?~by9?)(kK(!h zFjWb<(kBqP?poCVd0t*!7%LnP!RkROH{CNAcaMd%Bji+kD4h)_Q|J~z(BqM`vcdor z7}H4Vc5OlU()FTkkL#Y1)<1a<)HgWLnz$c?bZT^i=G10hk@r~<9g?e$Oil87l#iXM zPQ(?7%I73r*}~11OI~qJzFv)!Gx`~qwqAslK1RbelKU3(74-A}E?W>IIJU5TP4?^; z?51F)jj4q(jhdk!0`L(%^q{t+uCnn%1PpigWvxvh!HiTjor@UfjP?oEQ=?LDQZoz} zgbQO~>QZ6^D+8tgZ*2~t9OW9Mk3uhqX|uqTnZ3YSofV(iw>gLaIcY-{C#R*|_DqQw z(Kr~C3=M7->gX8yd)G+6Y$<^v+_YScV85{rlFD5x7Bg092rta7_d)$_){Hk`5Fi$a zqFI1t8h%*vtq3&|B>2A*P^j6F;rQ3Bm*|Il(fd?Q)k zKpz?WvF}d`znSCXyRsna3yQJ_@Zf~h6~BmNLr zET3ML4;!9yE$}MIAdg={i17Vd*2D60>(Ijg*UC~Zw~sx2>a${#2%(%U2^D!vPI!Pp zhXp6sO>C@#?qghBm5^%2xR&W9fm#6K4ihN)n^>zVtZ(JK#Lv%6q!?smAs5+5d2!^L zNcWBM4GO26?jOA)7sG<0=(&_j5hD-~FLuAV^=h#aLdmcuk*$johY+Q3m7_q&=i2#P z(^yfXL*17PiWbYH^eBE!{$r)@GB?pO_oe5ukK!Qoxf9PnrKrU2-hzH^YeR}r~zxe+yz4x&{aUr4wm9|{omHYLt|B?wYhT9 zTG)ay_6<3Q-YVWGW~Ko{XtI*?Lbt}^fk0h3N$r&0)hK<-AP|P}>qc02l9~)w@a&7_ z?O@P;l(9Lyroz-N%%^K581{KO(o5B@h}4mya+oK46f3`o|s~-52iGj*n$VsmiwLrj4OaA2Z0>p(tuU$U#I&+fm=6E$GwJ*DKQ!ZI(D ztx|5IUrbW$uiUe!_MVn~9ecydUKsHEz>EGBD};3uIGB6%sxeEL_X-}9ZNZCLU!~2ei5`6$`+e< zS&ghV>|-|j;A8v01?b%A9UUTayJ=(4vhIoUD4ck}kM>9~y7r9MrnQCLi6r-jf6 zL1ZT2DBspAxOx={hO1{}>XCZ|QRrW354~I8yT2`APV^@G>P=aV$^jZnItyl@Sp*>; zrsaED)gUD*KFs_Eu} zt{;_adJii1ojG*N3mK!@Lze(&=2EMb{$QThUG6OE`vtmUi>I1wdYS@eXpf+llWM>{ zOWB_J%pC$l%a105QyNxvr{|_|A6SrZTSk$?(pmbhVxeyXVSSamu4}$dr@`nti-Rc` z6qyP#uzdIVHHQ4QtjV-)gmlwFOTb$HuO$D>W9>-c=C*eXG(2!d>o`*KGp+Sfp`$*< z7#^|%J)GFRwimp2y+y5%Vxp*M%tb{SsK)lUsDv7$;|+7krJm#ko873IC5&57L=mSI z&kvJ9_s(bXFDedxqGe2)@ z6=Ut4iSDp9jgMG=!r*wT$e#4VBUwfP)ViV``D$%9>Ti9EL2w%Tggv;`K4B%hnLcukF$7T|QxiT-mFY4ict(QNuutDk8MI8Jv98L;>2yxaY#q0F{ zfQkzz`(vBJ%i2F&GMco(CU;^_&s_Ibq4ReBDd4gY2>Yr}-K!lXT; zYGWqhA`T@3x~fV4uxMFkjuuXt_RhU%rMaIB|DO*v1s&eh_k}?pehdRj4n~jrEJyEv zca&VJ^RU1;bTctVa4YV#-xoO2@7!MBBuotD)R_uQ6N{7ijJ=!3NVQC*06e^)hx>H} zyz6{g^!)--?i2)=?@su=_xY6BO`W5}vSXJOy{IB+u}O%;SwYMs#-@feI5p9Lw>Fe|DgaKYn=a(|k@XYX#Tn|IEb6E?`-UUvKwGI>4EXjPHmMeQM z$JgbCZgm^@?PC-X;kN^tvtZwNn?@unX91Tpc+gJfexX?F%)?LM-Lp<)W|{@+5sP*# z;M%NH$q4g7Wf$IGXP;Pms5yK!kBw+shUE50>u&wBd-Hkj_X{k_DdhEG9jie9U68tw z3Ub}ZcqMejI+Y}JST|61nxXRK7~V?pvq@6G^T#AOXx7buWei%`g_@wIDdRgyTcpT8vnxs=Qirzoem-FjJJxa z@clF1Vb%|s&29e+9sbqbX9=-wZ1F43v*5>+^bPL^S{)x0`A^c$^|gfNNKQwjW1yJE zWtU4u|B5!<(P=p~ONEY4pg-mQSNk}edPf8qv6&pjiOS%SWIb_M3)wA3e1{;Kh6po~ zPGWuY?m$Si=^3=;aZDmYtSVwKm)x^;kOz)osTXDH<|2JOf^)^_;KYTCW=8j14lvyd;QHEs4jv zA}vsRoV*uti4(aZ+gz<>=)2WR) zF0}N^0onH}CN9Ewcj)f*DP94pPX=!jS;uB~l4tIx2MEO8)G@<`DKA@^$PDk6)ETAu zk{_5byJdQe7-gjRd;4rSXh19WnG@T{F!x~!L`e^X1=gHTNeeA0A~$c;`pg$(*_ZJI z5302dc2}DlTPm}c<-T7C_UT~7Y1r8BDV5*nx)}K*^kuk7j9K&;*|LR-C?DwM!g)^;gHDv%Emn z>M=>>H}72sd_U7s{4ZEw-<#Q!8$2CQw4YU#=7Lw@M;<2(5LKUC;CE0LKL>Q!r^;Z( zw>~e(R<~J}CYgV2fmB*mKF`=0OB;!Sr^_Ex}cO1E=0&HfEcud3{+ zP*&A7S*TCnhV`XoJb4n3u2)_J8C8TstYMc#59E%-#%q8!&GwNw)1^|8f7Im+g6B_j z+7qO#favJI8uX01?|6NzC`~7OURFV7 zw=&1o4gFqlD6~J4J}>IEUDN4*A_5((BZ)zG)Nl&)*v?uwz4LA4Gz~HC(E~tu zi95n=vd|{Muv6idW;U)!h9GXhFU6c;goDUcg--CngW1SRs^0Uhjyv1bjJ##P2rjSe z!xMm_D$9%5iho>Qn{YR-q)oOUPGIRC9z<-WQZiGamIFJBL8Z%7QU?Nie;mqyomojI zW}$`Veb;%t@Yc>xp@wNji5o%l33}wVJDc4t6MI^Ob>g8yH>;dNkoQuX<2VpjpA-0K z+iY#%L3SNG$=$mYayyBB88qL+;;Tkk9E2@b{lnnzwkh(`ipnX$__a^XK@l^; zTW+V-C~h^KsWOT`eOlRK09sh@=y>=}Dj}jNt~Y)zT-lnTqvWdDb`QV2WN;|qV_=?c zZtWG(=^ncT0^EiT7;fvmFILfFD`mPa#L#Y-TKs{SG`i7Ey*@(m^=WJIj2Y6?(cGlf z(TkUL>Pm%@apGaR_h&J6=bm|}>Oz|=az3XtOQGA2L^3q`qP(Fcaq4&^sDPLQ-J;nR zi6{?$&FS-atXrr@@q5%e^Lwkjc=|!mPZgbPRbkU)s&k8|{<7^5mqy~EseNdhvYJ?y zgIO3({O>R!IeE#E_O(MIK=7LdG>dNq(Eo+?iQZX{7oA^-8J7Naet9bWy@8i5-^%2iqCo`{ni zpRbmR7QJAH78&km9xuT0<<<-I8okpm2s#?3#{wb)pqQ4PAy(=~$8*iXkFZf=npqkg zXffz(DADk4kboyDWm1$fhho?3c4U`FgM5DpUaVVJrB6W;w4%sk&dMk6v9Y9QR^6YA zM5}+M=wy3_i|M0IRT}PmRY7E84eb|H!O&pj>r;?qwkA)p-4a{ZcCGEs5MvUlWZUuj zd22)=*s|a>eFYg>@HT6i-*y%}(+`8EDHohN8(5yGoJy))agXv6u5!kP6S91T+vs=C zM)n2gY7%=xW_D-xoQgpi%l~ATbCI2DXXSR73HCD1L$c@|r<+BuGk|eJ13y0PqcD6uFc$|%?@rJTXO}O_>Q{i<;zx5PxxpYVwe~WThdI zj}ptFi>)`hp;c1xpoGikgBCy(iZ;3z`;PTd679S$`?32@I&jc6%RrspH5On$AOEm2 zM@)7`OIeNLFfrdtD*;se2{0-JX9o;d(bv5Byz*o_#Zd<6l@yV(S8 zWdA1da@QHts|Bzlde5U~8-EmB@w&0XS<^8CIr9FEM_>g@wcfQVeZTNXdQm+spIVR0 zQ*19HSI_Qz9V_Gx2c}tz`wn=9P51%tB!6a4e{CL4^=Ic#C%csL!70n8@V<_*?-!Yr$7&^Yqc=G!8PLXMQvU}$r`ZKT1r_*heul8$AQ#sAPas= zN771_u@+1Nyj&lkFhB0yb=xTn{wfWY5;@}mt3Nj%LD4%OXXBK)pzEy&z{6huwu(0E zx^j|O?*p)}g)iBsiifx)O-%LD<&0!!!iHTzO~OW?jE0s7pHF5X;i&m(WZT2Om#x{P z{szb5$0T@gqT=J=H1n6bB!sfkl_J+(gA&)|_YHifM zh)e~&SJyKga9rT}(CK-kd&&+>sW&a6@AfA(x%mH0grcBxA=f05GAl5!OS#?muvDVh+}Bc=CB9*iH+b5Lz7HJ!MCP;3#PdyRL{**zR$yi(Y3i z3_0|w7VUrpDv)eouIztlV=qCPIcYsPzfsESY~nm4pg40~K07v_R{<$GwUlrUJ?$fa z^T`|<$aY$-d??vdd-aK~>4B7jvPtr}x4B8UcHQb{3Y4t*FRfP9_c!{1eB4NCbiou& z44UvP-tfVZi2TAVb=akF8W-zcaKbcs=qrJ@XcKVSIQs;#iQCg{9pGfo0FLgrMrMkd zTx0IYx(W61lh|%KgCtdrn3_;SqFv?p3ml+f+jRT+qGKECaFO;TCc5!k|!!Be^X)v^ahG^^Bm~~U3TkA(g=E#UuVT%kzhb;rQ)0$U&=0;!e z@A=+ia3Em=V&-g#2zIVvdP)uGwD*r++ylowjrY5Bk#k>B9?k1^{m1#z_3syciyE8a z#{FSuv#@uQTv%z8!|$`ty@_)>$RB~YF2?(?c9R7%K#snaHw#>k?M^_FN~2b~AfN6q z44Ou2+>vgAI#yCl_P;$369I6Fj&MlXs7O9AO-?_Q&oBh4`&5TBR_I`+e{6kQ0Mt)n z+3zqW$C-rj4_-ML3NkBmk_Q~zTMBPCm`NKFJJ3hpD1ui1T($6SvRY|r@Icc`fE9(} z3!Mu(x^|8{SV;m#C(Hn6Q`ltLiZWyB9*2Z0fGKX^j)$Hn$YFeP{wWsIQ+Q;ts;qa^ zV(mgC*p}7!bx1Y(8wNARQW?!zpV8SEmQOIu>rGBCs>UuBqjMP!$Gd#)rCr8;%7mfGFqF}ZLk-dM$?FRfgCPxRkbOaFGR&}PhYi@8Vep=cZ&1Y42QDW3 zXx%t;KV7%>$kU4%`?E`Kt*<2u+*wd}DD^Zb?rEvisJ){um$6q(jcJm0FCvb;g=B&T z4+e}4hyHW?vUbSwNP51eB%so&R|l3Z5!mSw$Bgr?_b(1zfg7ae`b4 zbW%R5wJRcGIKbt&Fefenn(7n~muVi4oKMnwcs>0DX(%mQ#Evv5Xs)DyRCpq$KDM3c zeVN4Bfa(@% zD5<4Mo~M+R6IZllJG?>e!yFtExw5f$aw`OBecQ&*mb9N+tO(?t(l@JNC%>Q=n-#`? zS)eF2ft&W&>D4?s5l|*%BT)OzXH6azVAD=}OE9=UNw2MaxZ+^`n-9b1Z)>f*5PI3m;JEvdyz?=QDHn{b3u4*v zpoy_>J5|%i!qk z-X)ioyQm1^M9mh+)foA1l}-T)?g~vb{^l#+hEfpRHYs2yCWXue1=??+@OewikkE2Y zS$^h#%TVL4`tC&&X`M?WQhuihKJ3K45zjk&s_p24OIU+bQ*wJNu zVCK$sP2Uyy*I@FmYa6YrUTQxE)ZibP94TkbQk zjgEjYVFbPDaLR~-=@L=Zf8RH330fK9Bd5)*4W7HiB0B$~NDuB{5EN+n+wmF)70Vb! z{LM&NQfPI-fOCFxrIIgJw^y<8h#0QCr&ow>vh#&ocO*N*LA?XGYM(oiKp`m|U0kvv zE4j0T*Knbm;ih!*r-e)0l}B5f(}%8?d^DOdbsI|W6HdlzBV&$)&tYPZ;yj42|+x`9ULc@IJwG!PmZwGL>exGa6Vld_Zj!tZDGXz zo~XLCES#U5h(epRP)yw_V|X>QtXOo~YAI1oRV4ZeA0WK!(Bo{t>84c;$WKJup zm6*|)<95PA=qmQW^l?yDSa$F*pu2T_>1sTrdcIn>s?oLhH)07+p&0_QRMg7!3%1<4 z9$+evtPAgKB8@#H){?{n1BnukmIOySMM$jL?c03gr(=p$w`0}dfPO}pt6<5J1e@wk z*UD^+{rNf0YB0Ec+!>*zW7pYr3uHJAjXCWuCS+K3a>my0KP(0XcPr%-NqBK{Z~R>2 zsbHa1I{rR7s1-*B+R11Qd(Y$$`#Hz7Hb>}ij_Z4zlu&;5wERbZGX%vq&+K_>BLPLn#^=XkUthZPBS9i=PLT(5A=5l?N-Klhb}&k3obVC9(c1?i~Z+#08uV8Bx%_e<83V@V=toqdY0(K(p-Mr2k4zcJ4U7 zen~u-B$tCin#*`INS7-Gj4zgeDby+yK=%*FOvhWD1ky@$4T}DTf(P3nwWmqgFFd1$ zdSN^ZC)fS3BO~3<{ z@#5q%PZ4ZyD3M{y?0m;VMw$mS1G7uzGLLsZMn%J2=5r(sb1+GTz0&k9N`LG?|G^euI@RS_O%z4)Xe$_kKB^ zPVgR`0m<9hm2h8-o;&e>m%yHkSw5NX);U)rFZN=+pWK1Ewwh5^^)f}Cjk?d-y2#rh$bRNFFP+W1DYUe7*%5>OdcSjH7qhlhoXW}_7W&OKuS#36`1^(X3ntWy?+(~%M1y3Q zu4{NzR&7KKFaD>Jned=q<2Zj3Bl|91WBAOCUt`co7MXC&;`kOEW zA5v4Bd}FB;xmZY~MtZssE~Pu(rs}<9gI9=nP``oAij(i4_;A9P$AVoH6jh1$!wJXb z`Djf%^a_!iVsv^_PeCD8czZzFBYat6pld!D|F|rx16$Q<*EzBG9#b^`YzK{sZbnO_ zU72L*ydXRKM}>)pX%=MMkCI<)mOC$v1M_+7%*Zd~^Q>7KX^BF2;S5Oa0t^q(^r&|o!z%i1OKa5#XZ5>jeg`RT`Fb?>ncXxV~aGOzr{Kp!yqYK0tJ8 z(a0G^$`IhI<_@zFQE(S@9j>T;@puq+{31OY=g~K|7t8gV2EQKSSj0L{#zAcNG4FuE z(@Sa(Q_TJJn@Z_jza)ae8SdsKgyy$lO&oc6NStC5s;92l#! ze`*)W9j~ltl5|j*O^Wdmk8HaDq9k+ zu6ftk;9rt^wMG^kknnr~ZE_zxrdEQ^(QtfW)owQy(*5 z^1^9pd$xYM-!COyLLN#}nfcqPoW6^hi3=l2?i_$`uF4tq6&^5V|2a@s6 z_w=dGU6%l6sv5PY;BlRrdr5mLCZnUQ^PC`piQxTWY-TvjPpu`ImSMVFnA^bS8TGk@ zq|S5IJ}Hk`KBJh$(+f!yCY}A5^0Z5xVu#&0e37}JmeV6Sr5#C5PqXF+5)8S*8*B?> z>z_DD8@Zh`N?v{_GCBWsJz%8J)*5`|d-4l&K4X<53#DXEwjbynp~{~RYCr!R!S zDQc5uWmDNN`I_5l>FN!@#1cEsYa)FycZ5ubq4fv0vrDq(*l`_%q9-ZTJ)=&;Qb@A1 zdqIQAZQ;}RE?5U!)Pacqz3&(HWZvZ7KOF)aT@+AXe_n=dXk3WjbC#kVNn4+pwfICd z2*F#ELHWyN$_`e-_0a-CFR%_{1nk<1Zg;b&wQ7-UhK-TNo{?;YTdLoq`8O7(9{f70 zZ0)8|Ky#sX4)54yTOt*L7E>0wH-(!;+7?89TBMENrkvc0k6*4#sDvaG2Z%iBTcRX< zY6m^E(yh;r7N5>rdnDEsl==@nH8aT1tk9?AP5$as0uo1k^+KZhkz5hPxwT5TNMkX} z@w!IuL3C^aVAIrUaCkk8(j1GUqB3m=esa@YAok5CmW)z(j}B^N==~==)RsLz?$%CP zuEmFY`cFGYVw>nT(0H(RHdmmUD?AMK*4Q#nZV`(~tE=g5( zrIJPpeS=mHKeyyIi0UbG=eMI`Z@M<^MmZ+M2ZPs=y>Tl-G+o#e-)ZcIL33K9bh*}x z`no~O@s&1IstBg++(P=k?VTv;dJ}pmG7?sCA|tfb)b;j5{b25~l}RVm`lH-_VY!fG z&VD5S#NNP-BxUo#N?BfiPJzKu(tJZ-*O}hw3JEmH;-{m~;DW<1R-I0Qq+mMpdC|}{XVMwI zF10%2icy$v;U2v9jDIv<`X9*xpSf}GeM`*{@8{Yz}CKP+x2*?((k(J6Rq@ScZ$HgxMCAD>0i zI(c^V{lfC;wc|_6+I>LH(~@)}{U+?6UrX9}9Zk|3guDGT>z`MBP1c7FGYZZ#N!xEp zWBtbvocZ?)2$MODQfTj5?VLmmv2ovSRj#Q}ag?aCr^Hvn-}U=I^B==E+b(xMeT0(r zBeU_4scW_#pX4So>{~2BSu3CieuXnKUl7cQ$m9E~;`=_mC9%4wh8NLoGL78J=E4eP z$K)XYsHj3}8Ge0F{_H{RR+jnSpN|S3XY5YzTXyqo&K21QrVqy_sIV}Q1EBRYk4Zf1uBf19`*aKm7R?5gYBxoczA``NBj*lR z?FfAx7k8=0*d@UM#W8|4xBW#^jM&SXij~TfQQ1aQhnQy@?R*uF+Lv; z=}pneStqypX0Cx|ev~c!Y+=zCjZC@!%)8V4_gwe;ZdphoFZ@SNLc^_XM|;GzFRHOW z@gzhpyq@ZKA!!kIX_>5p48E)>cXY=^`(on1`<;gmQr?9*&k1b@eDt5*_-i#`e0lLf zOL5(eDtDN)!pWGfs65nR(w_S4pB2BS1mgbr_|HZr@(X~@I(~an$-4VV;NV^0-QC^hSPKXh z|1Z;a%Z&c2c9%(&O-OG8OYO(w^Ri|}=KuDrD^^iWooh@PmEXJO{VA;Bze99Cl@aS# zWTVIDI~K2y-?ps8tUX2+vu9UdmSj3v+1UvtsL35=b8!rt8R5gKbBVs~vB%?`!HjT0 zud+-|tPCefikunvw^*Btzn=Vi$P5YbSF~C@h}&actM8x0DFGo+Bh$Pl~3U zKXr9w$K}%fdNsB{jQOL1NmhOuQ|~ULN6EHA(LHWaQT80xF+X5rEZU$+r({tZ)Yh%= zYXF<9KH>XC_Mcp=ogX^gw6t);3w;u0Fm@MS)ZDLiO_`0zqxM=8J$dCFds^9!WZ~er zC{_QO6`O+wv}ebii@N%)JSa`cVF$Tcjr>;kGZ9)5?r{l%YRRf~WeoZery~kk4Fg-6 zyF*rm2Rd5(Oy5HoG8=Yhgw9e5ohNkl08B&8OzFhjV zMT*9&<3wIC3i}X%?-$CW;>O~Qd}~u$Y9#zJNxBJ@ z$ADseZ(GTUSsK>*Pgk#p)##b2XYk=Dpo?-t8jG(u{Bej4z<)$Lm_7VPaq)YSaYPQl>{sP}7;F8g@`BVJz$PH6*Y7>Fo(>m618O$E298YN*kfa`$Hm%rtXkvs)Mn~Y` z;{LfS-x@6r`8xw(E3P;sIa?yNv=4CpaiNj{g#^EDkfdHM#I8iVT+qZ_35vNpaf!^d z>9}lFzSF0JsOs~o$18ktu58pGlX5M^n$^5af_j?FEe)wR5V>sthXb3I>202zm~B>S z;6aF!Lv*~ddZL(`B%0KBV6I<@)ib-DBPWu@8AK}uz4c1J+D<~X?n7fl(Fd*j?LxeS zLF@CPjgTU!lo%e+tHHj{synU8Q1$#aVuuBW9JnsWb<{m3lCA<2yX}d98d_6~fRIu% z7!MT#nzR(%58+xP6?Bp+$lK^I~{h^0|9-;>)=UgQL68|j9>vHgC*PwKX>SlgYEeLmQ$ z`DA5fKha5DC-)gTdDN%Z&W`YjrCG+4*g-}&sC4Et{rV)bmK$Y@xX9Qe*SsjK{U4cO z^60D}tA=P!PD%qLaVRWNRTS1-sUt_sbk=7j@91Zvfxjfmr_5w?FqyprE|wXp?W9<) z^PWmeDUX3O=>3HsiJ2Dav-ElouTrzTlgXznFZmq>e1wMj`BZ;UtPbwu<_(wdY<@_| zv@p5dQe@1}iPTsIXF&pEAm`ltQT|>0L0j(q@dUReLgDU-g!UwYM#&86YB&s8%n)f?uV=9W?7&4I@GQLQUZ#r@c3XM z*`z>fePz@hnaiM1aZOWlqTRP$mC{{rxlu99f0+SQf9>%l(w!D^O@=kydCi#24k0g< zws&aZ`i!eV)X^$lPvDm>+gVOQIC$>gw#RR9K!LV*{F}Hqs=<%4G2vS}7j}Oez7uEJ7ZKIOh(FF;i^ zULY7lTAx&|i+SJo4OYn@4j2Md-n+h9@_vkOy8M{8K;<;oHLUZN<;8!Y%=i~A-tX`| z*i+tZ|7ck{_f1dH%B~qc(BZ1M|*cgoiJ|^Z1Ez|@!NBIpY~hML0+35-Ggv^Mp-h2 zM2$0xGF4h1NtZZ}KDn((M@NjL+G00iW;D9igIL6}B_tKO-p4QG^o~JtC9B=8oR<{% zl7F4~X+!N{tft~1*jG_u{mkjAZ74cu@{f+AocJFK=j@Vm-_bz4kd^+GsPt)DuG*bU ztJz4O{*l&|w9C4Of}4|$_T9t5aS46e?fU49@uV_8Z>KxPviagQ`3+(ha z+dGS+xd>Rfxkf2js^*$wyI7+c4K<1*ZzCMd^ZTCVQIvdueTn8pqa#t~LZA>l2-ah{ zvJ)P;Z4iYuQnAtjmC#(1{X(3qk;{&a$ElthW5C#!qGNH~;&|5L}~dh!dPiTZ_H3 zOLbJX9a+;2IVadZ=tE$(>rzQ~4XZ-{{`HK^U<>1v1J8GGGA(qzsk3d6D?}JIk0G6i zMT0W7SKebW^z=UY=%9(PX}X_$_MPt+)O%bgW;ofiCdDn7y$xsY0sbS?04Q2>>QLS3 z&Q>WDQ&ePq8L}p>yXW&J`W_B<*-q)mH*LoZgiBbCe!7t_`R8%j@_MPGqfW9;FE~cP z;QIy3Xz%tGJ-#t*ljf@VVtp*XJg3Fl9C|gV9(z9vp%+NY@msMWLA4IVt?6M8Qn7lE zeE=*i`Svm2GkiP}=oss7hhL@Y^(s{<(F1TXxQhc+JW{65jASlhB>d(p#Y;h0 z0v1?V$Zsk_(C`eUkk}m8I|0*U2E7nG^ahy!A#GjhWmbawsm(|F%kmL`6BlH-b@W$ZDd;d!EM>wN0HrcPOPn3O6#X^sDkuN zcqwH?NTxBPNm@ITS04{ayF$707_=E>(tNzvU>iB)kdjp4i`=umy`5=weFfguOi~2q zQ(S3yLag*Kd;B?3FZ(rgQ~1H!RpT#3^QxA%jB?3 zVo-gbJKs@C$>+ua$R%|%LGvXCGqpOZ$t-xR^P2*W+-Mz|`a2EUJ#Q?^%R0Zc!<|x@ zF`YImBcGl8Z|YcnDa5cTB8hrb^uK?%LXXqpx;b*(E)#G`0kZ_a{|R* zFu^rd=O5+e_H^bia|d22oH1yB4XI--W)_jBr2D?ZB_W=oBd_9JcuJa zcnsp4P(8;d{WyXHW?q%}y?3WX^4AG~zW8^}+J$s+TMuiC23B#s^*<9{7Yq7IUH=od z5IC2!o6i#)@_ZFG`k&vOVm|SRoC$-~-6r*xjM=+a9{h7??{U>2!6Sb2TKIHLSHHB- z`CJD=)~Q-#a>^hfC)~+(MdR~~L1aquvBQ3R(AxjLUx@uNo}J-`K%W+o2RHs`-rRU% zpM1CZzWitcZ9hpwE7 z+gFu6iwvK&wm<)#`IfM`)et!GJSXii{L(d-$c*W@9lybOk6@;%(=QW{dl`a8?8b}T zyN%SIz;~!d^`unXrHFy*F@?<3mn4PqrZROwHlunSOOHH#I=&Ii(4rsbs2TwC&U5s% z+VJ~3RJ3&88YvKf?%H#))SyfaUyfiLRF2--1-ru|{E<>+3~AYkE!Fh8xFH z-c*-6&Nipmj3>D^3=?PO*qfu}zV(4P-*4Lo&T;s%^w!kn^785iQtKs5D)Pg&ozcA( zr9oY(!i*%(A$xXX;p?15A=~`SjsBfO61Kn`AC(*e&W6Xf{Wu7q^N-Wnh;M7Y^z32< z3WDh3RqbQj3x*g;zWkt_|9=#ncRXAB|HpfKyGw^rTW?G3QG1qKt(6ot5)!L+&6u%@ zZ>zNS6$vFqiHO)cwi=;!5i9m4_TKu-@85I&JCAcd=ks~JU$5sY=xch6qo$&lN09VY zhp(X}GYZ;{&qX4)Ez7SSN@yhBpW=?p8No#Dn3f!1oF4bN%>ABmE6uI3HeRsF*Fb9n zX{=z9AWnyGguiiyryrbu&%?Do-ZVtlD|e^egofT2T@KJ5{@m$2z#ueg$BWLs~K>(M&Pm-kfT6Z%gOlH~|HnSN0R7o-uUdlZl>Ao!%N=1w*Y| z0S9K#de~>rkUgVy8`KuDPF&TxH`zR;D-Fw3ZBH{8*`xoVcB(1+1kumUMyrNz&tFWG zZF3NW4xdH&Y+D<)-~#_#Yk6|=elypIpn}neF2XMTJd_NRE6`sga#z9% zUd+&RT|%?A179EKrCR;ktY>%Be7}OBq4`Aawrl6q$A@4;kghXbR(6Jr7r7O#X~J7v zSkTV}B2!<@>)<=nL*e>{kS{Dw4w&#g9Lt=4x+dXC-9UfEcGJH)L%puJZdLuw^ zC}4^>=0Ph>dk4DjW5kTUkh8=n8BED9v7V+j6m3<_)#Z+R>um~l=&K1UiM4xUSBAEOsORkQ;oj+O2FvN`4)HLQ*uIUc&bh8X=ed)LwpDeytn6Tr-(Ka$hm^gd?H0V zk&`@{;s*}7cru3))5V>T4HxMS2;EecNP*=5nz(T2lZvgW_dZe?FJ!+{VdKBiOVmb8W$%#3LQ5R3I)_u!^{Cw zZB$np<-L`2>1zeJo-!EO@Ydfh>1*c)hsnAqgQ>=C{xdHg@K#ptc)1@i8<}p|YM05B z+b*95wzZ-%WfH@3$1C=IJFhOnQt)+d#;7EZShjXQvz+aeDyWys2+Y;S&rG~o+EuXQ zmaw|1=}N|%=)?ubuTP|tAfL*jg(F=a_ zeV_;YwtL1(FUB`UXw6ebR9(=+-VE;B0o?II3>{yeE#W-RcCtF^wYGSkwjzxEnHq>U z{Xq}K_?Y>Y)9s}Xs;1ngmcHXlMsn5~fP;=yP2(rYVv{}3(@3jOxW;$hqnPcg18bpp zv-=fdV`OE3678 zSQemiiCP3*kE~Ll@kgGaif%dFE3o1mmf2gP3)c(p*L-kPgS8dgC7Mo7b-$0hyE2UIa zUREgKXF7UJT24um(}8SCUE&ODnTYV^1mkE_9a}Hi(b@6xd~RG|g=M0=Vl}8OtDs<% z&k|dWvs@hAs?V-(Y>8VN^7YlrQ*dMDN_9CcFVr8*E0|C2*sDQg3zB`CKmEBDTDCLk zbC0|ve_NQMCat47WwhN|h$cJ?vMP1B#Y$Tm^Y*x-?;+ zHBnF0XE>`cHVLZ{WA4smYrFvuKBF@X^qHD48NjidnpH| z&)0QI$0-o6GNM3Ju8r~MP%7%#gK(!8I1Y8F=PQ)UXb z1lO;`MbdpFA2sYG|Dv7dt|9io5M?O5vSDb!eoyaoBIGr6@CiHOa(M>hQhE1t;Fm_DBwnNt z`IB09o{|5!C$JO23qvyka^jvZx;Tn`hxGgLx?QZ%V3Ma`IlaXs~mqq{+ZAc)*wm zMO{~z@OsqSDGNpc2|^)exEj6At&grf4LO9n1pVAnHAfP@QmW}vjw)|@a8|rjPg_2H z&?;jW16QU8${SQ&R3-%Dk;;o>6Qk!gB+JG*%I>$!{7pWKZC8qPqn_IPE4hoTA0+&y z0<`Oj0~juT2WF&qw4Et{29FOe^&Ic*LPA8fE-8^?N%p>GDasnchcd;)L<%-^4oj^? zyO)_pAQl<#)Ld=In*EMWT6|peTQx|tEpg&o#x>a7KG$3DuaDaMEG&PyS{0~>S-zId zcsKJ+;_qLwJR5w>JxTMolkdE(eI$D`!tv z@BK^U6HOLtQDf-N6lsr17Q3a!cP44|@pm5!1+@2`^ege9A1$Zm!2OEvt=Wl5TxCqs zbL*sk9LS0Pwu+O4|Gs%rVj0T5XZp95CzpVaOXOXT>$;|_HVSgCnGLmnkwn@XDk2!* zuNGcOEZo}n>i%C5F3bxA;OxQa7u~v=v>+z^I%xdmBMFt3vfPQmEAI(E5hp#V+bSV%@@3cx)q zcxu{}p8gE+0fn;Aedh+w)${5eFYnYS9z%uw#~E9PX7!xHt{chSa@QK5=Sle zF0gva839C3g89VikD5MV@>$ou4H(3!sS$4_n7e$QV_oeQistFMNS_|W6!P~Jt?L{T z!5P2HH%U*h8d9+WGGkpQ6ex$Tjx9EHVN*SLf7#(ix?d@WZK$&Q8u!cwV6g+yYny@kjtLBjc3#jG@CAu|kT&z{e+WR>{i2*RB&Ee85fnQ&@rQQ3?6L&u;mnAA z|DYS@SX1Tg*$??$!gb8c4`=kbcn=dE=X@i>1COIvy_!PVMhK#FsHVWsiO;{ISZ;** zSw3;r+nnF#j|#Upf2;adKTRD50@%D|t*+7rPZnUAYGPP%1(B#cm&3*7wCC=LPWDPgBcWqfHYfRu#l@I&rO|sUqk*Va|)B7z0500fyo{ma*WI z3={cc<;LsdhLF$t8yboAN${TwMhE6fwfa@gs?A)1AGZSXRLS6a(cS12bA|Grz!6dx zGVY{l@&OBM)g=*Bv8ULtd+y)aLKUVVE-J9bbhZh)SXbpCZPy)@u(Cq|H|!0H7Ygo% z|8K%=MIDmLOU?wMo)49qELLo^jiYQJ(rMbH_Xe$bdUX-@-U&uS3y2}h#=MSXY(f63 zaid-BG+26q8cx|nQ~?Oh8PwOA;N96Wi*qz})-BsL+;ZiFSyaN(oFWYyg!FR?23;cbB;)4Pb3a;=U{#*lgvTxY0j}%K^$d?d2VWyS#j+OW`r^j&| zv|{Mn<(F@SAOuyn7^R#;6mC#idHn0)gY^SUfT;jTSw*(G3)=ZPHbqAc1@Cr5u<-;O z&fP604Wsgu%F&8wIiU1fpV8`I8$siEM29Fwbd4&9K1cmS=$4soys|%w^h1yA9R|dD zm#R;zUeB3sydAdMJ&msPKoQ86vW`1;Q^db@@g9wI1t_x1FH-0yESr54k~^!Q&(nfJ_eUqI$Bz2D70y`R{zdTWYrBpc2e($v)sCJf)!EV3tR!L z`je&+hIu(#1sm3Lx(W3XVytC5Vj4+A4iEcX6jhv?rEkQ><)|#QL95!-H0S%?)Nme3 z($O(7jOxtK-;fF&&dalq$II1BEwywDBRJ2JrIY?HXE#a}6_JQzwwnopFT<+~g*It6 zC^J>LUSy2m45L1TT6VUK;K#>6g;&rgoHI4UtqKD5M6<p0n-K1BQsNHR@ z8N;d{9yvdZvMAenTXo=V<1bxL%ikD-bMp!o?3&S#t^Bh7k_J6zHB7Bvh7_F-zq0g@ z>mu@s)Wy7@L94ATE=cORn2tiqp|#d%C>c_Qob9vI{{#6IJXIAV(Cj~BT^RV`BuvqDC@H92EP5& z&e}Lp5U8+RZ9h=3Dkq$61s=ew(ht^of|SY%P&l#hIQ`sMAk*>mNpc44mIP&UqHRfu zGEydclnpDih+Pwq(^n5>%@6Y8n-2eMgm&sbj*%%7o-$AE9M)Oh&@#lT2lqmE6HYTS zUNzud#mvTsaB8aM;T+V7`V8l_PWq2`>^OvZlWsI=oWCCa%&fdM0nfkO+q;M3p&xcg zcWs(^B_yJkD*@#5aTd0HEA)B@?~vS|f~r)MJ{n68|4q(aT(U{o7PLzaGtW=yG+X@; zvF2rDKCNDfM)@08ZEI1U%rpubcECB_>rZPcwu=RuQFm= zA+0$%y(%UETs)L&*DTfRcn~BkKP~F2gWpl{t);kC#V8&u-Cx8Eu7~ye)D^SuIsqm1 zLbZzvyU{!jEDm7}uh-63fdl?~{^=>>$&SuGYg6aDT^1iNl4pa~V813pDlP&3>K~DQ zKTf?jVNZDWp5?bv{68Z5R2`h?287(~fv)Sh@6p}TtcYIQmu%K?Otk7Iz2^;rbGhk= zB%h-#8;HN_eWNG^hZM84Ejt0MV3)JKSznrB0=3r>n9y~EoIN>W-VQ6Pgvw4~3|3E$I5VIJ zm4vSlWTnZ*#d=Z(4_3rrDt_!B&M?sb+$Urq=~BGd!7zipBsOnFU2i7#$u?kbirTn6 z=JgEbyBzn}|22QqV!%^_gGvJwl?2O%h$Ph~9=ai3;;Dv}gI^gWSPBJeMZCtm2R@{hawNS0b^l<$ftmeE zqK{ex)F^!(T;JvRm2|mrW9HxK$qpo3nrXRIK%X<{W{vs_qrPf}$J#V48@#~Y>TN~O;Wzav<&c5>hluVd! z{Nj38J^hq{_gXSOVcRqnR@vzvm9+NW z6tnTbsu9v6yyR)K&kz5ZxWT`j%W16l!)s;wZE*0jIHYZhCS`!ZazkTOJeR4sNmYV| zmtY?IHTqwpEkeyaXt_QNartNywAb{%U#3Oai5QYuO31&1!{F@Ubh9vnmSeFYp>rXQ zu!Zno=UJEGlcwj(Ga=QPws?`3Tp3;;a)%lwN(X~;sJXA~QX2LYiV&07S@(t2>9)M# zGDPSR*~|B14OhML_|(_j%{)c-=oHjqL#OTlP*{m?=81dC+im@nY1(M{TvtQ}{0w=L zE*q$A)#`GRUYj$5woG;yb_!HPXHm*^>{8CS_29LHszIIN+O2`r_oWU!3$0|Q7KWIt z?L$kM#W=e8wZ+lnm#&st6Xbkd|AMFCxe=xw88h%$qm=VWkw{e}XtmqneX6?aFSKQy8R6xJ=~!B+A>lT;USojkT&&0=GM zdi8Bt{}AT=!6^3bd91wkUQ3;~LuMFAKdKR=suTz%CFQT3!2-1V20VOB*Z zoZSh41(bD$2()oEJ&XHmwQ1V&GVp<6R16a?2Vg%9wRJUU6hhl)a0;vx_PoGlD985I z844Iid)7B;cpO^bUh9K_j9VnzbiL+u-7tVqg8qQ^rKBd@bybPNW`s*k_lz1M zky9+oJ%~JuE1BL{b4>+*2#nn%O^aPhX>EVVMk`e&gEXm3=E8!4ASW|jQ7xd9(^1sf z*d{!Pn^^Nv{yn;`aVYsxy#`vO$Qc5gtVILbR=cw5eic#Y6_}Qs9ez%^x=XuXZZHFU z5^BOUBQP2u<>bixFJ0M~D%!jZ*MVg7+hR9p=@!cAmMkCsZA#TpB@dpWgh-8mwE#Ej z6}w@|I@4G&^I*Ueq2_LR_4?}PL;2jADzHt-Ki4dhcCo3MN=`m=Yo%Zq3_2N|_bk!o@?8t8ESfGp;W%Nf@sJCI&72$3dKMJ# z6&`&AJ|tIoACKmo2=ZOs<{MPpIdEMvM&Bj*AX^%<3=KuR)o0SS_O@644 zgPiadE_(tZySDR{z%SWxys7gO8=@N&>|F$(9n3(R#8>@%k0cAK$_LUrt~t!8?0Tp;0&~&o$M4foZCiF%YtS zZ$lzG%`xX(Cx=kywj+F)P^%FbQ2Fd5#hm79$D$ZK=Zknh?W=z~`W zXC~&oCCxNN;X6>;OS;ee6^rcH8@9DMXYHcwWQHV5sKi_JUDPvO1{f8%C*j2 zqZEGs<0HPWt@*S&b)X3Wof^nRyY@-bJRA4`kke&L@ zqZMgs|CcyF?G8hyia8nYzC;QV!s2SsEyb@hIY5qk)Zsqj`#Wy|kaYckFobfiUQc|3 zcc7RX^(+y-_;x;@%wunL=Ff)C+G)F&R8Rb#;nh!Ub_9ar?j)zrSRDolgkaVOo62)Q`0nc7&8k?pan=vvJ-#cbrm}&KN{q2K4lEh zuUh0|9WqYx2!mQK+YtKj;cQ}(wrC$6JG=f3HI2~mq*Do63>@0@*Yuo6KT-+@H1q}4 z`@hd_QW3!m?IrBA(;W3jY`S;t5YwX@8hQp*uNvE3*=ii@n43eqz{I5alrs)OzCy2G z2%R^nx50Is0L?>75yW}G96Yyk6VBe2nqx%kjTy{GGXSms1Itz*=-=2bDQrP_#T;4N z70Yo%?={M4>Pb~buLm>qUC^jq_xl-F#bj}o8U~|=;raggr@@bUk9q3R_txd}4>h08 zI6AeX?ZCd76P%-=ET;kfHc4wgwG6$d4fgz1cAHGmlh#qgvM-`YcjX)N&zT6uY1&X< zqGu)0V>+$zsU-D)mfG;NxKnz!QF#A^$uf_mihQ|LOi_pSNuEi4c;^YNKYw79&Q*An zX1OGI>A9n+?=V!o6p~+D$9-hw7YqoI@~sT1RUohENMAVsRbrm(qcT2=Zy~NO1qahT zQq5qCT?~g95tMNtL+tv&@H2x{0~Bv|h86czQ4X*`Qr~wrwmoTxFi^$Hd%7LWFPW67 zD_75CuplbA)`n>UqqhB*hQ4~N(ZVbGo6nfYIN3qJ_eI0Q`-8%11q|4wmMs5jcaK?2E2xpG%8DPFf?#V>U$!USA8!mO&Rovh!o7U(^@9v$8vWG~$L3^S%ZW=? zF?zF|#A@3m%ImnR~7S=4N!9f zNJ8U!&0_TrWwQ~Ex@mTfFiEOm=&$`UxL1GZbSx=IMzQh0Ynhnd!u)(6>ILI=hSw25 zONr*(VaHV-y+L%VTm1Cg=4>lDCCg=sGTsap>$UTN_q-a5Rv6*bh>NZs2jHo4_q_rk zk>O+@+M&YHF|Tz^2^lFgI6as#uPN3z`0_#Y++7dR!Cd1U{4be*r1P40l{cMObqig~ zsIi~(;w@ez4i@B^QNY+{Y=2jDY{6=_8F(N~o@uF)KA>k&^8*#wVV`z~M;n47{xkP0 z-yqeht))^YEvi*GTv_6B7aUPSDpzx(9|0~D2;BM7p~DiF07S00)?dYW9@#<{<)@e7F7s3 zKtJ6SDE>R3FKL2aNwnv5MujI#;S+``!k;-Etr`P9dsTzxP&bde5l0R=epW1)g((ZUS zN?E|jh5>FSl%=21w4!p`$nH-HP7s6W~##LSxww$@4fZ&m+-H!aH1#-&Y=m(w{wwy$sH-7wxgLL^AqbgicV~8E>}h z1h0+~1VyHzi}-F-ydJY-cDec=Y4FOXy5oCt(})oK$M~}p$SNScV8Eg3Wfr2SO?aQ9 zp52GO_M=9HF}eO)ria_v?MFt&@3ODZ6Tg@r z#A}A;X+QK6`~Q<#zIs;6r#4eDM1LkREU2RO7^kpxQtE*ti+RN6_%5;(BH^XC; zg=C%_hVaoD&o*zpT5aBXDWa(3lyRahAa>22c5Z1CdSkE($o_M!v@o-y_B5jI>wml2 zJFF1fqL(>WU@Nbgec3fqv_s4ffGBKRDtEb3lEf9&TyCr`8ajT+I9Y>>wlOfzp`nBl zq>T(`gxUS?P-T9S<2^I}vL;L@{TjtGSBrC2&51NM}%ej0Z39v5#HK4ldHN^{8slY<%$GscvFcy>pi= zo^B}6^6%fPn`()7c(X8|PTR)Y9A_EBR>X;J=erh#Z?lu;Vrv>V;>4RsFf2izD5x%b z+F5l~P(%E4ZE5Z-*`xkLPPv!A%@yn6dS>EL<4e*#kXCNKciGUdn!km>Lm*M=iez?q zQqs?IQ%8ZWtaD_vrL*POsOKj4ui{UJJ%H`T{npa!Po0ooiTy}hPgBIA#WV>39Lij3xJ3M)xql2 zsu|_+4E)Bro0D4hk4qfCh#r8yn?SW;#X0jPYKt-~+HftsY|w-iE`{$!8}u2>6={3a z{F>Pkk4L1@h@y+eF@A&4`aV~aN`!eL1#AK0$!MA+&$!_rW)^Amjs}`QrPzScm7nuu z#l3@JmqQtfFh@%|lx5Kvyr6H}CmHIXW(qZ&rJWM=1vLyqd!;mthS#xb^qLKFH7;hB z<&Fj@3PNo2QU#pX!4fVw)hLcsWF`>QI~n2UKu=T=ffgIE)VexlU6&ac%IC$))hZ_` za8kT+2D%2W#D=ovA^Y{ZCb}nO7xBMQ3qT=>PM#!UM|IO3k<{A8sA83{6ts2OXZ^@7 z2^g;lo?2mgxnVBXKOGm@KWm&A(>uCE7~=QAFh=GyrxesWWyy%c52_(2t*#O(vP<2BfbcESTwF-c z)z|wW)Jh@k?ADD|P>oDK;1uKfp^~GlfaXO-PqD19X)XzWe2@ZB1UsF%-u`pV2LY5k z$-mT-rUKnICkxMD^e1TbK2g<3QF25_E}l&QY{D^lmFy4N@lOjtDgO5GsQoywjyU`< z9IhyN%rG1*aMZD{+&+Og8fHQjj&;#G?TC@74P8NlakM_I*8%h`#>SdlzfD2+K`_)t zipMyc`9!!V&0RZkl{xV|a%Lc32WYIbE-)T4g#S6?sv#lLjzre_`J0MwF50l)Q;K&p zqE1{A&tGoSjP4Uf(*-N7zEuqktHtv>7oKOyBN;b;^uv{Qg2L`qxVg%J7YX2ylF_~n z2-> z_EhK!t1j!KrUAWoMKZSA@Isy>+x}OYx_N*|!F*WnlVPpgsq_%9+N14o9i_u3i4;SG zv4S@q=HrH!pV;aw{_E+|``{|GB2ikHf98o8;FDp4O;hlwy_RB&rY7x)CvuIws>qORq*e zcVJq_)q;71_^ zQ_j4=u7P1f2!kcFz5gU*h=C#{?sYM!K@FI9QMT1xZgaw&wQKf3Z1gM+Zk{_Dh2=j< zihSy%I8&Gc0g~jm<_rbZn-8JI6OG20#9w~Q&1%*(Qi3b0afEXEz=)!%?wz`m- zul3~e0cv2alTu;DTD}6wCJszPd$*M-uV4evK&3n{%#JEu5`Raxq0_g1JvoDDGh#kz z=2LgntI3XuLf#7hVyT*{T<01*yHaxQ$1Xzzj1$>2bgCM6T=DDjiq~uJ?Y0>(M2w_JxmHRp*Pm%Y=9| zY_nG=@lkAaTYwaDJd0C6(eVVU%8ud5Cf!aoAZ+S2`8I zQIVZ4T+|}urhDX2bg&J|S|bfdFUS`0bn%R{9#a&qubHkfy|Vm*iiPS&wOM<@o>;*7%iI{^m*3BH^$|) zwH7#RS1L-!eoHE>$y*#aq4JMTVd6$pk9@1`$;|QMeM=B>Eu==ua!*si3qGr(l~w!K zPzqI+?L{8av~Zu1*1}5nc){aKOHItzTsVKp$cwEhWGC$$8ZR>QG}oo@cdw>1`_9J> z_zz{Xh?KdV$MZ0mXI#Yz#J1q&pZU8zmXAhP3&hw|t{>sYWLaG~IzR96xb9d_gFY!n zU1&SKWqrEOaCm$4D;d-sAu*HicbJOH_rP~X_+ON0te(kaZ(!V;4}|~>sQ3G^UxaHMNbNy(qpAOMVuX|S`$prCYZj_m&q?jgJ;SpBX8nxwb)lT;>z{BXaVoNab{+MV z1Ewi~%Bz_+5`X!!{e<--?tdHh%dM#D+yA<$ zX5C^TD_m`SI5GpL*Z6-uh9mwh4@?bwr)-|tP_42X<|-z)&##wIJ;gUdT6`PyPNL{{ z<$EF2Ne;g*$mT%^rhTq0B1ui~u*AJG6+CM7q68{lbor%5DkOZ`26xw+rX&B)9hCCE z@|clfwKY>R?*8d*g(9xGmYIuBRaHOCA0s3<|1)FYz{#Za+|zLlO1dRqUvVGw!_%3s zj-93VHpk>>|DNfY-jx|VKXAm?q9cW31G z$Nv=_A*Vk5itu?;LIW~D8J2(Q{gaRQTP3dAxz)|ZSjp|Jsg`^|T)6md&`mw$7T*J{ zNY6!1^MB{Vo81JX1QGtv9k{t|ER<|!-)5-%gzf|_I*L!xm^G4AbpuluI(^GR#_RML z+J)VT@70F$vU(x2&tt{3*l*VPa4;#-WhN%^=mT~d;!2XgE>rpRE!a^4iy>iCkSCi4H-{Y~b9CcGRz67_v`Es;+#oFvIUh+|bn$ea9svo}iIdm!V z_Q}9RiBMZq6%6r@pP$(P=z*Wj7_R}Pt@6(`Mz)zxT(bKHH6kYrVDU}{6F*4-(_~tb z9I$UD%)IHg$2UzD*V&$YTl-@ERZC)Ay_m?bhO@HVhNKfHFyt{u6m2JaNduuM_32_tkM#wlRyH4^F)boKOs~bwh?PR;N@H}lnaPHt# z2K7aUOvA}oF;^@_v`dX z5S(0vsVCs4O@UK^(IMJ*m8xfYF&%Wl``e;ThGe0`{P%mB%z}13&7Owyn%Y;Mlo9QKIm{V2do`ov>!-RRIurn5x0w?BDn2xKE zby!2+M3rH1e3frY2|mvZ^xf__9PFfSDmLKW>$#!mJZhfgX-w&RgX`iKzr4p14EPFW zEJ%`qlSKrjZnF&2MmsCsn3$p_sSk4+LZ;U4A;0ZW#STSG$y1(0gRWwR!t83x>VuG; z)y@LRU;7QRL~Xxx$FrAqPqNGB2Dp!hX5<9O!Vz~7ZITbV;5S#c5W6jC+?<`V7yyeEHRrt34P~~a#6>LHu#x5*i+c|C zu@#v5EX;c5>IZud$L)2)Qzrlg-FVwx-XXp{#ucn!4&PHI0li`on=h3pn|2(gVo5K> zYd&|R)4?$=)= z%<%Q>A`K%4cx?{tqr5_w^uO+qEE}d$1O&FNVx)O60r!QXk~2Q#g-*@kyz3{3I_w% zv;QITO7Gj^A#kWlv*2gkTXt3@a$2IDqk-v|lX~Y^&AqaL>!~Q_aqD#ds@o6Y%l-$# z{dk5p>79)g82XAW`B$Dt5AcU!LO#X@YDm0(SBB$CYf*I7NMF zf2>Ki7BVmrId9(u{^x6M%%exNpEsMXJ~u4(+9QIrqcp>#FjIRa=%@I&C(jD`?yyxn zfxhjPJm6M)wP^qQNG`g-&daZ>h0hqJV+Q&!8OA8uV*fba@@{@akRDdJ#V%Q^gDpwp zp;eyx*p`!c+eOz{gkZTsxk{=I8~jj8I7hUIHThtWeJQEN)&BjB0!J&`GT!eV=176T zs2Q7?FKZ<*f8T|b)E2rc?nn#UI&`y&6Y0U?#IaEat$ zN{6=Fx_GVDpi}t4ID1pk?XtmeapdiX0k|qH6>UpbK=PebtND1%^5kf4PuPDIcS$T}+Gf zxVZFh#Zk!tiLpOFvoUi~jA_MI{ z*55~FhIsv4y~^rday@`tbg5i6+OT6Bo8?#+77IfN!#Iv5UN7oezJakveTZ`4z@<4J zm@1_2Yv?iC#F(k)p_`*ku|z;sXG)g)3WS8^@f3F~7$h=3Qi^ODjbG(2#~UiBtBUt5 z;6g?a)HOjTFK4`JKXbxW?qVG{dDN}rdHNQPE}`RT9e>IdoAb1L;z!*W*XWRYrN7oD zf(ERsF#7n;4pC&RM^IB}x#IWj%or{b`zdLqU0hInCcgjYsYD*X9t~gDbvb|q7EJ5+ z%t)96J)q@+zPAK*mV6J+O03M1A{s<*`ge^3dN=52q}b~y(hGsaU-!iG_cV5J{3l5B z%c-}|ZcSws^}nK-Ky-9)4cl=4ifm->*qqTMdD5tEz>)b!B*WTQjDJjxWAXpvI~zq0 zL*kpA%o=a2)kOmW?dE+M&4ETnc&@ju9P807OiOn?9hC4PIeML@^Os>w^`EC0wv;>~ zH+|2ZH?7$o#=EMW@m*!S8yw}kCp|J&uI0qx+rnO{nk>ckoA{^}EI%$5wJA<&(XWcj zHJ3LPNb#B7o0Lp#+)cqrdo2MT+~KH|gxN>-B?Piqizxp7WHSkhk~qTi$dBlZ6+apJ z-Nl6RZ3Cusz8rY1n;)R%=W0VLFVsaja2b0*(&tDYOgrclcs=b5JpC7q^%MN7ylw7~ z?$$)Fs?ryd9m&Tku8J%|YC2Ou;ZI=T`Sv^{G^Suk#I%Iz*rzc4Nlkvj)2T@FoMOUh zQd|G^q`{t%2XU(^eAP(P@Wbf9FFp*zGbN(h+XUvZ(4&F9+hLBkF2-N3P?#=djx*3f>5~V8M$a9qFJlJmOtiMTY8{N)2=AU)kZuXM8#Ay_h3h8oPj~#3gIi^U3g3l_;c=@u-b8=pc@35 z?!YWv#jWbG#!Q71+L?ra(GEI5KUd~EmA<}-Y52kljARxSJPjuL{Jv_5;@9+UtSjbjpFXVvZJ+kbZ>bj+BhMsnn(>}6 zU0M_gw%mUyy(uEEy&NcKt}t4DZ1$`?kXW&IX6^$-ywf#W0Pr&vrgztwonF37I70YB zI)o}=js=;KqfOwOZVy(E>I!3oh@MU|oMuMT2t8BbF~4qlVed%uKi9S#HpxU5QhF0N zJgkwQLx+l5hlxQ_sn#j}0v+h7YMvF(iTW3aKi80kL~F!JPC&yb78n;p`-Wb%Jn1vQ zR4&;CLHJI~h82P}q)P>%)NzUr%_MNTD<2T!O;u1grwNJ;dp*0;eaY@2uSZ=j#li%J zQU_7k8|A%x`{;z_>G}t_n-LQ#;x%UFnsA|a4fSy2V`JTEyq-Q)T0m^SzKPchK2CAx zoAgT1-a6?P&@Gg695ut4*A*@GwV%(tU4)M=MW0Wk1P?6Xm! zP&b_v_0P3?`$BnWr~Gks_ej~oe1huH2=lJZL41s80nh&O)&GuPZ@A8-ZbL#2idsI_ zqR!CUhzW7;ELbyeM4S0#yHI{PB>9k;qslxd@3B{z#t`;KaYp=?6@D(A z1$9-VsHgZqT>1S5odrQtzAdQ=@I)z!MuEL97hrG24rCi?h1ZczcNnUK3UT;%YOL8q z`@WKBs5Q+S=W1uWb$>EI&vDwQb(C`$_%%+aPmma=7(ihs5vOGwar1-4M9M6bUvRRF(Tly!V(7qs=O^ zWeRLNy7b^sdl+osz>;Jbj(^6`hS&=f0T74_?OF6zV`r$NU2lvVX71P$+?}To`7frP)57ucZ)dEc_YT(9+gB+XZV=0pp&OaA zc#0uIuD>=J?rHAnjT}#16O`_aLixHqc3M$=Y|)?$P3eBka-Rnh`^DMXSFk0sw~&L~ zSi|GnkqDXKS*28#_j=-)hD=RbC&Rz2EP!Aej==p$5M$Y#*R4I-S?*g5StjB`({_aJ zwxP#iXmceFZLV*lUi11`Lmq|eb`Fo1qqcm7Ta4`#Yd;tLh7TYcl4F`BCh-h8 zxeWeTEr#p85whkKC0)Ki<8!U=wBldkAl=J4^--MxoMKgzt{7yNcyS}QwBk&d!-XK) zz8v#pClAeYe?2uViF_o721?uSw?1{dsEj--+01Fulo@1gYMsCMlDCoET)aNEFd38a zSYV)SU_*9h$Y>4JAz~gK@UX4O;LB3_?eDTG-?7}9!j$7wG^92J5>sI?FFT<(eTJDy(#xb-`G$3 zKQmWpdMwIcGQkPyn3WkwXCccc@yO}DeA_Uvf$3_cic+AHQ$WhCR2I{SgyonQOC3m# zYEUSX?SAK>M!EhM^PQ{rhRDTM_CT{n9;>$xf*yw&tox8n32X7p+D%!P(TS#|stod) z+YH;LV)Ib`+$KTAxV?WA^=ymYKJDp@%PXx-b#*S<|Lo19YOhWV* zWA5~~Zsudvu$WZTvDqPrY?h2C|li?Jp|(lal;sQu@V zfvVeld&b)`ISce}7qiNy-@a>Te4SqcdliMW5V(9Th{rYm9{}<|4ZqTR(=}Kv?^rnc zi1Bm=6!T2U%29bl%iZ2Hj6XH}jWrcHbRRYT;@(o$EQ<@?-+;Z&Y>yWqyj+8cabHnv z_!?T5<}$Q-iR1gD-Si#1ktM{@#vhq>-`XY%?LyvN7;$o9*^KP8xY!fegBWF#_<_pZ zNae_7LQgdz6yFDKOqd?efpvdDdfo-RUYL21F2Sy=9(j^1+9f~ zru9r;a~4>E?ZzU>Rfp0^Rjszci-f-sBTkpBC-R(P8`6eWOAZWo$Rkfb{E1o@u9U zYuE`+oNtQRb2FVd&nBAeV$0i)8}wHiR8*|*4%PjJmX5d7@8oU#(`4y*KWG-_lhDWg zJB%tnHTD?GaTu)N3cFjYU(Zu0={ci#`h^*NGhK_?Z1*dHUF>SF#TVxO!#Qr31d)r_ z)Xvh6*X%NumYP|wnQyH|Yu;ZknY4EkdOv|#QmY4}G>+n29mS!xnt7-3_#DY;;j&gQ zxKfdQR+ki1QmBhCsVQoWloDAZoam*t<^GKl@43jS?o}7X&^VeoxDq;Xr>Fk_67D8Y zbFa+OH1E|5jhw{>_cN^Yv>xkum)yA=)wry+UP9FUpBsUtElz5s&9eK`IPP~Zx%9D3 zZWOgv0;?Cs#d@(&=D8H(N(+4aOKJ&kMW;D%iCYtTGRsZonthC>xk#$vL2qls-fDt- zm%y;4Rq!%{s`!>$sElbjDO&eN3N%-{HjSb7(%6$VZl@TdDSPXSX3fi_Uus5@aZ5$q zDywg<=HF*VdB*-DI(PgwGW}Ipg~gVNQ1^54$Y)Q;G2O;r zHeI3pJY}3+$Aw1qit@~sRQC+3VT6n{YGmRxp zUT?_e?l-fm#>k!xta{CnQMyax!GurOr#|^xx7-^+U;Qsq}N%oUJUfJ0b$$i>Vy84w_o%+1Ia8o&tGGq@H*SVo2! zu^Ts~BRJ&DnjN98Ez7D#DnN-583w3E?u`%;n+qz>QK}(`DGX$S#sVP<(nga+AqXrG z(L%6=F`*lqK+aRb(i9;>ByO0BLa;WL)EJ@T9!ANCm;`kqwuUz~jW^KKGA5YTGIf^1 zMYCwqU~G(bWa~ai5xHU%Ll%r=+8LP2q`8Q$T8$OZw4{oWs>3!{o4Jf)U5!3RPMx}x zp;e1gq*l`!h{816;9Z3?u}P#+rChCAuOlkC72!hXN}UtAPNj-8>s-oBy`m>_q-ooM z&~#3gJC!}_2H{lxQL*J?8O>CVF8sfthImt#byKc#IY?lO_VHYV%$t$HAQ2iqH6;SNr60>rWhxbabhm zSV3|+FLz^=Igd`GoUUuPj)euL;B_otmh+j)Z!-4Y<5QK6W70|NvCwBpZr4+ui*S<` zEatrrGW$A9c2SEiQ>vDx5jk&46U0AZn=eW)}d&~06G+mV=qMRk3kE+P08luY#)ufa15y=XFaTK;P9?rU?*1&j7Ul^bxS@m%f&yyS!tulGlah4 zYm~F@Yx6dZchtuO=>8QhceCiVq1vGS;jK$Laod%hEa6oeI>`S=*9Qs{%FQssI4{B zS6}rOXG$?#{MWL^(0Co6(8e_!pH9#3a!P56t0yZsN6P;IY0jNETQzsgx~o%$P7wYG zznZyrGL`xjT`5a7DSwVj;Os=@e38Gi3aa;#{{S=lPFr$|$-5nSEhTkn(}(p^cqQTP zzSLi|Uj%a!+|hT1S^oeHMeawxRpiX$Qrh3VSF~&7t1iiSm)dewOXVqeq+g$=>E)d| z*5Oh=`=fR6IxZH`cNF2i+(#;uRKJ>>zx2xA63kkc;`)`N8qn4;eB%EACR6FIN%)sn zq|N)4DND+wyDZWzImvY@OVr^Xo|S0-0FwTG7R!;LMtoX*rDeEq`JpA{a)O|nG_Ovk zmVG+0agxr@mo(N*aYfI6BRVxHZlR8pl%u8B2hpdRe3@dl)xl+O!nA5e>Q3FilNvfz zw87Pu$1f1dyERnT6&~V@u_DyspAy$f5GqTr7^zA%BNpw7QIbvXwccBdJ;_MkGNUPA zjNs&z@Tn%!y{~1Ru5;UM+cMhle5qfpjpbG`D&t0K5@z^JBSNFj*jGG@7L>JSvZ~(X zV&o!)jS>beUdW5jxLJD{eYZbDm#Qn70c0m`Ya5Cu1diEaJLWdrqWuZd1ILUo)(# zT^#xD)FEW^_!+6*c6JjEZgR>i>&WK2$1j7;Gpkoi8Rdk(^JnnY9J*V#55!uB>8d;} zIKC21qn%fJo3v6m{Wwo|armzTp#3>7s!QT^eaS~(7sR5s_xvgS4y^X`ipqGGKeZJ$ z+xoFql2Tm`XWBeV6O3cFOFXzD{PYrkU%=@8qr&i}<-t6&?I}Zh75ILp*lhe)U5exW zryu>b-~EHv&{Ct)gOdH1pBDL;uS}Gl&?;Nm)%PT#{{YOpk~aN(S3#pzlFP)rgqMk4 z)+H@$AsbVmr#t@uyHjH1lJ=G<=rI0>&c4Q_X{S=Vg*3n7r^WS@p1r`H@k>Kxy`#3f zrAbO}@z3Cr)4b=^E4kL5c-&w3*%#KH2^vO~3VA3eZ@rmU-;-w5eN%H?+0M(Pa`+Tu z^Km(or&4?7-<|&e1Enf3FUD|=kxR10B~nHBL)^{D{C*}-sLo_5IC1U#Y;@H)$6)=|YxPn)5OSB#OcWI(VwJ*%OMpLCcnOe5j zaJ)(q{{Sv;?J=D#{?=XzR)W7*W!~~7I8~`EQg>%dOB!`8g|923?59c* zv{jBZO~qPyRE(Dkw9)7HZnZeHnq7$K@908FxJoiw_*!{+8fjBjzVljZH|G8f<$A1_ z>nht%4ZpyOyV>I0RDEs#0E#X8Ta`~sp(%XN+`Hrd04)CiBF5G#p2~zRZ`nt^tG}s! zmf}*}X5?bzad*+o+kG6(LK#TPQaSNPiXDdo#H}nbW;F$U=3AmQHDa|;MLLWKd?t(yg5|$r%IrX_*d9TW$fqq(*2N}i0K}~} z86#c1tu)7_4A8ooYNaX1^F^-|YV%LXT2&n?u$tB~jF)P664{=k((5U>!Mvs6eUBoR z!MbvT=-0&gw6Vpw*<|hDT=gw8rKzlv?zuL*bFuYUxXvm`-87o)XFJh%vE1yaUHIb# z32}FVwfxna8u%pR#bnnsU*GzBr&e_%^3CH~Sz^xKOrqSFyLlZvkV+SCKnC0cdu?R6 zECMHP2xOJEiJrv3+Z!RRHNgiM>dR{d*skw_2%aSLKyKl(z40+|0{oC{Oj3LSVf>%K zM#b$zSke^n^j>1Ct|67v&5L&SrbT8O4Jf$e$mG~mt<0BOSAZHNpHM}$rHD2s%v`zn zg3F*rla!Wd8FL#Hx%>#5*|WHS#>szM3>9vWfyLuzbwM@ z*fIj`9_hRN7~Teh7nizb>;}1yn3-^GFuM%p$s}jmDqPs)Zu+Z;HcTC&goN@M*5Dv7 zaf5an8YgY5;$|hU!Hx_|?%am247bYuMtEBjHKMTB70L9l*W^8+$O;1Zg4nlgHyado zVg-R`$b@n1UPQ>4zpKPnOg){$0Y=*V27ROU$V)wpUg5GAAdhFwnciIAh__u3*%J>- zFq&O2shQpF7s(A|O!%t>4BU9JZVba$#X1_?UOyR@D3g% z^CiF=C%}o{Yh8fvb0xVGAUk%4+e0inS{0`G0pFlSym-a=GFrp;cz7AwTLtkCkuMMD zM6zRIw!|%)-YvPYu7POqY&K|YN$e&LBG@>EwTX5M-)F^)qLC~H#>BX*g5bQC=Ft0z zWC4;-OodY92PwWHITFzsBTPJ#6-+z%336x~1*rBD74=sR5@pPnAT874M{a{h3g36w z3(0O`yS)Qt&^POFVIy&Gb`%TA?;^TgNn{7hF|5}B-7Ui8OE8P&-bZocQ7&Lok0dLL zeFD=O(CGWj^X6-bOMIV&gvA~LS;t7BtivbK65XL{5!}SYxdQTIS%Na31cq649n^NP zx4z+UAm|(4SodTy!c1?7dTf|iB4R>i)uG!#>fe3eh^{hQ7%*E)A+EZMic9fdwE?BU zh0RNpds&+Ti(Oxm7%pw~7e3O=Dk%MPD(>22C^>!@n~TSy>z}}HBKp-yeNVL%l(>~% z-Yz1m8y6+0zWeq^^_5h(m6a5<`2PT6#f;quu3lr|G<9PkJFj{XL7}ri zgj9G9rvb4=ZUz{F&FUMlO7iF&T#aL?Yj#EspA$JzPg6`nU|;NLUWT%+*9&>SVM#*U zO0wG1d?obzOIW!H8DV@)Fr&ZKM>?LDQK-(=kLGzVYdIdD1!8&Qg8|tL@M3N&O0yeQLCu!5G3ln4f#g z=yGJ{{{RbTtfT0gc(2+@b9`*@ucm)e%qYUL{{VJNv0KCSnDnDf`@fIO)}?BczwJe? z*M9#1>{NT&wRNccviMl>{{Xd5{xsJ%&wCF|+C_M1Q|)UL{{X7zTTP^)OFT+Xn`oy- zqS{y9Z|ln|du#XNXIh>-+q?bv>Ug?Tef*RvIepwM_hGs1r%&5fin8g$xmg{^rEIqq zwDmCMu;xmlo!#+sy~M4zbfo1UmHg5C(c>q#ohOEHaD5uHJs$Ro-A2<(++Qs0yP0(& zMW>mZBIY%?vvBbWC68!gT-N1Em1WMwcNViRQll4>R~t00YfC(f zO%)+kPkYRui>dp9JDRw4x(=mlrYqwxBt$k%UETtFPg^D`c zPfDpKoZ{DNWyrFHP3n$OX4g4y)G5b&((Ne1l%94y8uZyblo`=;k5i5Mnaiotayb+A ziV@o7+uY!+h(5q0RilntVQklW4>rP(eEybLuQauGPNQ8cQU$C`JANHB(($run|ZjT$GVr?jO{NTZ_(6fw*1Gqqn*mebFTar3<6{h5gE>e^0x1lEE4^L7*tfpWTYy3f5OfSJiW{JZ3u9s$ zj-FMehBt5Eyx1%yTJfVB%P))ULbmuDQrpU_v{!3-F4it1wWIXvblPvfK6@9E{713J z-EV4EO?;5(wSBMhFvBKv{{UmMjw`88gr5ietMk-BFx*!X(1z>_#Jre6J;6> z<*a2NxtAh_0{o~)sncmqWGy&R;6D&ia1N3rhE*iOx~rE>U2A-s~YL;DtM7= z*lU5!sRlKAg{K5ob@-K(=W$%)dqXJwUp8|qX(xQ;6~jMa(9}ffQ{2OXlkO<( z`$!Pe(wXs4Eis(W<5nVD16RN`Evre5^3;jq?Jp~$CGaNbeNpAw^|Dd`vRIxQ$8 zInJ)4F;x<%qwc9^Z!Sg52C=6~grzEe2}|a2%Kg1cxa@NO07*5PynM@(mLilVEi;Z7 zQ;vU3RxjeQy;?Y(@mj^H!Zp)Ul;xM=Zr#Ro>RD-X3RJM>)+==$RijN>*DOy_3iPnn zSfh0C&oyhmksyD!w;D6BEE zOGI)nDvV`J(-5wylZ~}3dycH`JsKi(&DKbNdd#ZZY~CxvVY{pC!pO^^6HDCErsiBT zFp?>HhU*vX;gUrOySw!>=s|2WxwI?E@iK}wq>!{^SCurjBWWV^Dn6xnNa#`)n=3ev zGU}O>8FmiED!Yqhit|C5EkIaEz50c7mF`OB5_TQLu@oYi)NV5=Q=2i(n%P9#BxiRb z&IKIVlape`7T!rnXFi0CN0j&>1e!*3ora5MkbrYzBRY=BnNFf(!Cjh076fs1X3(c8 zXso)G+#N|9tZyhmx8!W3pp2dsoTQLrD{;7OPM;gIDQS5ZA*0mE_oC6Fu2r7zZw)08 z8BaTJ}qhx?Puc`Wo&Ut4b9Y6m=;QnN7$^98Kc5+mlcNQWT`3I|%^UEGt160yY+m zS{gwhgILXlgosod8AuZe1|W-3R-&+gF;^`>MJrfR62qi!C>a?dhe*;aRK)|7R`wej zD7V1X=7hv(jJmv$n?}CbUZTuunwuDPWcJv=u&D8|gINY_uvc%vBz27h<(zTP7Qcnl!?J z7GsHOWN+iQB0K(TYT@vKF=r>?oc%o&*mnLAv9)xd@wi?k5!I>_FyAv_&h8E*o z3EWWT5)E`rC>|qc%z((5a3&iGaqjlnj3{owio&KI=M)lW$%f#%VYB8%!d@WAo>&9XF?&S`nD<=?gq@_gMT-ty0-(r~<}M)Lkn1rNuz{iMxsfq*=xOcqU{%qb zjWQnMW;PivWcQOZxX!~70~1^r*SHmkqMp(c?Qk-axMg7F$QU0m2Nxqe!-#J0AYpvL zm~IO`p;JJ-c@CgUKBBo2BAmZGU8v6^8)vwTIlou6Bd>8=EgNb-LXTWF3isu>_9 zFWr+kk1C47Ug9V*tV}KI7v4H|GnBax-9w}Z&8Cd|26h|FR6e36 z#3i_lOPYC+EAd1nnF(-`)(A5p2<-R+5N^nUV3OR!ka0+jFj)|plHCthBxHE1wOrVf zN35YmQjaplh6phkw_sQ{5>dGJHB+&UA)-RHoEfyDSyQ=LNYe_EBw4Kr#a!UWs}&$( zxl+E|zlBX5$~B4(|?II?k^H3Uf_oyX14do@Vk-&xbxt z@^elO%^gkV(-wBj+0~Z;B(3Gl$x=qK@o?>34n`o zN>b4_YK2TDGt^Ut_G4o8KTfYxMPo!PWQ|IX)HKU{?dDRk)BgZP-f)Hc4Ui&Qxl#I# zk#DU9+H)|SHR#*tlA$JQfr%|zM%5Y4t?L>!x}%r$#gwT;?Ct*mr}yvXgkk(rUy){s zgm;u_>f7u!l}AojMA43Ln$*kNlNaWLMgCBLz6woc{pznsE7P z%3Zl0iiV|4Z#;|jD$gj|E41r|=lNuC35=^M?<@Y8FXK9>qK>HM)qbBv>^Wony>wZ3QFk?N}Y_L><+ z^5S_fGsSKH0HiC+Izoe9Ox$CUT_3CaaWTLEC zx#+9^0C7j?h9}MBk2`jG${*7SeDxY#7N?`J%U+*Wq^~()>^g6?of_V?tE>K|1Buv?ZJ^!l)c!> zUP-@^-u~W;>FQ;DuwDtj#1g3p+Z3Z4>555Tvge=vq@V5faki1D@BB^+{{Tp;GM15V zu{LzQKfz9`>f*bv_M8V!YkXw9(>tD^;nYdaD?P0AN^yEPzQ&o!^z|=l>N0tBIoNHj z*O9K~ukA9<#|q7CFUq7ByWU<|d``9R#HyhuuZ8yK^f9%%n^UbRD^4do=+jzy-t;i` zdkbYby43WQ%yTrU7p!8}na?`E%V^DaB>a81EtG#*Qa?O=?f%AhW?tmssySJg-ixO5 zr#W%G*Zn2ps@$pRIIpAnn6_l%q~^i~5vZvg5evDwCQ@QnXg+ z?n|*7PjN<@!zz$XaBW|^QkS&|JH|P`#jAd7)-Z2G*}u8WtEbRPqrFU*BJWx$Z(omr zwSD4(v@?B5{>+}J6zHvLe}!x2_16o_Tm*-gLJzP@n zS$32mLdQXErNaHfDA+DD=)R??R4IMUDQ=qWE5?~`*r>ISa2q10H>_WgWz4}alEN%;E&{kGuy)rl zVrz)DeGAmx_}I#niqfGMvc}D>X7H@cEiQ$Y=DC{F@fFoO78Q%0<;sc4l&e$3v@C92 zI4tEA+R`xlcPD$6I+5m!e)>wqbqRE57OY$y;8%Qh;UBAllrujApo(u}aj6Uh<6 zbsCH!+FjlH8tisn<5HfT4x+W?WSESEki1@$FzzLnRU@`MRTD|*sVrWWg3d*nu-YLI5zBE_9q{~-gRLt zittivHgf7iGQ`|(kd&z?tGtl4VNtIW@i4|Qx%bf-S00XYY@{#X_BU9LTmn0G@ zl6uL%GdGhot4jHnX{>M+pv!S$F6MNd+Bq_(f@$<+z2!U*Z<_d`m0EF(`iWWX;yrD} zscqKc7;k66mMERSO!Yd-lA|~7LxuH^WBX2Tr%yrj^`TPfJl6HJ=iXX*DzVt}T-MZZ z_j^jaX0fGbq?U~I73jltPE@7EWco{kJDY`di-R7Fqg^_0ip!Jgc15K))r4mkbtQCp zTN>1*DpQmstth)Qp2J!WoZMxTPQ#GO!&p=ZvLPKvu0BFEYk{&!21bT1t9!CBvAU*= znIy{yt%<$tuF+w|BMN}3S#nEiE0xq+;BOe_VxDGFm5p6Um^MmunQoj7om|Rpd5-G6 zh|I33TT;ZLbzwWAy$RhB$-V4W>}H^s187roc1rBEaW0mmDc09hgKFYf?QMM;q>__H zmnzG19SeBUIMekQ-uSXPQQOK&_Q>^g{Vk~531U)3PjeQBUmj;0`Najjx7c~e?ecjZ zkNp;_(Ih=a3=)#ycayq2^#1_3RDH^A`JPU6@H5uO`f3x-rB5VOtC_U6&Faqd+2$qv z#aaIVrjIX1nd~F|Ikk?Gk;<;iv)CzqD;|d(k2U=EtN#E^c%Efax$4)UP+yKmB}&If z{VqR!o|Y9m$C-Y!U9Rj@Uo*B5aTeg)^5?A1s`Hsv0B&k*WK z@0_1&JPdZ#q|{Y8>u;5xLQsm*F@T%e9@E(T!i2!*K9I#+50?rnfy_JEVDs>Ux*;{0}KAeka;`Z}k1Q z+(!gE%)LW!9Wnd_QpxkJrE!n!7Ftajf(<59diy1zJi=!nduX!^p$8mfKDlO0FjUrB-fhy!%Y!xJl+pl;ef?QCgg()H8#o^(+01 z`Pl2EnXl@h+H+fI+ZD{?E1YTQMQlSfX&Pk)tXq{hve8{lqb$w+vYET4kz~nC#Mc3wUAB?Lc10r>7HZ%}AXt_O zjutz}Dr=E41`-+JVPVA*Bug>_#6VpQg%ZpcC7A^7@fzt3m@^8v5VnsoyR13%24e(Q z4oxwvgK%AvzGPFut)}w*)nEA5J9lE0OSw@B4KNQ z%3|aUAqE7-A0Gh_FoaKUnWhBC>OMfYxI<$s9L*gt^&cQ&1_T=jmA?>uwj5jq#!CkX ze8Gg5CL$8US#SbKmnGRO3llN!hCshGh)Z)8<2Epz$ISpa_KBB3Hu?=@Eu;B}O!@}= zg~cv|VL-CkB3zf`7b0E016W%=V7PpRV|fi>bLN3?*O8T4c{TyxKK(k=mdA2z1bU5y^AhYAT?Voh)RW>hqCOrbcVgd=;$50# zJ)`CznT#f5k9DyM48ksB*f%ABK{Fp963isHwqdvdVgwc?$#PhPAuL1^0D-Vj01#Yk zkU^VvS+~dmZACWSlFhfGLT1NTlSwPLl1o-h-bnBIZ=|X*RkaJg{n-9r)=ZXffx+K; zquk5dq5l9HdCTvAZl6(9UF?%>@I~bh(yk7()#|CPi>$i78WgB#Wdx@`dt&AK&b?Ni z?3V+fS9Yp57<|2Y{{Ul~MK#Tal?c%BRB6kn2>J14TC#jwZ<))IsJCWXo)mOrh3a7( zOtD*YPq(nY158&QtdD{omD1b2st~I>1d<9=J>M3&1bo5OuUS& zYlzWBA!R}_+>sj@Rc}@c2AFa%lyI|0W@??pMVMKm2*?qs75agaB%;{avsu!NwLo4Y zN2sdujnImuc|Od<_F=^)7Z=renLVuzD(21^UhB^xWlnV+=O)V$SKhGy09C$Q#*Cu{ zXrzoBt)s=+_UK~a2&+X|7}ecVi?Z>)kk9L(f})XaLN9YSbsH+mq~iWnZk$GbWjXF^ zC;tE%!_xWp=vGwMBTCn$y*qPC=(CK@l$A{yzekfSxg=pue)FGPtr&b$dCGioy zM4TnTqe)7fH7d@O-i;bUmHCz_)}WQ#?dF)0ow^uQr&{(=zhgsG+;mA5rAn{ezhg#n zq?W+FLa6OdE%AALH<~J!;wq!c1zm5g}E#0a;59EpzTsQb&uFsjA0_IHx84v zT5jKZKm0H4IqIa#HFOp9{{Ze&l3$yAd{#$AsFW%CrrU9qI}SIc%Q`uEVKOVObffwI1mF%WHFWlLJ@!BLxenXRlZd??w}ibAD>B`bCqd&xVF>&{ufiDHh8%X$@3 zgmPa)3cIMoMr`RxtGnB*n<`e_N?B%Bk5Z$uy5e?eX`=$e!3}J3s_tby2ArV>t0oxq?@AIPR}gPxQ;O?Jd>vkxrJ8ZM!bU7B5xbebbvMm`Bs8$5e~0 zr=XG2)3)d0T1eho>bKYEQk06nrlTDojafpjif`R%@U@xHvDBl!i}|j(>W)r36{Mq0 z>2HBzP*X=fjVBzMvC+br*{M-AbnbH@TbazSA@>w5b2z})3oJ@zIMX?`7;93JB8eJk zl94WoRZ?bE(_Bp#BqUjcjUn_5_g*&M(om> zRMn7M`iCKKesfn5>UEH~a8M)b5y&a@pmS(hMt!w5z zDrWSog{wOit2SjS7T{e}G>S8vjHe@wRt1{S4KzGe zjH^h_qq{2Yq2f2A95-VDnWQ>K?U}g*h7}O?5z^XALoKAV19=G{C_ftg_yUeqqv!Bu zo~sNyT9W<`{Niis-~RwDtonO@{{XfIa#)z?>OxpigsL3vrEC#7aok&pUbQR1mSr2F z)jFOcr8=G?)NFTuwOO1!p6b;WXFt>R4veKm?!Du|{|TqFSYf&By+W6}Nmh=jc)73aF2{RHr7RMXI(2H{jHM@N#W_9+X7WWEx|LT+Tc1j4 z`Qc}pvWjn}W$os{(v538l{v|FD7JI9^y+SFQ=?5{sZw!-BXqARw4$!sO6}_1j-JPB z`hQsT_Z;CZ?%LeHZ5+PB+$N_O$}aJeO+F1>Zz<5!!Ww#9DMSAN6jN4`O=$Bv8$U~_ zR*e~FX;Xvmie(DaXSb*9OsN)+PS{^-od%0Qb$${{Zfh zeB;*jsL|aOB887lL;7J=S=5wHe32z2c5h^Pn!nsrmP(plXPSj4QdUQ|+kZ^Zqnn(h z%JrR)mDW8PJI_*-?Q_Xg^=guBteNLw>7TM$9?~A0N-EDZbE?$wGhfms^LIUits7mm zj}1LtdcO4K;&^qlsTXAzF2sBIZK%`vVHM<5$98mKLY#DpJ!YfUbeepzW0xqNI_Hkt z{{T;J_c^qw$E(!#!fodIGR2O@tNVhjT&cl9b9>G{M;m4RJGa|e%1(0n9g$|-_}K-; zn@?3a=G(c6oM=WFC6MM`(i+T;q@K`P3|of4u?2Ap%r^j2L5dd4d<-DOOvbo_ zxCC54#L09a9zzgUnH`3@5Ed90k|5VYg3Kc(-w{T-6agDFK!5=y!Jx_)3I)K$avScH z(jd1ZLAVoR;Rv@9#+fs`2U07E;xP)T$?`q+8xhG5V<%CtIT|V?LOGgsRCky(nTv~M zg-wpqGxsc5>o>b1wrH#E7nL-^#dA#G8++M?GOTIXx$FUo0tFxiUkXp#;>+<>v2nXh#Zk)C5*OS3%r z8eI>NqllAcdGa*1#unpU5k|�{khkytmemKJEx_YpC^hHbWF7m(l{=tK)MtLUDb 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 Camera{}; for(int I=3;I=179.0) {std::fprintf(stderr,"Invalid horizontal FOV\n");return 2;} } + else if(Arg.starts_with("--camera-m=")) + { + const std::string Value(Arg.substr(11)); + if(std::sscanf(Value.c_str(),"%lf,%lf,%lf",&Camera[0],&Camera[1],&Camera[2])!=3 + ||!std::isfinite(Camera[0])||!std::isfinite(Camera[1])||!std::isfinite(Camera[2])) + {std::fprintf(stderr,"Invalid camera position\n");return 2;} + HasCamera=true; + } else if(Arg.starts_with("--yaw-deg=")||Arg.starts_with("--pitch-deg=")) { const bool IsYaw=Arg.starts_with("--yaw-deg="); @@ -376,6 +385,7 @@ int main(int argc,char** argv) FScene Scene;std::string Error; if(!LoadScene(argv[1],Scene,Error)) {std::fprintf(stderr,"[OffscreenCapture] FAIL: %s\n",Error.c_str());return 2;} + if(HasCamera)Scene.CameraMeters=Camera; if(HasYaw)Scene.YawDegrees=Yaw; if(HasPitch)Scene.PitchDegrees=Pitch; std::printf("[OffscreenScene] voxels_per_meter=%.6f camera_m=(%.3f,%.3f,%.3f) yaw=%.2f pitch=%.2f vertices=%zu triangles=%llu\n", diff --git a/Standalone/Source/LinuxScreenSpaceGate.h b/Standalone/Source/LinuxScreenSpaceGate.h index bf5f751..10c56b0 100644 --- a/Standalone/Source/LinuxScreenSpaceGate.h +++ b/Standalone/Source/LinuxScreenSpaceGate.h @@ -22,6 +22,12 @@ namespace StandaloneScreenSpaceGate int Lod=0; std::uint64_t Samples=0,Missing=0,Floating=0; double P50=0.0,P99=0.0,Maximum=0.0; + double VerticalP50=0.0,VerticalP99=0.0,VerticalMaximum=0.0; + double MaximumNormalDistanceMeters=0.0,MaximumX=0.0,MaximumY=0.0; + double MaximumReferenceZ=0.0,MaximumDisplayedZ=0.0; + double MaximumSlopeDegrees=0.0; + std::array SlopeSamples{}; + std::array SlopeP99{},SlopeMaximum{}; bool Valid=false; }; @@ -30,9 +36,128 @@ namespace StandaloneScreenSpaceGate int CoarseLod=0; std::uint64_t Samples=0,Cracks=0,Floating=0; double P50=0.0,P99=0.0,Maximum=0.0; + double VerticalP50=0.0,VerticalP99=0.0,VerticalMaximum=0.0; + double MaximumNormalDistanceMeters=0.0,MaximumX=0.0,MaximumY=0.0; + double MaximumReferenceZ=0.0,MaximumDisplayedZ=0.0; + double MaximumSlopeDegrees=0.0; + std::array SlopeSamples{}; + std::array SlopeP99{},SlopeMaximum{}; bool Valid=false; }; + struct FSurfaceRoot + { + double ZMeters=0.0; + double SlopeX=0.0; + double SlopeY=0.0; + }; + + struct FViewBasis + { + std::array Right{}; + std::array Up{}; + }; + + struct FErrorAccumulator + { + std::vector Pixels,VerticalPixels; + std::array,4> BySlope; + double Maximum=-1.0,MaximumVertical=0.0,MaximumNormalDistanceMeters=0.0; + double MaximumX=0.0,MaximumY=0.0,MaximumSlopeDegrees=0.0; + double MaximumReferenceZ=0.0,MaximumDisplayedZ=0.0; + std::array SlopeSamples{}; + }; + + inline double Percentile(std::vector& Values,double P); + + inline FViewBasis MakeViewBasis(double YawDegrees,double PitchDegrees) + { + constexpr double Pi=3.14159265358979323846; + const double Yaw=YawDegrees*Pi/180.0,Pitch=PitchDegrees*Pi/180.0; + const std::array Forward={std::sin(Yaw)*std::cos(Pitch), + std::cos(Yaw)*std::cos(Pitch),std::sin(Pitch)}; + FViewBasis Basis; + Basis.Right={std::cos(Yaw),-std::sin(Yaw),0.0}; + Basis.Up={Basis.Right[1]*Forward[2]-Basis.Right[2]*Forward[1], + Basis.Right[2]*Forward[0]-Basis.Right[0]*Forward[2], + Basis.Right[0]*Forward[1]-Basis.Right[1]*Forward[0]}; + return Basis; + } + + inline std::size_t SlopeBin(double Degrees) + { + if(Degrees<30.0)return 0; + if(Degrees<45.0)return 1; + if(Degrees<60.0)return 2; + return 3; + } + + inline void AddError(FErrorAccumulator& Accumulator,double DeltaZMeters, + const FSurfaceRoot& Root,double PixelBudgetMeters,const FViewBasis& View, + double X,double Y) + { + constexpr double Pi=3.14159265358979323846; + const double GradientSquared=Root.SlopeX*Root.SlopeX+Root.SlopeY*Root.SlopeY; + const double NormalLength=std::sqrt(1.0+GradientSquared); + const double NormalX=-Root.SlopeX/NormalLength; + const double NormalY=-Root.SlopeY/NormalLength; + const double NormalZ=1.0/NormalLength; + const double NormalDistance=std::abs(DeltaZMeters)/NormalLength; + const double ProjectedNormal=std::hypot( + NormalX*View.Right[0]+NormalY*View.Right[1]+NormalZ*View.Right[2], + NormalX*View.Up[0]+NormalY*View.Up[1]+NormalZ*View.Up[2]); + const double ProjectedVertical=std::hypot(View.Right[2],View.Up[2]); + const double NormalPixels=NormalDistance*ProjectedNormal/PixelBudgetMeters; + const double VerticalPixels=std::abs(DeltaZMeters)*ProjectedVertical/PixelBudgetMeters; + const double SlopeDegrees=std::atan(std::sqrt(GradientSquared))*180.0/Pi; + Accumulator.Pixels.push_back(NormalPixels); + Accumulator.VerticalPixels.push_back(VerticalPixels); + const std::size_t Bin=SlopeBin(SlopeDegrees); + Accumulator.BySlope[Bin].push_back(NormalPixels); + ++Accumulator.SlopeSamples[Bin]; + if(NormalPixels>Accumulator.Maximum) + { + Accumulator.Maximum=NormalPixels; + Accumulator.MaximumVertical=VerticalPixels; + Accumulator.MaximumNormalDistanceMeters=NormalDistance; + Accumulator.MaximumX=X;Accumulator.MaximumY=Y; + Accumulator.MaximumSlopeDegrees=SlopeDegrees; + Accumulator.MaximumReferenceZ=Root.ZMeters; + Accumulator.MaximumDisplayedZ=Root.ZMeters+DeltaZMeters; + } + } + + inline void AddMissing(FErrorAccumulator& Accumulator) + { + Accumulator.Pixels.push_back(2.0); + Accumulator.VerticalPixels.push_back(2.0); + } + + inline void FinalizeError(FErrorRow& Row,FErrorAccumulator& Accumulator) + { + Row.Samples=Accumulator.Pixels.size(); + Row.P50=Percentile(Accumulator.Pixels,0.50); + Row.P99=Percentile(Accumulator.Pixels,0.99); + Row.Maximum=Accumulator.Pixels.empty()?0.0 + :*std::max_element(Accumulator.Pixels.begin(),Accumulator.Pixels.end()); + Row.VerticalP50=Percentile(Accumulator.VerticalPixels,0.50); + Row.VerticalP99=Percentile(Accumulator.VerticalPixels,0.99); + Row.VerticalMaximum=Accumulator.VerticalPixels.empty()?0.0 + :*std::max_element(Accumulator.VerticalPixels.begin(),Accumulator.VerticalPixels.end()); + Row.MaximumNormalDistanceMeters=Accumulator.MaximumNormalDistanceMeters; + Row.MaximumX=Accumulator.MaximumX;Row.MaximumY=Accumulator.MaximumY; + Row.MaximumReferenceZ=Accumulator.MaximumReferenceZ; + Row.MaximumDisplayedZ=Accumulator.MaximumDisplayedZ; + Row.MaximumSlopeDegrees=Accumulator.MaximumSlopeDegrees; + Row.SlopeSamples=Accumulator.SlopeSamples; + for(std::size_t Bin=0;Bin ExactHeightRoots(const FHeightGrid& Grid,int Ix,int Iy, + inline std::vector ExactHeightRoots(const FHeightGrid& Grid,int Ix,int Iy, const FVoxelTileKey& Patch,double VoxelMeters, const VoxelForgeCore::WorldAssemblyManifest& Assembly) { - std::vector Roots; + std::vector Roots; const double XSubvoxels=Grid.X(Ix)/VoxelMeters*256.0; const double YSubvoxels=Grid.Y(Iy)/VoxelMeters*256.0; + const auto X=static_cast(std::llround(XSubvoxels)); + const auto Y=static_cast(std::llround(YSubvoxels)); const double MinZ=static_cast(Patch.OriginVoxels().Z); const double MaxZ=MinZ+Patch.ExtentVoxels(); + constexpr std::int64_t NormalProbeSubvoxels=256; for(const auto& Plan:Assembly.plan.strates) { VoxelForgeCore::Heightfield2D Field(Assembly.recipe,Plan); if(!Field.isValid())continue; - const auto Column=Field.point(static_cast(std::llround(XSubvoxels)), - static_cast(std::llround(YSubvoxels)),0); + const auto Column=Field.point(X,Y,0); if(Column.groundHeightSubvoxels>=Column.capHeightSubvoxels)continue; - const double Ground=static_cast(Column.groundHeightSubvoxels)/(256.0)*VoxelMeters; - const double Cap=static_cast(Column.capHeightSubvoxels)/(256.0)*VoxelMeters; + const auto XMinus=Field.point(X-NormalProbeSubvoxels,Y,0); + const auto XPlus=Field.point(X+NormalProbeSubvoxels,Y,0); + const auto YMinus=Field.point(X,Y-NormalProbeSubvoxels,0); + const auto YPlus=Field.point(X,Y+NormalProbeSubvoxels,0); + const auto Slope=[&](std::int64_t Minus,std::int64_t Plus) + {return static_cast(Plus-Minus)/(2.0*NormalProbeSubvoxels);}; + const double Ground=static_cast(Column.groundHeightSubvoxels)/256.0*VoxelMeters; + const double Cap=static_cast(Column.capHeightSubvoxels)/256.0*VoxelMeters; const double PlanMinZ=static_cast(Plan.bottomChunkZ)*32.0*VoxelMeters; const double PlanMaxZ=static_cast(Plan.topChunkZ+1)*32.0*VoxelMeters; if(Ground>=MinZ*VoxelMeters-0.25*VoxelMeters &&Ground<=MaxZ*VoxelMeters+0.25*VoxelMeters &&Ground>=PlanMinZ-0.25*VoxelMeters&&Ground<=PlanMaxZ+0.25*VoxelMeters) - Roots.push_back(Ground); + Roots.push_back({Ground,Slope(XMinus.groundHeightSubvoxels, + XPlus.groundHeightSubvoxels),Slope(YMinus.groundHeightSubvoxels, + YPlus.groundHeightSubvoxels)}); if(Cap>=MinZ*VoxelMeters-0.25*VoxelMeters &&Cap<=MaxZ*VoxelMeters+0.25*VoxelMeters &&Cap>=PlanMinZ-0.25*VoxelMeters&&Cap<=PlanMaxZ+0.25*VoxelMeters) - Roots.push_back(Cap); + Roots.push_back({Cap,Slope(XMinus.capHeightSubvoxels, + XPlus.capHeightSubvoxels),Slope(YMinus.capHeightSubvoxels, + YPlus.capHeightSubvoxels)}); } - std::sort(Roots.begin(),Roots.end()); - Roots.erase(std::unique(Roots.begin(),Roots.end(),[](double A,double B) - {return std::abs(A-B)<0.01;}),Roots.end()); + std::sort(Roots.begin(),Roots.end(),[](const FSurfaceRoot& A,const FSurfaceRoot& B) + {return A.ZMeters Errors; + FErrorAccumulator Accumulator; for(int Iy=0;Iy Used(MeshGrid.At(Ix,Iy).size(),false); - for(double Root:Roots) + for(const FSurfaceRoot& Root:Roots) { std::size_t HitIndex=0;double ErrorMeters=0.0; - if(!MatchNearest(MeshGrid.At(Ix,Iy),Used,Root,HitIndex,ErrorMeters)) + if(!MatchNearest(MeshGrid.At(Ix,Iy),Used,Root.ZMeters,HitIndex,ErrorMeters)) { ++Row.Missing; - Errors.push_back(2.0); + AddMissing(Accumulator); continue; } Used[HitIndex]=true; - Errors.push_back(ErrorMeters/PixelBudgetMeters); + AddError(Accumulator,MeshGrid.At(Ix,Iy)[HitIndex]-Root.ZMeters, + Root,PixelBudgetMeters,View,MeshGrid.X(Ix),MeshGrid.Y(Iy)); ++Row.Samples; } // Heightfield2D declares ground and cap roots only. Other mesh intersections may // be valid tunnel geometry; the exact-mesh comparison checks extra pieces. } - Row.P50=Percentile(Errors,0.50);Row.P99=Percentile(Errors,0.99); - Row.Maximum=Errors.empty()?0.0:*std::max_element(Errors.begin(),Errors.end()); - Row.Samples=Errors.size(); + FinalizeError(Row,Accumulator); return Row; } inline FErrorRow CompareToReference(const FHeightGrid& MeshGrid, - const FHeightGrid& ReferenceGrid,int Lod,double PixelBudgetMeters) + const FHeightGrid& ReferenceGrid,const FVoxelTileKey& Patch,double VoxelMeters, + double PixelBudgetMeters,const VoxelForgeCore::WorldAssemblyManifest& Assembly, + const FViewBasis& View) { - FErrorRow Row;Row.Lod=Lod;Row.Valid=true; - std::vector Errors; + FErrorRow Row;Row.Lod=Patch.Level;Row.Valid=true; + FErrorAccumulator Accumulator; for(int Iy=0;Iy Used(Mesh.size(),false); - for(double ReferenceZ:Reference) + const auto Roots=ExactHeightRoots(ReferenceGrid,Ix,Iy,Patch,VoxelMeters,Assembly); + std::vector UsedReference(Reference.size(),false),UsedMesh(Mesh.size(),false); + for(std::size_t ReferenceIndex=0;ReferenceIndexZMeters-ReferenceZ)<1.0) + Root=*BestRoot; + } + AddError(Accumulator,Mesh[MeshIndex]-Reference[ReferenceIndex], + Root,PixelBudgetMeters,View,ReferenceGrid.X(Ix),ReferenceGrid.Y(Iy)); } for(std::size_t I=0;I Errors; + FErrorAccumulator Accumulator; for(int Iy=0;Iy CoarseUsed(CoarseHits.size(),false); std::vector FineUsed(FineHits.size(),false); - for(double Root:Roots) + for(const FSurfaceRoot& Root:Roots) { std::size_t CoarseIndex=0,FineIndex=0; double CoarseError=0.0,FineError=0.0; - if(!MatchNearest(CoarseHits,CoarseUsed,Root,CoarseIndex,CoarseError) - ||!MatchNearest(FineHits,FineUsed,Root,FineIndex,FineError)) + if(!MatchNearest(CoarseHits,CoarseUsed,Root.ZMeters,CoarseIndex,CoarseError) + ||!MatchNearest(FineHits,FineUsed,Root.ZMeters,FineIndex,FineError)) { - ++Row.Cracks;Errors.push_back(2.0);continue; + ++Row.Cracks;AddMissing(Accumulator);continue; } CoarseUsed[CoarseIndex]=true; FineUsed[FineIndex]=true; - Errors.push_back(std::abs(CoarseHits[CoarseIndex]-FineHits[FineIndex]) - /PixelBudgetMeters); + AddError(Accumulator,CoarseHits[CoarseIndex]-FineHits[FineIndex], + Root,PixelBudgetMeters,View,Coarse.X(Ix),Coarse.Y(Iy)); ++Row.Samples; } } - Row.P50=Percentile(Errors,0.50);Row.P99=Percentile(Errors,0.99); - Row.Maximum=Errors.empty()?0.0:*std::max_element(Errors.begin(),Errors.end()); - Row.Samples=Errors.size(); + FErrorRow Stats;Stats.Lod=Patch.Level;Stats.Valid=true; + FinalizeError(Stats,Accumulator); + Row.Samples=Stats.Samples;Row.P50=Stats.P50;Row.P99=Stats.P99;Row.Maximum=Stats.Maximum; + Row.VerticalP50=Stats.VerticalP50;Row.VerticalP99=Stats.VerticalP99; + Row.VerticalMaximum=Stats.VerticalMaximum; + Row.MaximumNormalDistanceMeters=Stats.MaximumNormalDistanceMeters; + Row.MaximumX=Stats.MaximumX;Row.MaximumY=Stats.MaximumY; + Row.MaximumReferenceZ=Stats.MaximumReferenceZ; + Row.MaximumDisplayedZ=Stats.MaximumDisplayedZ; + Row.MaximumSlopeDegrees=Stats.MaximumSlopeDegrees; + Row.SlopeSamples=Stats.SlopeSamples;Row.SlopeP99=Stats.SlopeP99; + Row.SlopeMaximum=Stats.SlopeMaximum; return Row; } @@ -352,19 +511,52 @@ namespace StandaloneScreenSpaceGate return Distances; } + inline void PrintSlopeTail(const char* Kind,int Lod,const std::array& Counts, + const std::array& P99,const std::array& Maximum) + { + constexpr const char* Labels[4]={"lt30","30to45","45to60","ge60"}; + for(std::size_t Bin=0;Bin(Counts[Bin]),P99[Bin],Maximum[Bin]); + } + + inline void PrintKnownFailureControl(double CellMeters,double DisplayDistanceMeters, + const FViewBasis& View) + { + constexpr double Slope=100.0; + const double LateralMeters=CellMeters/200.0; + const double VerticalMeters=Slope*LateralMeters; + const double NormalLength=std::sqrt(1.0+Slope*Slope); + const double NormalDistance=VerticalMeters/NormalLength; + const std::array Normal={-Slope/NormalLength,0.0,1.0/NormalLength}; + const double ProjectedNormal=std::hypot( + Normal[0]*View.Right[0]+Normal[1]*View.Right[1]+Normal[2]*View.Right[2], + Normal[0]*View.Up[0]+Normal[1]*View.Up[1]+Normal[2]*View.Up[2]); + const double PixelBudgetMeters=DisplayDistanceMeters*OnePixelRadians; + const double VerticalPixels=VerticalMeters/PixelBudgetMeters; + const double NormalPixels=NormalDistance*ProjectedNormal/PixelBudgetMeters; + const bool Passed=VerticalPixels>4.0&&NormalPixels<1.0; + std::printf("[ScreenSpaceKnownFailureControl] case=steep-plane-lateral-offset slope_deg=%.4f cell_m=%.6f lateral_m=%.6f cell_fraction=%.6f vertical_error_m=%.6f vertical_height_px=%.4f closest_normal_m=%.6f projected_normal_px=%.4f status=%s\n", + std::atan(Slope)*180.0/3.14159265358979323846,CellMeters,LateralMeters, + LateralMeters/CellMeters,VerticalMeters,VerticalPixels,NormalDistance,NormalPixels, + Passed?"EXPECTED_VERTICAL_FALSE_POSITIVE":"CONTROL_FAILED"); + } + inline bool Run(const std::array,9>& SampleTiles, FStandaloneStreamingGenerator& Streamer, const VoxelForgeCore::WorldAssemblyManifest& Assembly, const std::array& Distances,std::uint32_t VoxelSizeMicrons,int MaxLod, - const std::array& PixelBudgetsSubvoxels) + const std::array& PixelBudgetsSubvoxels, + double CameraYawDegrees,double CameraPitchDegrees) { const double VoxelMeters=static_cast(VoxelSizeMicrons)/1000000.0; + const FViewBasis View=MakeViewBasis(CameraYawDegrees,CameraPitchDegrees); std::array Budgets{}; std::array ErrorRows{}; std::array PopRows{}; bool GatePass=true; - std::printf("[ScreenSpaceGate] angular_pixel=%.9f rad (90deg/2560) lod>=3_limit=1.0px sampling=65x65 skirts=excluded metric=vertical_height_error field_reference=direct-Heightfield2D-roots\n", - OnePixelRadians); + std::printf("[ScreenSpaceGate] angular_pixel=%.9f rad (horizontal_fov=90deg,width=2560) lod>=3_limit=1.0px sampling=65x65 skirts=excluded metric=local_surface_normal_projected field_reference=direct-Heightfield2D-roots normal_probe_vox=1 view_yaw_deg=%.3f view_pitch_deg=%.3f\n", + OnePixelRadians,CameraYawDegrees,CameraPitchDegrees); for(int Lod=0;Lod<=MaxLod&&Lod<9;++Lod) { Budgets[Lod]=Distances[Lod]*OnePixelRadians; @@ -372,6 +564,8 @@ namespace StandaloneScreenSpaceGate Lod,Distances[Lod],Budgets[Lod],Budgets[Lod]*100.0, static_cast(PixelBudgetsSubvoxels[Lod]),Lod<3?"exact-2cm":"1px"); } + const double LOD3CellMeters=static_cast(CHUNK_SIZE*(1<<3))*VoxelMeters/64.0; + PrintKnownFailureControl(LOD3CellMeters,Distances[3],View); VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true,0); for(int Lod=3;Lod<=MaxLod&&Lod<9;++Lod) { @@ -389,32 +583,43 @@ namespace StandaloneScreenSpaceGate const FStandaloneCpuTile* ExactPointer=&ExactTile; const std::span ExactSpan(&ExactPointer,1); const FHeightGrid ExactGrid=Rasterize(ExactSpan,ExactTile.Key,VoxelMeters); - ErrorRows[Lod]=CompareToReference(CoarseGrid,ExactGrid,Lod,Budgets[Lod]); + ErrorRows[Lod]=CompareToReference(CoarseGrid,ExactGrid,CoarseTile.Key,VoxelMeters, + Budgets[Lod],Assembly,View); const FErrorRow& Error=ErrorRows[Lod]; const bool LodPass=Error.Valid&&Error.Missing==0&&Error.Floating==0 &&Error.Samples>=16&&Error.Maximum<=1.0; GatePass=GatePass&&LodPass; - std::printf("[ScreenSpaceGateLOD] lod=%d key=(%d,%d,%d) samples=%llu p50_px=%.4f p99_px=%.4f max_px=%.4f missing=%llu floating=%llu status=%s\n", + std::printf("[ScreenSpaceGateLOD] lod=%d key=(%d,%d,%d) samples=%llu normal_px_p50=%.4f p99=%.4f max=%.4f vertical_px_p50=%.4f p99=%.4f max=%.4f max_normal_m=%.6f max_xy_m=(%.3f,%.3f) max_slope_deg=%.2f missing=%llu floating=%llu status=%s\n", Lod,CoarseTile.Key.Coord.X,CoarseTile.Key.Coord.Y,CoarseTile.Key.Coord.Z, static_cast(Error.Samples),Error.P50,Error.P99,Error.Maximum, + Error.VerticalP50,Error.VerticalP99,Error.VerticalMaximum, + Error.MaximumNormalDistanceMeters,Error.MaximumX,Error.MaximumY, + Error.MaximumSlopeDegrees, static_cast(Error.Missing),static_cast(Error.Floating), LodPass?"PASS":"REJECT"); + PrintSlopeTail("same_lod",Lod,Error.SlopeSamples,Error.SlopeP99,Error.SlopeMaximum); const FErrorRow FieldError=CompareToExact(CoarseGrid,CoarseTile.Key,VoxelMeters, - Budgets[Lod],Assembly); + Budgets[Lod],Assembly,View); const bool FieldPass=FieldError.Valid&&FieldError.Missing==0&&FieldError.Floating==0 &&FieldError.Samples>=16&&FieldError.Maximum<=1.0; GatePass=GatePass&&FieldPass; - std::printf("[ScreenSpaceGateFieldLOD] lod=%d samples=%llu p50_px=%.4f p99_px=%.4f max_px=%.4f missing=%llu floating=%llu status=%s\n", + std::printf("[ScreenSpaceGateFieldLOD] lod=%d samples=%llu normal_px_p50=%.4f p99=%.4f max=%.4f vertical_px_p50=%.4f p99=%.4f max=%.4f max_normal_m=%.6f max_xyz_m=(%.3f,%.3f,%.3f) max_slope_deg=%.2f missing=%llu floating=%llu status=%s\n", Lod,static_cast(FieldError.Samples),FieldError.P50, - FieldError.P99,FieldError.Maximum,static_cast(FieldError.Missing), + FieldError.P99,FieldError.Maximum,FieldError.VerticalP50,FieldError.VerticalP99, + FieldError.VerticalMaximum,FieldError.MaximumNormalDistanceMeters, + FieldError.MaximumX,FieldError.MaximumY,FieldError.MaximumDisplayedZ, + FieldError.MaximumSlopeDegrees, + static_cast(FieldError.Missing), static_cast(FieldError.Floating),FieldPass?"PASS":"REJECT"); + PrintSlopeTail("field",Lod,FieldError.SlopeSamples,FieldError.SlopeP99, + FieldError.SlopeMaximum); bool FaultControlsPass=true; const FHeightGrid MissingGrid(ExactGrid.Side,ExactGrid.MinimumX,ExactGrid.MaximumX, ExactGrid.MinimumY,ExactGrid.MaximumY); - const FErrorRow MissingError=CompareToReference(MissingGrid,ExactGrid,Lod, - Budgets[Lod]); + const FErrorRow MissingError=CompareToReference(MissingGrid,ExactGrid, + CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View); const bool MissingRejected=MissingError.Missing>0&&MissingError.Maximum>1.0; FaultControlsPass=FaultControlsPass&&MissingRejected; std::printf("[ScreenSpaceFaultInjection] lod=%d case=missing-displayed-surface missing=%llu status=%s\n", @@ -435,8 +640,8 @@ namespace StandaloneScreenSpaceGate const FStandaloneCpuTile* FloatingPointer=&FloatingTile; const std::span FloatingSpan(&FloatingPointer,1); const FHeightGrid FloatingGrid=Rasterize(FloatingSpan,CoarseTile.Key,VoxelMeters); - const FErrorRow FloatingError=CompareToReference(FloatingGrid,ExactGrid,Lod, - Budgets[Lod]); + const FErrorRow FloatingError=CompareToReference(FloatingGrid,ExactGrid, + CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View); const bool FloatingRejected=FloatingError.Floating>0||FloatingError.Maximum>1.0; FaultControlsPass=FaultControlsPass&&FloatingRejected; std::printf("[ScreenSpaceFaultInjection] lod=%d case=2m-floating-surface floating=%llu max_px=%.4f status=%s\n", @@ -461,20 +666,24 @@ namespace StandaloneScreenSpaceGate FinePointers.data(),FinePointers.size()); const FHeightGrid FineGrid=Rasterize(FineSpan,CoarseTile.Key,VoxelMeters); PopRows[Lod]=ComparePop(CoarseGrid,FineGrid,CoarseTile.Key,VoxelMeters, - Budgets[Lod],Assembly); + Budgets[Lod],Assembly,View); const FPopRow& Pop=PopRows[Lod]; const bool PopPass=Pop.Valid&&Pop.Cracks==0&&Pop.Floating==0&&Pop.Maximum<=1.0; GatePass=GatePass&&PopPass; - std::printf("[ScreenSpaceSwap] lod=%d_to_%d samples=%llu p50_px=%.4f p99_px=%.4f max_px=%.4f cracks=%llu floating=%llu status=%s\n", + std::printf("[ScreenSpaceSwap] lod=%d_to_%d samples=%llu normal_px_p50=%.4f p99=%.4f max=%.4f vertical_px_p50=%.4f p99=%.4f max=%.4f max_normal_m=%.6f max_xyz_m=(%.3f,%.3f,%.3f) max_slope_deg=%.2f cracks=%llu floating=%llu status=%s\n", Lod,Lod-1,static_cast(Pop.Samples), - Pop.P50,Pop.P99,Pop.Maximum,static_cast(Pop.Cracks), + Pop.P50,Pop.P99,Pop.Maximum,Pop.VerticalP50,Pop.VerticalP99,Pop.VerticalMaximum, + Pop.MaximumNormalDistanceMeters,Pop.MaximumX,Pop.MaximumY, + Pop.MaximumDisplayedZ, + Pop.MaximumSlopeDegrees,static_cast(Pop.Cracks), static_cast(Pop.Floating), PopPass?"PASS":"REJECT"); + PrintSlopeTail("swap",Lod,Pop.SlopeSamples,Pop.SlopeP99,Pop.SlopeMaximum); const FHeightGrid MissingFineGrid(CoarseGrid.Side,CoarseGrid.MinimumX, CoarseGrid.MaximumX,CoarseGrid.MinimumY,CoarseGrid.MaximumY); const FPopRow MissingFineError=ComparePop(CoarseGrid,MissingFineGrid, - CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly); + CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View); const bool CrackRejected=MissingFineError.Cracks>0&&MissingFineError.Maximum>1.0; FaultControlsPass=FaultControlsPass&&CrackRejected; GatePass=GatePass&&FaultControlsPass; @@ -490,8 +699,8 @@ namespace StandaloneScreenSpaceGate const FStandaloneCpuTile* BadPointer=&Bad; const std::span BadSpan(&BadPointer,1); const FHeightGrid BadGrid=Rasterize(BadSpan,Bad.Key,VoxelMeters); - const FErrorRow BadError=CompareToReference(BadGrid,ExactGrid,Lod, - Budgets[Lod]); + const FErrorRow BadError=CompareToReference(BadGrid,ExactGrid, + CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly,View); const bool Rejected=BadError.Missing>0||BadError.Floating>0 ||BadError.Samples<16||BadError.Maximum>1.0; NegativeControlsPass=NegativeControlsPass&&Rejected; diff --git a/Standalone/Source/StreamBenchmarkHeadless.cpp b/Standalone/Source/StreamBenchmarkHeadless.cpp index 8e5a310..ad0ad16 100644 --- a/Standalone/Source/StreamBenchmarkHeadless.cpp +++ b/Standalone/Source/StreamBenchmarkHeadless.cpp @@ -38,7 +38,6 @@ namespace bool ForceGenerate=false; bool MeshCoverage=false; bool ScreenSpaceGate=false; - bool VisualDetailCutoff=false; bool HasSeedOverride=false; int SeedOverride=0; bool HasRenderDistanceOverride=false; @@ -98,7 +97,6 @@ namespace " --auto-surface-camera (place the 27-tile cube on this seed's surface)\n" " --mesh-coverage (report non-empty tiles and indexed triangles)\n" " --screen-space-gate (measure display error and historical negative controls)\n" - " --visual-detail-cutoff (enable the declared far visual octave cutoff)\n" " --render-distance-chunks=N (0 disables the extra far ring)\n" " --tls-probe=N (Linux worker TLS/stack measurement; N=0..12)\n" " --tls-probe-touch=none|surface-world|floating-islands (first-use probe)\n" @@ -114,7 +112,6 @@ namespace else if(arg=="--force-generate")options.ForceGenerate=true; else if(arg=="--mesh-coverage")options.MeshCoverage=true; else if(arg=="--screen-space-gate")options.ScreenSpaceGate=true; - else if(arg=="--visual-detail-cutoff")options.VisualDetailCutoff=true; else if(arg=="--auto-surface-camera")options.AutoSurfaceCamera=true; else if(arg.starts_with("--recipe=")) { @@ -317,5 +314,5 @@ int main(int argc,char** argv) scheduler,options.StreamHash,false,false,false,false,coldStart, options.ProfileColumnOverlap,options.FarHorizonLOD8InteriorStepVoxels, options.MeshCoverage,options.MeshExportPath,options.CameraYawDegrees, - options.CameraPitchDegrees,options.ScreenSpaceGate,options.VisualDetailCutoff); + options.CameraPitchDegrees,options.ScreenSpaceGate); } diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h index 1f3fcea..8388ea8 100644 --- a/Standalone/Source/StreamBenchmarkPipeline.h +++ b/Standalone/Source/StreamBenchmarkPipeline.h @@ -137,22 +137,17 @@ namespace StandaloneStreamBenchmark std::chrono::steady_clock::time_point ColdStart,bool bProfileColumnOverlap, int32 FarHorizonLOD8InteriorStepVoxels,bool bReportMeshCoverage=false, const std::filesystem::path& MeshExportPath={},float CameraYawDegrees=0.0f, - float CameraPitchDegrees=0.0f,bool bScreenSpaceGate=false, - bool bApplyVisualDetailCutoff=false) + float CameraPitchDegrees=0.0f,bool bScreenSpaceGate=false) { using Clock=std::chrono::steady_clock; FStandaloneOwnerWorld& World=*OwnerWorld; const VoxelForgeCore::WorldAssemblyManifest* Assembly=CoreSnapshot.worldAssembly(); std::array ClosestDisplayDistances{}; std::array PixelBudgetsSubvoxels{}; - std::array AppliedPixelBudgetsSubvoxels{}; - if(bScreenSpaceGate||bApplyVisualDetailCutoff) + if(bScreenSpaceGate) { - ClosestDisplayDistances=bScreenSpaceGate - ?StandaloneScreenSpaceGate::ClosestDisplayedDistancesMeters( - World,CameraPositionVoxels,World.CoreVoxelSizeMicrons) - :StandaloneScreenSpaceGate::ClipmapClosestDisplayedDistancesMeters( - World,World.CoreVoxelSizeMicrons); + ClosestDisplayDistances=StandaloneScreenSpaceGate::ClosestDisplayedDistancesMeters( + World,CameraPositionVoxels,World.CoreVoxelSizeMicrons); const double VoxelMeters=static_cast(World.CoreVoxelSizeMicrons)/1000000.0; for(int32 Lod=3;Lod<=World.MaxClipLevel&&Lod<9;++Lod) PixelBudgetsSubvoxels[static_cast(Lod)]=static_cast(std::floor( @@ -160,67 +155,6 @@ namespace StandaloneStreamBenchmark *StandaloneScreenSpaceGate::OnePixelRadians/VoxelMeters *VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)); } - AppliedPixelBudgetsSubvoxels=PixelBudgetsSubvoxels; - if(Assembly&&(bScreenSpaceGate||bApplyVisualDetailCutoff)) - { - for(int32 Lod=3;Lod<=World.MaxClipLevel&&Lod<9;++Lod) - { - const std::int32_t Cells=FMath::Max(1,World.CoarseTileCells); - const std::int32_t Step=FMath::Max(1,(CHUNK_SIZE<(Lod)]; - Probe.sampleStepSubvoxels=static_cast(Step) - *VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel; - std::uint64_t TermCount=0,EligibleTermCount=0,PotentialSkippedOctaves=0; - for(const auto& Plan:Assembly->plan.strates) - { - const VoxelForgeCore::Heightfield2D Field(Assembly->recipe,Plan); - for(const auto& Term:Field.visualDetailContract().terms) - { - ++TermCount; - const auto Result=VoxelForgeCore::FindVisualDetailTailCutoffV1(Term,Probe); - if(Result.firstSkippedOctave==0)continue; - ++EligibleTermCount; - PotentialSkippedOctaves+=Term.octaveCount-Result.firstSkippedOctave; - } - } - std::printf("[WorldAssemblerVisualDetailCutoffPlan] lod=%d terms=%llu eligible_terms=%llu potential_skipped_octaves=%llu per_call_budget_divisor=%u sample_step_vox=%d status=%s\n", - Lod,static_cast(TermCount), - static_cast(EligibleTermCount), - static_cast(PotentialSkippedOctaves), - Probe.callBudgetDivisor,Step, - EligibleTermCount==0?"exact-no-eligible-tail":"candidate"); - if(bApplyVisualDetailCutoff&&EligibleTermCount==0) - AppliedPixelBudgetsSubvoxels[static_cast(Lod)]=0; - } - } - if(bScreenSpaceGate&&Assembly) - { - for(const auto& Plan:Assembly->plan.strates) - { - const VoxelForgeCore::Heightfield2D Field(Assembly->recipe,Plan); - for(const auto& Term:Field.visualDetailContract().terms) - for(std::uint32_t Octave=0;Octave(1ull<(Term.maximumDisplacementSubvoxels) - *Term.maximumNoiseAbsQ16/65536.0L - /static_cast(1ull<(Term.stableId),Octave, - static_cast(Wavelength)/256.0,static_cast(Amplitude)); - } - } - for(const auto& Island:Assembly->floatingIslands) - { - const VoxelForgeCore::FloatingIslandFieldV1 Field( - Island.manifest.recipe,Island.manifest.plan); - const auto Contract=Field.visualDetailContract(); - std::printf("[WorldAssemblerVisualDetailContract] recipe=floating-islands strate=%d declared_terms=%zu exact_path=%d\n", - Island.strateIndex,Contract.terms.size(),Contract.terms.empty()?1:0); - } - } const bool bRoundTunnels=World.Generator->bStandaloneResearchRoundTunnels; const bool bEnhancedTunnelWalls=World.Generator->bStandaloneResearchEnhancedWalls; VoxelForgeCore::World Shape(CoreSnapshot); @@ -309,12 +243,7 @@ namespace StandaloneStreamBenchmark FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel, bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap, FarHorizonLOD8InteriorStepVoxels); - if(bScreenSpaceGate||bApplyVisualDetailCutoff) - { - Streamer.SetVisualDetailCutoffBudgets(AppliedPixelBudgetsSubvoxels, - bApplyVisualDetailCutoff); - Streamer.SetVisualDetailProfilingEnabled(bScreenSpaceGate); - } + if(bScreenSpaceGate)Streamer.SetMesherStageProfilingEnabled(true); const auto StreamStart=Clock::now(); const FStandaloneStreamingUpdate Update=Streamer.StartWithDesired(Desired); if(Update.DesiredTiles!=static_cast(Desired.Tiles.size())) @@ -370,14 +299,11 @@ namespace StandaloneStreamBenchmark std::array ColumnCaptureCompleteByLOD{}; ColumnCaptureCompleteByLOD.fill(true); std::array WorkerSecondsByLOD{}; - std::array,9> DetailOctaveEvaluationsByLOD{}; - std::array,9> DetailOctaveEligibleByLOD{}; - std::array,9> DetailOctaveSkipsByLOD{}; - std::array,9> DetailOctaveMillisecondsByLOD{}; - std::array,9> DetailOctaveEligibleMillisecondsByLOD{}; std::array ColumnEvalMsByLOD{},MarchingExclusiveMsByLOD{}; std::array GroundRefineExclusiveMsByLOD{},CapRefineExclusiveMsByLOD{}; std::array MeshAssemblyMsByLOD{}; + std::array SkirtMsByLOD{}; + std::array SkirtTrianglesByLOD{}; std::array TileCountByLOD{}; for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level>=0&&Tile.Level(ExpectedTilesByLOD.size())) @@ -425,19 +351,8 @@ namespace StandaloneStreamBenchmark GroundRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.groundRefinementExclusiveMilliseconds; CapRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.capRefinementExclusiveMilliseconds; MeshAssemblyMsByLOD[LOD]+=Tile.CoreDiagnostics.meshAssemblyMilliseconds; - for(std::size_t Octave=0;Octave<8;++Octave) - { - DetailOctaveEvaluationsByLOD[LOD][Octave]+= - Tile.CoreDiagnostics.visualDetailOctaveEvaluations[Octave]; - DetailOctaveEligibleByLOD[LOD][Octave]+= - Tile.CoreDiagnostics.visualDetailOctaveBudgetEligible[Octave]; - DetailOctaveSkipsByLOD[LOD][Octave]+= - Tile.CoreDiagnostics.visualDetailOctaveSkips[Octave]; - DetailOctaveMillisecondsByLOD[LOD][Octave]+= - Tile.CoreDiagnostics.visualDetailOctaveMilliseconds[Octave]; - DetailOctaveEligibleMillisecondsByLOD[LOD][Octave]+= - Tile.CoreDiagnostics.visualDetailOctaveEligibleMilliseconds[Octave]; - } + SkirtMsByLOD[LOD]+=Tile.CoreDiagnostics.marchingCubesSkirtMilliseconds; + SkirtTrianglesByLOD[LOD]+=Tile.SkirtTriangleCount; if(bScreenSpaceGate&&Assembly&&Tile.Key.Level>=3&&Tile.Key.Level<9 &&!Tile.Vertices.empty() &&StandaloneScreenSpaceGate::TileHasNativeSurface(Tile.Key,*Assembly, @@ -541,7 +456,8 @@ namespace StandaloneStreamBenchmark if(Assembly) bScreenSpaceGatePassing=StandaloneScreenSpaceGate::Run(GateSampleTiles, Streamer,*Assembly,ClosestDisplayDistances,World.CoreVoxelSizeMicrons, - World.MaxClipLevel,PixelBudgetsSubvoxels); + World.MaxClipLevel,PixelBudgetsSubvoxels,CameraYawDegrees, + CameraPitchDegrees); else {std::fprintf(stderr,"[ScreenSpaceGateResult] no-world-assembly status=REJECT\n");bScreenSpaceGatePassing=false;} } @@ -576,19 +492,12 @@ namespace StandaloneStreamBenchmark } for(std::size_t LOD=0;LOD(TileCountByLOD[LOD]),WorkerSecondsByLOD[LOD], ColumnEvalMsByLOD[LOD],MarchingExclusiveMsByLOD[LOD], - GroundRefineExclusiveMsByLOD[LOD],CapRefineExclusiveMsByLOD[LOD],MeshAssemblyMsByLOD[LOD]); - if(bScreenSpaceGate) - for(std::size_t Octave=0;Octave<8;++Octave) - std::printf("[WorldAssemblerLODDetailOctave] lod=%zu octave=%zu evaluated=%llu budget_eligible=%llu skipped=%llu eval_ms=%.6f eligible_ms=%.6f\n", - LOD,Octave, - static_cast(DetailOctaveEvaluationsByLOD[LOD][Octave]), - static_cast(DetailOctaveEligibleByLOD[LOD][Octave]), - static_cast(DetailOctaveSkipsByLOD[LOD][Octave]), - DetailOctaveMillisecondsByLOD[LOD][Octave], - DetailOctaveEligibleMillisecondsByLOD[LOD][Octave]); + GroundRefineExclusiveMsByLOD[LOD],CapRefineExclusiveMsByLOD[LOD], + SkirtMsByLOD[LOD],static_cast(SkirtTrianglesByLOD[LOD]), + MeshAssemblyMsByLOD[LOD]); } const double NeighborhoodSeconds=std::chrono::duration(NeighborhoodReady-ColdStart).count(); const double NeighborhoodMeshSeconds=std::chrono::duration(NeighborhoodReady-ShapeReady).count(); diff --git a/Standalone/Source/StreamingWorld.h b/Standalone/Source/StreamingWorld.h index 1f5467c..0391016 100644 --- a/Standalone/Source/StreamingWorld.h +++ b/Standalone/Source/StreamingWorld.h @@ -646,16 +646,14 @@ public: int32 GetWorkerCount()const{return WorkerCount;} uint64 GetCacheHits()const{return CacheHitCount.load(std::memory_order_relaxed);} - void SetVisualDetailCutoffBudgets(const std::array& Budgets,bool Apply) - {VisualDetailBudgetSubvoxels=Budgets;bEnableVisualDetailCutoff=true;bApplyVisualDetailCutoff=Apply;} - void SetVisualDetailProfilingEnabled(bool Enabled) - {bProfileVisualDetailOctaves=Enabled;} + void SetMesherStageProfilingEnabled(bool Enabled) + {bProfileMesherStages=Enabled;} FStandaloneCpuTile GenerateDiagnostic(const FVoxelTileKey& Key, std::uint32_t GlobalOctaveDrop=0,std::uint32_t CellStride=1, - bool ForceExactVisualDetail=false) + bool ForceGenerate=false) { FStandaloneTileJob Job;Job.Key=Key;Job.StartedAt=FClock::now();Job.QueuedAt=Job.StartedAt; - return Generate(Job,GlobalOctaveDrop,CellStride,0,ForceExactVisualDetail); + return Generate(Job,GlobalOctaveDrop,CellStride,ForceGenerate); } private: @@ -775,13 +773,12 @@ private: } FStandaloneCpuTile Generate(const FStandaloneTileJob& Job, std::uint32_t DiagnosticGlobalOctaveDrop=0,std::uint32_t DiagnosticCellStride=1, - std::int64_t DiagnosticVisualBudgetSubvoxels=0, - bool ForceExactVisualDetail=false) + bool ForceGenerate=false) { const auto StartTime=Job.StartedAt; FStandaloneCpuTile Out;Out.Key=Job.Key;Out.RequestedCrossing=Job.RequestedCrossing; Out.QueuedAt=Job.QueuedAt;Out.StartedAt=Job.StartedAt; - if(MeshCache&&!ForceExactVisualDetail) + if(MeshCache&&!ForceGenerate) { if(const FStandaloneCpuTile* Cached=MeshCache->Find(Job.Key)) { @@ -820,19 +817,9 @@ private: Options.skipMaterialGroups=bSkipMaterialGroups;Options.skipUv=bSkipUV;Options.skipColors=bSkipColors; Options.captureColumnCoordinates=bCaptureColumnCoordinates; Options.lod8HorizonInteriorStepVoxels=Lod8HorizonInteriorStepVoxels; - Options.visualDetailPixelBudgetSubvoxels=DiagnosticVisualBudgetSubvoxels; - if(DiagnosticVisualBudgetSubvoxels==0&&bEnableVisualDetailCutoff - &&Level>=3&&Level(VisualDetailBudgetSubvoxels.size())) - Options.visualDetailPixelBudgetSubvoxels= - VisualDetailBudgetSubvoxels[static_cast(Level)]; - Options.visualDetailSampleStepVoxels=FMath::Max(1,Extent/Cells); - Options.profileVisualDetailOctaves=bProfileVisualDetailOctaves&&!ForceExactVisualDetail; - Options.visualDetailCutoffEnabled=bApplyVisualDetailCutoff - &&!ForceExactVisualDetail&&Level>=3 - &&Options.visualDetailPixelBudgetSubvoxels>0; - Options.visualDetailContractVersion=Options.visualDetailCutoffEnabled - ?VoxelForgeCore::CurrentVisualDetailCutoffContractVersion:0; + Options.profileMesherStages=bProfileMesherStages&&!ForceGenerate; Options.diagnosticGlobalOctaveDrop=DiagnosticGlobalOctaveDrop; + Options.diagnosticCellStride=std::max(1,DiagnosticCellStride); Options.diagnosticColumnInterpolationStride= std::max(1,DiagnosticCellStride); std::string Error; @@ -1007,10 +994,7 @@ private: bool bSkipColors=false; bool bCaptureColumnCoordinates=false; int32 Lod8HorizonInteriorStepVoxels=0; - std::array VisualDetailBudgetSubvoxels{}; - bool bEnableVisualDetailCutoff=false; - bool bApplyVisualDetailCutoff=false; - bool bProfileVisualDetailOctaves=false; + bool bProfileMesherStages=false; std::atomic CacheHitCount{0}; mutable std::mutex Mutex; std::condition_variable Wake; diff --git a/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp b/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp index 936ef02..30b7a44 100644 --- a/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp +++ b/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp @@ -1768,6 +1768,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, const int32 GroundSurfaceTriangleCount=GroundTris.Num()/3; const int32 CapSurfaceTriangleCount=CapTris.Num()/3; TArray GroundSkirtFaceMasks,CapSkirtFaceMasks; + const uint64 SkirtStageStartCycles=OutStageTimings?FPlatformTime::Cycles64():0; if (bGenerateSkirts && (GroundTris.Num() + CapTris.Num()) > 0) { const float ExtentCm = (float)(CellsPerAxis * Step) * GeometryVoxelSize; @@ -2007,6 +2008,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, EmitSkirts(GroundTris,GroundSkirtFaceMasks); EmitSkirts(CapTris,CapSkirtFaceMasks); } + if(OutStageTimings) + OutStageTimings->SkirtMilliseconds=static_cast( + FPlatformTime::Cycles64()-SkirtStageStartCycles) + *FPlatformTime::GetSecondsPerCycle64()*1000.0; // F17 — concatène sol PUIS sky-cap : un run d'indices contigu par polygroup (voir // FVoxelMeshData::NumCeilingTriangles). Mêmes triangles qu'avant, seul l'ordre change. @@ -2059,6 +2064,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, AuditInterpolationCycles,AuditInterpolationSamples,SurfaceCellCalls); OutStageTimings->VertexDedupStreamMilliseconds=ScaleSampledCycles( AuditDedupStreamCycles,AuditDedupStreamSamples,SurfaceCellCalls); + OutStageTimings->SkirtMilliseconds=std::max(0.0, + OutStageTimings->SkirtMilliseconds); const double TotalMilliseconds=static_cast( FPlatformTime::Cycles64()-StageMesherStartCycles)*CycleToMs; OutStageTimings->OtherMilliseconds=FMath::Max(0.0,TotalMilliseconds @@ -2066,7 +2073,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels, -OutStageTimings->CellClassificationMilliseconds -OutStageTimings->GradientNormalsMilliseconds -OutStageTimings->EdgeInterpolationMilliseconds - -OutStageTimings->VertexDedupStreamMilliseconds); + -OutStageTimings->VertexDedupStreamMilliseconds + -OutStageTimings->SkirtMilliseconds); } return MeshData; diff --git a/Standalone/Source/VoxelForge/Public/VoxelMarchingCubesMesher.h b/Standalone/Source/VoxelForge/Public/VoxelMarchingCubesMesher.h index 056dcac..1fb81d6 100644 --- a/Standalone/Source/VoxelForge/Public/VoxelMarchingCubesMesher.h +++ b/Standalone/Source/VoxelForge/Public/VoxelMarchingCubesMesher.h @@ -50,6 +50,7 @@ struct FVoxelMarchingCubesStageTimings double GradientNormalsMilliseconds = 0.0; double EdgeInterpolationMilliseconds = 0.0; double VertexDedupStreamMilliseconds = 0.0; + double SkirtMilliseconds = 0.0; double OtherMilliseconds = 0.0; uint64 GradientFallbackDensityCalls = 0; uint64 EdgeMidpointDensityCalls = 0;