diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6abef751..104cc1fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -141,7 +141,7 @@ jobs: "(do (require '[eacl.build.release :as release] :reload) (release/build-install-smoke - {:version \"8.0.0-SNAPSHOT\"}))" + {:version \"9.0.0-SNAPSHOT\"}))" - name: Run DataScript ClojureScript tests run: | diff --git a/README.md b/README.md index 5d1009b3..351fffd9 100644 --- a/README.md +++ b/README.md @@ -135,8 +135,7 @@ This README is too long & too technical, so I am working to simplify it and brea > [!WARNING] > EACL is used in production, but under active development. -> EACL is [available on Clojars](https://clojars.org/dev.eacl/). Use the `8.0.0-SNAPSHOT`. -> An official v8.0.0 release should be available by end-August 2026. +> This branch targets `9.0.0-SNAPSHOT`. Build it locally until the coordinated release is published; see [Clojars](https://clojars.org/dev.eacl/) for published versions. ## Real-Time UI Maintenance @@ -653,19 +652,19 @@ EACL supports multiple backends. Each adapter will bring in the shared EACL engi ```clojure ;; Datomic Pro -{:deps {dev.eacl/eacl-datomic {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datomic {:mvn/version "9.0.0-SNAPSHOT"}}} ;; Datahike -{:deps {dev.eacl/eacl-datahike {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datahike {:mvn/version "9.0.0-SNAPSHOT"}}} ;; DataScript -{:deps {dev.eacl/eacl-datascript {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datascript {:mvn/version "9.0.0-SNAPSHOT"}}} ;; Datalevin (coordinate reserved; publication remains gated) -{:deps {dev.eacl/eacl-datalevin {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datalevin {:mvn/version "9.0.0-SNAPSHOT"}}} ;; Core-only consumers and backend authors (you typically won't need this) -{:deps {dev.eacl/eacl {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl {:mvn/version "9.0.0-SNAPSHOT"}}} ``` ### Development from source @@ -987,7 +986,7 @@ order. Add the Datomic adapter dependency to your `deps.edn` file: ```clojure -{:deps {dev.eacl/eacl-datomic {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datomic {:mvn/version "9.0.0-SNAPSHOT"}}} ``` ```clojure @@ -1086,7 +1085,7 @@ Add the Datomic adapter dependency to your `deps.edn` file: For Clojure/JVM applications backed by Datahike, add the Datahike adapter dependency to your `deps.edn` file: ```clojure -{:deps {dev.eacl/eacl-datahike {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datahike {:mvn/version "9.0.0-SNAPSHOT"}}} ``` ```clojure @@ -1140,7 +1139,7 @@ commit records. For server-side or browser demos, use the DataScript adapter: ```clojure -{:deps {dev.eacl/eacl-datascript {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datascript {:mvn/version "9.0.0-SNAPSHOT"}}} ``` ```clojure @@ -1917,9 +1916,10 @@ but it is not a byte-for-byte or operational clone: sets unless your application explicitly sorts them; never compare EACL and SpiceDB page membership or cursor bytes. - EACL cursors bind the selected native revision and its dependency/order - proof. A cursor walk stays on that exact snapshot. If the backend cannot - reconstruct it, EACL fails closed. A relevant write does not silently change - page membership midway through a cursor walk. + proof. A cursor walk stays on that database snapshot. Qualified client-targeted + cursors capture fresh time and require restart when their temporal certificate + ends; explicit snapshots pin historical time. Neither mode silently rebases + its page boundary. Unavailable native history fails closed. - Omitted consistency means `:minimize-latency`. EACL selects the current immutable database value visible to the local backend connection. SpiceDB may use an optimized cached revision, so freshness can differ. Use each backend's @@ -1927,12 +1927,14 @@ but it is not a byte-for-byte or operational clone: distinction matters; tokens and cursors are backend-local. - EACL provides `count-resources`, `count-subjects`, a controllable EACL result cache, and `delete-object!`, which removes both stored Relationship halves. - Datomic commits high-degree deletion in batches of 1,000; Datahike and - DataScript use one atomic transaction. These do not have direct SpiceDB API - equivalents. -- EACL currently supports a smaller schema subset: unions, intersections, - exclusions, and its documented arrow forms, but not caveats, wildcard - subjects, expiration, or subject relations. + Qualified deletion uses bounded native transactions; each transaction removes + both endpoint values and their owned qualifier together. These operations do + not have direct SpiceDB API equivalents. +- V9 supports [Caveats and expiring Relationships](docs/caveats.md), including + conditional results and an exclusive UTC-millisecond expiry. Its bounded CEL + profile is a subset of SpiceDB's expression language; wildcard subjects and + subject relations remain unsupported. Qualified activation requires upgrading + every serving Peer first. - EACL evaluates relationship cycles as a fixed point and has no separate dispatch-depth limit for checks, lookups, and counts. These operations remain subject to configured traversal work limits. SpiceDB uses a configurable @@ -1960,4 +1962,4 @@ Some of this open-source work was generously funded by my former employer, [Clou - EACL is free and open-source, licensed under the Eclipse Public License v2.0. -See [Caveat and qualifier foundation](docs/caveats.md) for the staged profile, optional JVM evaluator, and Phase 3 serving boundary. +See [Caveats and expiring Relationships](docs/caveats.md) for the v9 public APIs, optional JVM evaluator, trusted-clock and cursor semantics, and coordinated rollout. diff --git a/bin/formal b/bin/formal index 6a33ca86..fadee0c3 100755 --- a/bin/formal +++ b/bin/formal @@ -162,7 +162,8 @@ command=${1:-} case "$command" in fast) need_tools - exec sh "$repo_root/formal/caveats/verify-fast.sh" + sh "$repo_root/formal/caveats/verify-fast.sh" + exec sh "$repo_root/formal/qualified/verify-fast.sh" ;; bootstrap) exec "$repo_root/bin/bootstrap-formal-tools" diff --git a/docs/benchmarks/qualified-authorization-budgets.edn b/docs/benchmarks/qualified-authorization-budgets.edn new file mode 100644 index 00000000..7fd97506 --- /dev/null +++ b/docs/benchmarks/qualified-authorization-budgets.edn @@ -0,0 +1,20 @@ +{:version 1 + :recorded-before-measurement true + :documents 1000 + :backends [:datascript :datomic :datahike :datalevin] + :cases [:ordinary :sparse-5 :sparse-10 :qualified-prefix-10 :expired-prefix-10] + :samples 100 + :relative-budgets + {:ordinary {:latency-factor 1.5 :latency-slack-ns 100000 + :allocation-factor 1.5 :allocation-slack-bytes 32768} + :sparse-5 {:latency-factor 2.0 :latency-slack-ns 250000 + :allocation-factor 2.0 :allocation-slack-bytes 65536} + :sparse-10 {:latency-factor 3.0 :latency-slack-ns 500000 + :allocation-factor 3.0 :allocation-slack-bytes 131072} + :qualified-prefix-10 {:latency-factor 5.0 :latency-slack-ns 2000000 + :allocation-factor 5.0 :allocation-slack-bytes 524288} + :expired-prefix-10 {:latency-factor 5.0 :latency-slack-ns 2000000 + :allocation-factor 5.0 :allocation-slack-bytes 524288}} + :absolute-p95-ns {:point 5000000 :cache-hit 250000 :page 250000000 :count 2000000000} + :data-read-budgets {:ordinary 0 :cache-hit 0 :maximum-per-qualifier 1 + :maximum-per-shared-definition 1 :maximum-per-relation 1}} diff --git a/docs/benchmarks/qualified-authorization.md b/docs/benchmarks/qualified-authorization.md new file mode 100644 index 00000000..aa6a55cf --- /dev/null +++ b/docs/benchmarks/qualified-authorization.md @@ -0,0 +1,77 @@ +# Qualified authorization release qualification + +The numerical budgets in `qualified-authorization-budgets.edn` were recorded +before running the semantic workload. They are local release regression limits, +not service latency promises. A failed limit blocks acceptance; changing a limit +requires a documented reason and a fresh comparison, not silently accepting a +slower result. + +Compare the disabled epoch's ordinary graph with the enabled epoch on the same +host, backend, graph size, public operation, and cache mode. The ordinary case +allows 1.5 times baseline plus 100 microseconds and 32 KiB per request for request +scope/certificate construction. Sparse 5% and 10% cases allow respectively +2 and 3 times baseline plus 250/500 microseconds and 64/128 KiB. Concentrated +10% cases allow 5 times baseline plus 2 milliseconds and 512 KiB because a +bounded output page may have to examine an inactive prefix. Both median and +p95 batch latency must meet the relative limit. Absolute p95 limits are 5 ms +for points, 250 microseconds for completed-answer hits, 250 ms for a 50-item +page, and 2 seconds for an exact 1,000-document count. These limits deliberately +include public request selection, admission, context, and result construction. + +The workload uses the actual optional JVM CEL evaluator. It measures direct, +negative, exclusion, arrow, recursive, first-page, continuation, exact-count, +and completed-answer-hit operations. Qualifiers are distributed over the +entire generated Relationship set; the report records exact counts and density. +Qualified prefixes use active Caveats/expiry, while expired prefixes exercise +skipping before Caveat evaluation, including subtracting Relationships. Each +document also has a parent edge, and every fourth document has a ban. + +Cold requests disable answer and decoded-data caches. Warm requests use the +same fixed context and `:populate-cache? false`, after public physical +Relationship inspection populates decoded data without publishing authorization +answers. Both epochs therefore perform fresh authorization; the harness rejects +unexpected completed-answer hits. The compiled-program cache remains independent. +Completed-answer-hit requests instead keep publication enabled and their context +fixed. The clock is fixed within each case; advancing-clock correctness is +covered by the native cache trace and cursor conformance gates. + +An initial warm prototype varied an unused context field. That was an invalid +isolation method: the ordinary epoch could reuse internal decisions while the +qualified epoch correctly included the whole context in its scope. Measurement +version 3 replaces that prototype with public read-only cache requests and data +preloading on both epochs. No latency or allocation threshold was changed. + +JVM allocation is measured with the current thread's allocation counter and +excludes asynchronous backend work. The backend matrix, raw batch samples, +runtime/host information, first-call latency, and measured native qualification +reads must be retained with the result. Read instrumentation is separate from +timing: ordinary edges and completed-answer hits must perform zero qualifier +data reads; a cold request may fetch each qualifier, shared definition, and +Relation at most once. Each operation warms for at least one second before 100 measured batches. +Counts use five requests per batch, pages three, and points 10–50, as recorded +in each metric. These are percentiles of batch means, not a per-request tail +latency claim. The longer warmup, larger sample count, and count/page batches were introduced +after single-request samples exposed compilation/pause sensitivity near the +fixed limits; the latency and allocation thresholds were not changed. Continuation timing includes +both the first page and its resume under the same context. Completed-answer +hits use the root grant, which is qualified in every nonzero-density case, and +must report an actual answer-cache hit with zero qualification-data reads. + +Run the benchmark through the project nREPL with `:datalevin-dev:caveats-jvm` +loaded. The harness is in `eacl.bench.qualified-authorization-test`; regular unit +test runs do not execute benchmarks. Release acceptance requires all four +backend reports and the checked budget comparison. ClojureScript semantic +correctness is separately gated by the advanced Node suite; these JVM allocation +and latency limits do not claim to measure JavaScript or networked deployments. + +Measurement version 4 adds an assertion that the first 20 physical member +Relationships actually form the intended native qualified/expired prefix. Runs +use a fresh JVM per backend with `-Xms512m -Xmx2g` and no concurrent test or proof +work, so retained state from earlier test suites does not affect measurements. +Version 5 retains that protocol after optimizing definition-content reuse. The +checker rejects duplicate reports, mixed versions/budgets, missing operations, +and incomplete sample sets. + +The [2026-09-05 accepted matrix](results/qualified-authorization-2026-09-05/README.md) +contains all 24 reports, raw samples, source provenance, and the 1,440 passing +budget comparisons. diff --git a/docs/benchmarks/results/qualified-authorization-2026-09-05/README.md b/docs/benchmarks/results/qualified-authorization-2026-09-05/README.md new file mode 100644 index 00000000..44ef1bef --- /dev/null +++ b/docs/benchmarks/results/qualified-authorization-2026-09-05/README.md @@ -0,0 +1,20 @@ +# Qualified authorization performance acceptance + +All 24 reports passed all 1,440 fixed latency and allocation comparisons. Each report contains 18 operation/cache-mode combinations and 100 measured batches per combination. Ordinary edges and completed-answer hits issued zero qualification-data reads; no request fetched a qualifier, shared definition, or Relation more than once. + +This run measures implementation commit `89e25860f198c1c00279e78e047d962ae902077b`, explicitly binding each semantic epoch before the default-v9 activation. The activation changes the default and release/continuation ABI metadata; the benchmark already selects each evaluated epoch explicitly. + +The workload has 1,000 documents and 2,253 Relationships. The sparse cases have 112 (4.97%) and 225 (9.99%) qualified Relationships. Measurements used macOS 26.4.1, Apple M4 Max, 36 GiB RAM, and Java 26.0.2, with a fresh 2 GiB maximum-heap JVM per backend and no concurrent tests, proof runs, or builds. + +Exact-count median latency, milliseconds (cold cache): + +| Backend | Legacy 0% | V9 0% | V9 5% | V9 10% | Qualified prefix | Expired prefix | +|---|---:|---:|---:|---:|---:|---:| +| datascript | 1.519 | 1.764 | 2.560 | 3.670 | 3.229 | 2.455 | +| datomic | 1.659 | 1.842 | 2.863 | 4.131 | 4.608 | 3.536 | +| datahike | 1.736 | 1.898 | 2.976 | 4.067 | 3.609 | 2.793 | +| datalevin | 23.033 | 23.664 | 24.970 | 26.310 | 25.868 | 28.634 | + +The [CSV](metrics.csv) contains first-call latency, medians, p95 batch means, allocations, sample counts, and native read counts for every operation. These percentiles describe batch means, not individual-request tail latency or a service-level promise. The [raw archive](raw-reports.tar.gz) retains every batch sample and budget; [provenance](provenance.json) pins production source hashes, the benchmark, dependency declarations, and the Datalevin fork commit. The executable checker result is [budget-comparison.edn](budget-comparison.edn). + +Earlier prototype/isolation runs failed fixed limits and led to changes in compiled-definition reuse and measurement isolation. Those failures were not accepted as release evidence. Version 5 uses the documented fresh-JVM, fixed-context protocol and the original numeric budgets without relaxation. The complete protocol and limits are in [the benchmark guide](../../qualified-authorization.md). diff --git a/docs/benchmarks/results/qualified-authorization-2026-09-05/budget-comparison.edn b/docs/benchmarks/results/qualified-authorization-2026-09-05/budget-comparison.edn new file mode 100644 index 00000000..5c0d71fe --- /dev/null +++ b/docs/benchmarks/results/qualified-authorization-2026-09-05/budget-comparison.edn @@ -0,0 +1 @@ +{:reports 24, :comparisons 1440, :passed? true, :failures []} \ No newline at end of file diff --git a/docs/benchmarks/results/qualified-authorization-2026-09-05/metrics.csv b/docs/benchmarks/results/qualified-authorization-2026-09-05/metrics.csv new file mode 100644 index 00000000..0261107a --- /dev/null +++ b/docs/benchmarks/results/qualified-authorization-2026-09-05/metrics.csv @@ -0,0 +1,433 @@ +backend,scenario,enabled,mode,operation,samples,iterations_per_batch,first_call_us,median_us,p95_batch_mean_us,allocated_bytes_per_request,qualification_reads,maximum_reads_per_entity +datahike,ordinary,False,cold,arrow,100,20,378.25,61.3375,72.63539999999999,142272.8,0,0 +datahike,ordinary,False,cold,cache-hit,100,50,5832.708,30.46166,37.29,83105.6,0,0 +datahike,ordinary,False,cold,continuation-roundtrip,100,3,4064.0,1462.8193333333334,1880.625,3260874.6666666665,0,0 +datahike,ordinary,False,cold,count,100,5,1994.75,1735.7333999999998,1969.875,5009688.0,0,0 +datahike,ordinary,False,cold,direct,100,20,1423.708,78.25625,475.4229,137376.8,0,0 +datahike,ordinary,False,cold,exclusion,100,20,890.375,70.71664999999999,107.30835,173306.8,0,0 +datahike,ordinary,False,cold,negative,100,20,730.834,56.5875,105.90625,136645.2,0,0 +datahike,ordinary,False,cold,page,100,3,27715.75,916.3053333333334,1480.4166666666667,1758906.6666666667,0,0 +datahike,ordinary,False,cold,recursive,100,10,308.25,71.4166,90.5958,164671.2,0,0 +datahike,ordinary,False,warm,arrow,100,20,440.916,76.19585000000001,84.875,177075.6,0,0 +datahike,ordinary,False,warm,cache-hit,100,50,129.417,31.45,37.5575,83169.6,0,0 +datahike,ordinary,False,warm,continuation-roundtrip,100,3,3284.709,2132.6806666666666,2584.514,4967658.666666667,0,0 +datahike,ordinary,False,warm,count,100,5,2627.667,2482.525,2762.1582000000003,7230088.0,0,0 +datahike,ordinary,False,warm,direct,100,20,653.25,62.4979,82.51665,167735.6,0,0 +datahike,ordinary,False,warm,exclusion,100,20,2621.125,342.56665000000004,404.4396,722293.2,0,0 +datahike,ordinary,False,warm,negative,100,20,128.417,70.27914999999999,84.4021,167632.4,0,0 +datahike,ordinary,False,warm,page,100,3,3850.167,1064.625,1438.2083333333333,2605042.6666666665,0,0 +datahike,ordinary,False,warm,recursive,100,10,487.834,75.7667,101.27080000000001,188924.8,0,0 +datahike,expired-prefix-10,True,cold,arrow,100,20,242.958,105.9375,120.51455,250886.0,3,1 +datahike,expired-prefix-10,True,cold,cache-hit,100,50,377.209,35.529180000000004,37.908339999999995,104537.12,0,0 +datahike,expired-prefix-10,True,cold,continuation-roundtrip,100,3,2960.458,2247.1526666666664,2897.6113333333333,5880258.666666667,128,1 +datahike,expired-prefix-10,True,cold,count,100,5,2836.458,2793.35,3220.4832,7849507.2,128,1 +datahike,expired-prefix-10,True,cold,direct,100,20,310.875,56.552099999999996,62.675,155391.6,0,0 +datahike,expired-prefix-10,True,cold,exclusion,100,20,192.417,66.80414999999999,72.5896,185341.6,0,0 +datahike,expired-prefix-10,True,cold,negative,100,20,64.25,54.35625,64.5375,151462.8,0,0 +datahike,expired-prefix-10,True,cold,page,100,3,2529.208,1751.4443333333334,2250.6803333333337,4311698.666666667,128,1 +datahike,expired-prefix-10,True,cold,recursive,100,10,120.125,72.6666,81.5958,183360.0,2,1 +datahike,expired-prefix-10,True,warm,arrow,100,20,288.458,87.39165,96.05625,221482.4,3,1 +datahike,expired-prefix-10,True,warm,cache-hit,100,50,71.959,35.24332,37.09666,104537.12,0,0 +datahike,expired-prefix-10,True,warm,continuation-roundtrip,100,3,2315.667,1821.2636666666667,1949.9303333333332,4879858.666666667,55,1 +datahike,expired-prefix-10,True,warm,count,100,5,2748.625,2678.2916,3074.8916,8026531.2,55,1 +datahike,expired-prefix-10,True,warm,direct,100,20,161.875,64.45625,74.5083,164922.0,0,0 +datahike,expired-prefix-10,True,warm,exclusion,100,20,303.291,73.1,77.13125,199070.0,0,0 +datahike,expired-prefix-10,True,warm,negative,100,20,90.542,60.060449999999996,63.47085,163063.6,0,0 +datahike,expired-prefix-10,True,warm,page,100,3,2379.459,1187.986,1361.1666666666667,3131882.6666666665,55,1 +datahike,expired-prefix-10,True,warm,recursive,100,10,141.75,75.5,86.8125,190973.6,2,1 +datahike,ordinary,True,cold,arrow,100,20,189.708,60.32085,72.98960000000001,156780.8,0,0 +datahike,ordinary,True,cold,cache-hit,100,50,759.792,36.65334,45.738339999999994,104537.6,0,0 +datahike,ordinary,True,cold,continuation-roundtrip,100,3,2572.5,1174.1666666666667,1538.3886666666667,3115813.3333333335,0,0 +datahike,ordinary,True,cold,count,100,5,2459.667,1898.35,2176.1418,5610528.0,0,0 +datahike,ordinary,True,cold,direct,100,20,339.917,56.34585,65.14585,150769.2,0,0 +datahike,ordinary,True,cold,exclusion,100,20,424.625,67.4625,83.375,182604.8,0,0 +datahike,ordinary,True,cold,negative,100,20,344.375,54.75625,69.9875,150656.8,0,0 +datahike,ordinary,True,cold,page,100,3,3780.042,623.153,806.0833333333334,1547818.6666666667,0,0 +datahike,ordinary,True,cold,recursive,100,10,161.625,73.2667,101.6959,181040.8,0,0 +datahike,ordinary,True,warm,arrow,100,20,143.125,61.0375,79.43960000000001,165146.8,0,0 +datahike,ordinary,True,warm,cache-hit,100,50,153.958,35.308339999999994,39.53168,104537.6,0,0 +datahike,ordinary,True,warm,continuation-roundtrip,100,3,1658.334,1220.0,1455.861,3455560.0,0,0 +datahike,ordinary,True,warm,count,100,5,2337.0,2236.8916,2562.1418,7314688.0,0,0 +datahike,ordinary,True,warm,direct,100,20,234.5,59.94585,81.69585000000001,162370.4,0,0 +datahike,ordinary,True,warm,exclusion,100,20,263.167,75.2,95.89795,198266.4,0,0 +datahike,ordinary,True,warm,negative,100,20,68.916,64.52085,86.64789999999999,162257.2,0,0 +datahike,ordinary,True,warm,page,100,3,1770.666,602.1806666666666,748.597,1712701.3333333333,0,0 +datahike,ordinary,True,warm,recursive,100,10,131.459,74.3334,104.45410000000001,183498.4,0,0 +datahike,qualified-prefix-10,True,cold,arrow,100,20,185.208,101.75,118.6125,255488.4,3,1 +datahike,qualified-prefix-10,True,cold,cache-hit,100,50,572.542,39.1575,41.83918,110569.12,0,0 +datahike,qualified-prefix-10,True,cold,continuation-roundtrip,100,3,3166.625,3000.111,3655.111,7885114.666666667,133,1 +datahike,qualified-prefix-10,True,cold,count,100,5,4138.75,3608.625,4103.9166000000005,10250291.2,128,1 +datahike,qualified-prefix-10,True,cold,direct,100,20,302.291,58.64165,73.82915,158506.8,0,0 +datahike,qualified-prefix-10,True,cold,exclusion,100,20,155.708,69.825,80.95,190305.6,0,0 +datahike,qualified-prefix-10,True,cold,negative,100,20,75.25,56.44375,63.83955,151614.8,0,0 +datahike,qualified-prefix-10,True,cold,page,100,3,2630.541,1805.0693333333334,1974.5416666666667,4764338.666666667,89,1 +datahike,qualified-prefix-10,True,cold,recursive,100,10,166.958,112.8416,128.775,278038.4,4,1 +datahike,qualified-prefix-10,True,warm,arrow,100,20,242.708,108.28125,128.17085,265062.8,3,1 +datahike,qualified-prefix-10,True,warm,cache-hit,100,50,87.916,41.10084,50.205839999999995,110569.12,0,0 +datahike,qualified-prefix-10,True,warm,continuation-roundtrip,100,3,2827.5,2524.028,3010.8333333333335,6806690.666666667,58,1 +datahike,qualified-prefix-10,True,warm,count,100,5,3681.375,3517.8668,3904.775,10590867.2,55,1 +datahike,qualified-prefix-10,True,warm,direct,100,20,371.042,61.83125,70.3417,167717.6,0,0 +datahike,qualified-prefix-10,True,warm,exclusion,100,20,303.875,75.025,83.9333,204142.0,0,0 +datahike,qualified-prefix-10,True,warm,negative,100,20,77.75,61.9354,77.09164999999999,163415.2,0,0 +datahike,qualified-prefix-10,True,warm,page,100,3,2649.833,1510.097,1892.278,3997522.6666666665,38,1 +datahike,qualified-prefix-10,True,warm,recursive,100,10,177.708,115.2958,139.1166,284120.0,4,1 +datahike,sparse-10,True,cold,arrow,100,20,171.458,102.3125,117.50210000000001,256636.8,3,1 +datahike,sparse-10,True,cold,cache-hit,100,50,334.667,39.35918,43.4025,110569.6,0,0 +datahike,sparse-10,True,cold,continuation-roundtrip,100,3,2089.583,1628.5556666666666,2032.889,4334274.666666667,24,1 +datahike,sparse-10,True,cold,count,100,5,4329.584,4066.7582,4477.975,11407792.0,129,1 +datahike,sparse-10,True,cold,direct,100,20,219.625,61.7479,81.75415,160139.6,0,0 +datahike,sparse-10,True,cold,exclusion,100,20,478.667,71.22710000000001,82.33955,193273.2,2,1 +datahike,sparse-10,True,cold,negative,100,20,82.167,55.72085,64.0146,152195.6,0,0 +datahike,sparse-10,True,cold,page,100,3,1917.458,799.2083333333334,859.6803333333334,2215170.6666666665,13,1 +datahike,sparse-10,True,cold,recursive,100,10,104.708,74.79169999999999,100.45410000000001,186649.6,0,0 +datahike,sparse-10,True,warm,arrow,100,20,275.417,111.0792,146.94795000000002,266223.2,3,1 +datahike,sparse-10,True,warm,cache-hit,100,50,110.916,39.77168,44.95832,110569.12,0,0 +datahike,sparse-10,True,warm,continuation-roundtrip,100,3,2061.875,1780.889,2056.916666666667,4700650.666666667,24,1 +datahike,sparse-10,True,warm,count,100,5,5254.458,3999.4582,4385.775,11885147.2,57,1 +datahike,sparse-10,True,warm,direct,100,20,126.583,64.6229,77.11875,170491.6,0,0 +datahike,sparse-10,True,warm,exclusion,100,20,284.5,83.6729,113.05,206398.4,0,0 +datahike,sparse-10,True,warm,negative,100,20,90.666,62.052099999999996,83.0979,163927.2,0,0 +datahike,sparse-10,True,warm,page,100,3,1919.667,879.2223333333334,1106.722,2397026.6666666665,13,1 +datahike,sparse-10,True,warm,recursive,100,10,169.583,72.3208,85.30839999999999,187085.6,0,0 +datahike,sparse-5,True,cold,arrow,100,20,383.125,102.7021,127.68335,254376.0,3,1 +datahike,sparse-5,True,cold,cache-hit,100,50,985.959,39.886660000000006,43.59,110569.6,0,0 +datahike,sparse-5,True,cold,continuation-roundtrip,100,3,1873.209,1363.875,1727.528,3617490.6666666665,14,1 +datahike,sparse-5,True,cold,count,100,5,3165.958,2975.5416,3437.1916,8234009.6,67,1 +datahike,sparse-5,True,cold,direct,100,20,281.667,58.602050000000006,69.47705,157625.2,0,0 +datahike,sparse-5,True,cold,exclusion,100,20,258.875,69.21664999999999,79.0,189196.4,0,0 +datahike,sparse-5,True,cold,negative,100,20,93.292,55.93125,63.66045,153006.8,0,0 +datahike,sparse-5,True,cold,page,100,3,2586.791,722.7083333333334,986.264,1979194.6666666667,9,1 +datahike,sparse-5,True,cold,recursive,100,10,519.708,72.97919999999999,82.6584,185231.2,0,0 +datahike,sparse-5,True,warm,arrow,100,20,217.5,81.8896,90.97295,210290.4,1,1 +datahike,sparse-5,True,warm,cache-hit,100,50,162.0,40.48668,46.78832,110569.6,0,0 +datahike,sparse-5,True,warm,continuation-roundtrip,100,3,1720.292,1344.0693333333334,1672.5973333333332,3722922.6666666665,4,1 +datahike,sparse-5,True,warm,count,100,5,2831.416,2779.4332000000004,3256.8918,8589296.0,2,1 +datahike,sparse-5,True,warm,direct,100,20,130.334,61.3771,67.13335000000001,166101.2,0,0 +datahike,sparse-5,True,warm,exclusion,100,20,269.417,73.16875,87.2125,202546.0,0,0 +datahike,sparse-5,True,warm,negative,100,20,95.75,60.1854,70.825,164213.6,0,0 +datahike,sparse-5,True,warm,page,100,3,1820.875,702.5553333333334,877.5973333333334,1929274.6666666667,2,1 +datahike,sparse-5,True,warm,recursive,100,10,142.542,72.6417,84.6417,186424.8,0,0 +datalevin,ordinary,False,cold,arrow,100,20,1375.25,998.9854,1105.6583,3470392.8,0,0 +datalevin,ordinary,False,cold,cache-hit,100,50,4943.167,38.67418,41.70834,86033.6,0,0 +datalevin,ordinary,False,cold,continuation-roundtrip,100,3,9431.375,7163.472,8429.944333333335,23172181.333333332,0,0 +datalevin,ordinary,False,cold,count,100,5,22648.667,23032.625,24240.9166,79936184.0,0,0 +datalevin,ordinary,False,cold,direct,100,20,1425.417,129.625,175.92079999999999,364223.6,0,0 +datalevin,ordinary,False,cold,exclusion,100,20,705.584,121.52085000000001,135.59795000000003,400412.0,0,0 +datalevin,ordinary,False,cold,negative,100,20,364.459,114.375,138.175,364152.8,0,0 +datalevin,ordinary,False,cold,page,100,3,25186.917,3909.0693333333334,4912.944333333333,11700101.333333334,0,0 +datalevin,ordinary,False,cold,recursive,100,10,434.167,136.25420000000003,162.8625,418496.0,0,0 +datalevin,ordinary,False,warm,arrow,100,20,1415.042,993.25205,1113.8291499999998,3499072.4,0,0 +datalevin,ordinary,False,warm,cache-hit,100,50,252.417,38.86668,42.05,85897.6,0,0 +datalevin,ordinary,False,warm,continuation-roundtrip,100,3,8330.417,7858.125,8994.444333333335,24784405.333333332,0,0 +datalevin,ordinary,False,warm,count,100,5,23874.208,24105.925,24955.6082,82128425.6,0,0 +datalevin,ordinary,False,warm,direct,100,20,688.75,119.24789999999999,136.57915,395264.8,0,0 +datalevin,ordinary,False,warm,exclusion,100,20,2020.167,376.83125,460.29375,922316.0,0,0 +datalevin,ordinary,False,warm,negative,100,20,178.542,119.20625,140.35,395536.8,0,0 +datalevin,ordinary,False,warm,page,100,3,6093.292,3976.6666666666665,4839.194666666667,12503365.333333334,0,0 +datalevin,ordinary,False,warm,recursive,100,10,289.125,134.72910000000002,147.81670000000003,426675.2,0,0 +datalevin,expired-prefix-10,True,cold,arrow,100,20,5031.833,2473.5270499999997,4588.12705,6540362.4,3,1 +datalevin,expired-prefix-10,True,cold,cache-hit,100,50,1732.041,52.255,103.52584,105017.6,0,0 +datalevin,expired-prefix-10,True,cold,continuation-roundtrip,100,3,13298.792,11894.055666666665,16378.222,29344813.333333332,128,1 +datalevin,expired-prefix-10,True,cold,count,100,5,28776.458,28633.6584,38131.9416,79921432.0,128,1 +datalevin,expired-prefix-10,True,cold,direct,100,20,660.666,176.72085,326.8625,383526.8,0,0 +datalevin,expired-prefix-10,True,cold,exclusion,100,20,394.334,152.29585,218.10835,417861.2,0,0 +datalevin,expired-prefix-10,True,cold,negative,100,20,173.083,137.9229,366.31875,378353.6,0,0 +datalevin,expired-prefix-10,True,cold,page,100,3,14449.709,7391.166666666667,10783.833333333334,17794837.333333332,128,1 +datalevin,expired-prefix-10,True,cold,recursive,100,10,1563.333,171.00420000000003,272.1417,435481.6,2,1 +datalevin,expired-prefix-10,True,warm,arrow,100,20,2660.417,189.86875,290.40625,448542.8,3,1 +datalevin,expired-prefix-10,True,warm,cache-hit,100,50,559.209,52.9075,61.279160000000005,105017.6,0,0 +datalevin,expired-prefix-10,True,warm,continuation-roundtrip,100,3,11776.667,11498.722333333333,16937.708333333332,28167477.333333332,55,1 +datalevin,expired-prefix-10,True,warm,count,100,5,28672.75,28774.5582,45504.3,79940064.0,55,1 +datalevin,expired-prefix-10,True,warm,direct,100,20,346.125,153.1125,217.5854,391784.0,0,0 +datalevin,expired-prefix-10,True,warm,exclusion,100,20,1265.416,164.33960000000002,303.7646,431476.8,0,0 +datalevin,expired-prefix-10,True,warm,negative,100,20,156.167,192.75414999999998,864.5958,389665.6,0,0 +datalevin,expired-prefix-10,True,warm,page,100,3,7692.667,7160.889,19446.514,16445285.333333334,55,1 +datalevin,expired-prefix-10,True,warm,recursive,100,10,408.584,162.8125,290.6375,429059.2,2,1 +datalevin,ordinary,True,cold,arrow,100,20,1302.875,991.70625,1091.53125,3484143.6,0,0 +datalevin,ordinary,True,cold,cache-hit,100,50,1710.083,46.03582,53.23832,105017.6,0,0 +datalevin,ordinary,True,cold,continuation-roundtrip,100,3,9009.291,7021.764,8753.375,23047413.333333332,0,0 +datalevin,ordinary,True,cold,count,100,5,25711.375,23664.2166,25429.2582,80535712.0,0,0 +datalevin,ordinary,True,cold,direct,100,20,660.542,126.47085000000001,158.78125,377114.0,0,0 +datalevin,ordinary,True,cold,exclusion,100,20,383.667,124.08955,149.12920000000003,415112.0,0,0 +datalevin,ordinary,True,cold,negative,100,20,291.541,112.12295,136.52295,377359.6,0,0 +datalevin,ordinary,True,cold,page,100,3,5990.5,3740.9166666666665,4463.597,11510181.333333334,0,0 +datalevin,ordinary,True,cold,recursive,100,10,335.917,134.3875,156.3667,434424.0,0,0 +datalevin,ordinary,True,warm,arrow,100,20,1373.416,995.0292,1149.5021000000002,3485576.4,0,0 +datalevin,ordinary,True,warm,cache-hit,100,50,329.25,44.075,46.89584,105017.6,0,0 +datalevin,ordinary,True,warm,continuation-roundtrip,100,3,7821.625,7079.722,8091.041666666667,23387264.0,0,0 +datalevin,ordinary,True,warm,count,100,5,23296.375,24168.7082,26274.65,82239392.0,0,0 +datalevin,ordinary,True,warm,direct,100,20,548.542,118.33330000000001,150.31455,388424.8,0,0 +datalevin,ordinary,True,warm,exclusion,100,20,456.75,138.42085,196.3229,430648.0,0,0 +datalevin,ordinary,True,warm,negative,100,20,166.333,114.7771,122.29375,388696.8,0,0 +datalevin,ordinary,True,warm,page,100,3,4746.25,3506.5833333333335,4239.555333333333,11677581.333333334,0,0 +datalevin,ordinary,True,warm,recursive,100,10,280.417,129.5709,184.2375,419475.2,0,0 +datalevin,qualified-prefix-10,True,cold,arrow,100,20,1194.0,1159.38955,1268.9,3595504.0,3,1 +datalevin,qualified-prefix-10,True,cold,cache-hit,100,50,1073.5,48.61418,56.43668,111049.6,0,0 +datalevin,qualified-prefix-10,True,cold,continuation-roundtrip,100,3,10413.459,9858.222333333333,10935.986,28110093.333333332,133,1 +datalevin,qualified-prefix-10,True,cold,count,100,5,27947.667,25868.3416,27464.216800000002,85490385.6,128,1 +datalevin,qualified-prefix-10,True,cold,direct,100,20,396.958,120.00835000000001,163.50205,385966.0,0,0 +datalevin,qualified-prefix-10,True,cold,exclusion,100,20,1632.125,138.2625,192.35,424007.2,0,0 +datalevin,qualified-prefix-10,True,cold,negative,100,20,147.833,121.125,161.18125,378353.6,0,0 +datalevin,qualified-prefix-10,True,cold,page,100,3,6026.625,5390.527666666667,6271.416666666667,14921101.333333334,89,1 +datalevin,qualified-prefix-10,True,cold,recursive,100,10,246.333,194.35829999999999,270.4042,541409.6,4,1 +datalevin,qualified-prefix-10,True,warm,arrow,100,20,1316.541,1041.35625,1161.63335,3598303.2,3,1 +datalevin,qualified-prefix-10,True,warm,cache-hit,100,50,423.375,57.32166,74.83,111049.6,0,0 +datalevin,qualified-prefix-10,True,warm,continuation-roundtrip,100,3,8710.167,8543.389,12125.889,26865261.333333332,58,1 +datalevin,qualified-prefix-10,True,warm,count,100,5,25016.417,29448.1668,39497.833399999996,85675633.6,55,1 +datalevin,qualified-prefix-10,True,warm,direct,100,20,453.375,121.20625,144.16665,394270.8,0,0 +datalevin,qualified-prefix-10,True,warm,exclusion,100,20,263.417,140.98545000000001,193.10835,437723.6,2,1 +datalevin,qualified-prefix-10,True,warm,negative,100,20,154.625,117.07294999999999,157.0021,389665.6,0,0 +datalevin,qualified-prefix-10,True,warm,page,100,3,4946.084,4414.125,5016.625,14042013.333333334,38,1 +datalevin,qualified-prefix-10,True,warm,recursive,100,10,257.25,185.62920000000003,218.775,530080.8,4,1 +datalevin,sparse-10,True,cold,arrow,100,20,1149.375,1060.15,1187.65625,3594342.0,3,1 +datalevin,sparse-10,True,cold,cache-hit,100,50,787.75,48.75,52.55082,111049.6,0,0 +datalevin,sparse-10,True,cold,continuation-roundtrip,100,3,8390.042,7892.777666666667,8762.764,24330741.333333332,24,1 +datalevin,sparse-10,True,cold,count,100,5,25923.167,26309.975,28139.675,86650337.6,129,1 +datalevin,sparse-10,True,cold,direct,100,20,382.041,116.39375,129.1583,384583.6,0,0 +datalevin,sparse-10,True,cold,exclusion,100,20,236.958,134.1,154.3125,427384.0,0,0 +datalevin,sparse-10,True,cold,negative,100,20,130.25,112.4875,140.21875,378350.0,0,0 +datalevin,sparse-10,True,cold,page,100,3,4433.208,3712.1666666666665,4514.014,12217941.333333334,13,1 +datalevin,sparse-10,True,cold,recursive,100,10,191.5,141.0291,165.3166,439913.6,0,0 +datalevin,sparse-10,True,warm,arrow,100,20,1396.459,1298.975,1658.96875,3598240.4,5,1 +datalevin,sparse-10,True,warm,cache-hit,100,50,216.917,50.66083999999999,61.66666,111049.6,0,0 +datalevin,sparse-10,True,warm,continuation-roundtrip,100,3,10044.083,10014.569666666666,14882.152666666667,24697429.333333332,24,1 +datalevin,sparse-10,True,warm,count,100,5,34654.708,29015.108399999997,32999.725,86904313.6,54,1 +datalevin,sparse-10,True,warm,direct,100,20,200.458,136.65210000000002,173.34795000000003,395645.2,0,0 +datalevin,sparse-10,True,warm,exclusion,100,20,380.083,175.3729,271.0667,438934.4,0,0 +datalevin,sparse-10,True,warm,negative,100,20,165.417,130.87085000000002,178.93545,389662.8,0,0 +datalevin,sparse-10,True,warm,page,100,3,6949.333,5253.319333333333,10319.097,12399981.333333334,13,1 +datalevin,sparse-10,True,warm,recursive,100,10,289.041,160.0375,229.19170000000003,423141.6,0,0 +datalevin,sparse-5,True,cold,arrow,100,20,1536.292,1058.9228999999998,1259.48125,3590359.6,3,1 +datalevin,sparse-5,True,cold,cache-hit,100,50,1499.25,49.4525,54.60834,111049.6,0,0 +datalevin,sparse-5,True,cold,continuation-roundtrip,100,3,7723.208,7316.972,8286.722333333333,23551165.333333332,14,1 +datalevin,sparse-5,True,cold,count,100,5,24089.916,24970.183399999998,26724.825,83318097.6,67,1 +datalevin,sparse-5,True,cold,direct,100,20,385.708,121.18960000000001,161.51045000000002,380896.8,0,0 +datalevin,sparse-5,True,cold,exclusion,100,20,267.125,137.15,182.92915,421228.0,0,0 +datalevin,sparse-5,True,cold,negative,100,20,161.208,117.38539999999999,140.31664999999998,378343.6,0,0 +datalevin,sparse-5,True,cold,page,100,3,5444.084,3690.6526666666664,4645.361,11952082.666666666,9,1 +datalevin,sparse-5,True,cold,recursive,100,10,243.333,141.6792,184.1,435641.6,0,0 +datalevin,sparse-5,True,warm,arrow,100,20,1377.5,1031.43545,1154.2125,3535603.2,1,1 +datalevin,sparse-5,True,warm,cache-hit,100,50,240.333,48.695,51.97584,111049.6,0,0 +datalevin,sparse-5,True,warm,continuation-roundtrip,100,3,7544.292,7259.639,8572.805666666665,23636869.333333332,4,1 +datalevin,sparse-5,True,warm,count,100,5,24312.666,24680.275,25660.475,83539953.6,2,1 +datalevin,sparse-5,True,warm,direct,100,20,190.875,131.07085,172.2396,391710.4,0,0 +datalevin,sparse-5,True,warm,exclusion,100,20,309.958,138.07704999999999,166.11875,433938.0,0,0 +datalevin,sparse-5,True,warm,negative,100,20,166.958,119.79375,141.88125,389657.2,0,0 +datalevin,sparse-5,True,warm,page,100,3,4275.125,3664.111,4439.75,11887581.333333334,2,1 +datalevin,sparse-5,True,warm,recursive,100,10,238.209,133.1625,148.4917,420585.6,0,0 +datascript,ordinary,False,cold,arrow,100,20,353.375,43.2979,50.1,110792.4,0,0 +datascript,ordinary,False,cold,cache-hit,100,50,6020.958,22.24334,27.65582,64513.6,0,0 +datascript,ordinary,False,cold,continuation-roundtrip,100,3,5006.0,1245.9166666666667,1666.472,2772626.6666666665,0,0 +datascript,ordinary,False,cold,count,100,5,1827.209,1518.5918000000001,1748.2332,4993432.0,0,0 +datascript,ordinary,False,cold,direct,100,20,890.084,48.47085,64.8021,105090.4,0,0 +datascript,ordinary,False,cold,exclusion,100,20,820.083,51.59795,66.22085000000001,139521.2,0,0 +datascript,ordinary,False,cold,negative,100,20,204.375,40.74585,55.06875,104220.4,0,0 +datascript,ordinary,False,cold,page,100,3,23228.791,848.0973333333334,1914.3196666666668,1492642.6666666667,0,0 +datascript,ordinary,False,cold,recursive,100,10,331.0,45.8916,54.708400000000005,119698.4,0,0 +datascript,ordinary,False,warm,arrow,100,20,501.916,50.49375,65.84375,142889.6,0,0 +datascript,ordinary,False,warm,cache-hit,100,50,125.709,24.09916,29.33916,64513.6,0,0 +datascript,ordinary,False,warm,continuation-roundtrip,100,3,2300.084,1802.8886666666667,2276.4166666666665,4310266.666666667,0,0 +datascript,ordinary,False,warm,count,100,5,2807.167,2228.2668,2631.075,7117016.0,0,0 +datascript,ordinary,False,warm,direct,100,20,452.417,47.96455,61.4,133124.4,0,0 +datascript,ordinary,False,warm,exclusion,100,20,1394.208,281.59585,333.5458,591808.8,0,0 +datascript,ordinary,False,warm,negative,100,20,85.5,47.064550000000004,58.9229,132556.4,0,0 +datascript,ordinary,False,warm,page,100,3,3295.208,954.9723333333334,1271.7083333333333,2258394.6666666665,0,0 +datascript,ordinary,False,warm,recursive,100,10,165.292,57.2125,86.075,145303.2,0,0 +datascript,expired-prefix-10,True,cold,arrow,100,20,231.041,81.71455,87.9875,210761.6,3,1 +datascript,expired-prefix-10,True,cold,cache-hit,100,50,324.625,27.77166,32.965,83585.12,0,0 +datascript,expired-prefix-10,True,cold,continuation-roundtrip,100,3,2121.083,1868.5136666666667,2072.6946666666668,5096818.666666667,128,1 +datascript,expired-prefix-10,True,cold,count,100,5,2468.375,2454.5334,2826.3666000000003,7475811.2,128,1 +datascript,expired-prefix-10,True,cold,direct,100,20,244.625,42.475,49.51875,121870.0,0,0 +datascript,expired-prefix-10,True,cold,exclusion,100,20,184.708,54.0271,79.1375,155742.0,0,0 +datascript,expired-prefix-10,True,cold,negative,100,20,63.458,41.34165,53.34165,118206.0,0,0 +datascript,expired-prefix-10,True,cold,page,100,3,2184.083,1416.9583333333333,1764.736,3754682.6666666665,128,1 +datascript,expired-prefix-10,True,cold,recursive,100,10,76.959,51.0625,65.4375,138889.6,2,1 +datascript,expired-prefix-10,True,warm,arrow,100,20,405.833,72.5583,95.21039999999999,182099.6,3,1 +datascript,expired-prefix-10,True,warm,cache-hit,100,50,81.042,27.6825,30.41666,83585.12,0,0 +datascript,expired-prefix-10,True,warm,continuation-roundtrip,100,3,1901.0,1521.8333333333333,1890.2776666666668,4239658.666666667,55,1 +datascript,expired-prefix-10,True,warm,count,100,5,2423.667,2366.1916,2802.3,7815675.2,55,1 +datascript,expired-prefix-10,True,warm,direct,100,20,152.916,47.00835,56.01665,131096.4,1,1 +datascript,expired-prefix-10,True,warm,exclusion,100,20,299.834,58.2542,67.25205,170172.4,0,0 +datascript,expired-prefix-10,True,warm,negative,100,20,55.25,44.96665,50.06665,129466.8,0,0 +datascript,expired-prefix-10,True,warm,page,100,3,2119.167,986.2916666666666,1191.1526666666668,2725858.6666666665,55,1 +datascript,expired-prefix-10,True,warm,recursive,100,10,120.583,55.8,69.5958,149557.6,2,1 +datascript,ordinary,True,cold,arrow,100,20,175.0,43.083349999999996,60.09585,124328.4,0,0 +datascript,ordinary,True,cold,cache-hit,100,50,650.042,28.255,36.2275,83585.6,0,0 +datascript,ordinary,True,cold,continuation-roundtrip,100,3,3075.125,991.139,1265.222,2660970.6666666665,0,0 +datascript,ordinary,True,cold,count,100,5,2076.792,1763.6918,1986.8,5574232.0,0,0 +datascript,ordinary,True,cold,direct,100,20,352.25,41.2354,53.0854,117884.4,0,0 +datascript,ordinary,True,cold,exclusion,100,20,267.875,51.820800000000006,68.71045,153760.8,0,0 +datascript,ordinary,True,cold,negative,100,20,155.583,41.2,52.825,117316.4,0,0 +datascript,ordinary,True,cold,page,100,3,3886.75,498.0556666666667,779.903,1318610.6666666667,0,0 +datascript,ordinary,True,cold,recursive,100,10,215.584,50.333400000000005,68.8583,135220.0,0,0 +datascript,ordinary,True,warm,arrow,100,20,127.625,46.9,51.55,134569.6,0,0 +datascript,ordinary,True,warm,cache-hit,100,50,95.791,27.2675,34.4875,83585.6,0,0 +datascript,ordinary,True,warm,continuation-roundtrip,100,3,1411.75,1013.8053333333334,1267.2223333333332,2999498.6666666665,0,0 +datascript,ordinary,True,warm,count,100,5,2227.792,2078.4416,2428.875,7278056.0,0,0 +datascript,ordinary,True,warm,direct,100,20,164.5,49.0896,60.9833,129148.4,0,0 +datascript,ordinary,True,warm,exclusion,100,20,278.625,55.42915,64.47915,169210.4,0,0 +datascript,ordinary,True,warm,negative,100,20,68.083,46.60835,61.03335,128580.4,0,0 +datascript,ordinary,True,warm,page,100,3,1835.875,493.611,598.861,1485202.6666666667,0,0 +datascript,ordinary,True,warm,recursive,100,10,103.417,51.854099999999995,60.2209,143220.0,0,0 +datascript,qualified-prefix-10,True,cold,arrow,100,20,172.084,88.58335000000001,109.94794999999999,213816.8,3,1 +datascript,qualified-prefix-10,True,cold,cache-hit,100,50,681.083,32.29,36.154160000000005,89617.12,0,0 +datascript,qualified-prefix-10,True,cold,continuation-roundtrip,100,3,3787.834,2813.6386666666667,3414.9166666666665,7053418.666666667,133,1 +datascript,qualified-prefix-10,True,cold,count,100,5,3541.834,3229.1416,3606.8334,9838803.2,128,1 +datascript,qualified-prefix-10,True,cold,direct,100,20,163.959,45.6021,57.4,123834.0,0,0 +datascript,qualified-prefix-10,True,cold,exclusion,100,20,143.75,54.19795,62.470800000000004,160708.4,0,0 +datascript,qualified-prefix-10,True,cold,negative,100,20,58.958,40.94165,48.90415,118195.6,0,0 +datascript,qualified-prefix-10,True,cold,page,100,3,2233.667,1730.8333333333333,2269.222,4283906.666666667,89,1 +datascript,qualified-prefix-10,True,cold,recursive,100,10,130.083,91.7375,119.925,221317.6,4,1 +datascript,qualified-prefix-10,True,warm,arrow,100,20,245.333,86.79795,120.96875,225519.2,3,1 +datascript,qualified-prefix-10,True,warm,cache-hit,100,50,118.542,32.37834,37.585,89617.12,0,0 +datascript,qualified-prefix-10,True,warm,continuation-roundtrip,100,3,2520.958,2207.375,2743.6943333333334,6227186.666666667,59,1 +datascript,qualified-prefix-10,True,warm,count,100,5,3362.958,3218.1084,3711.9166,10433985.6,56,1 +datascript,qualified-prefix-10,True,warm,direct,100,20,358.166,49.741699999999994,59.314550000000004,133582.8,0,0 +datascript,qualified-prefix-10,True,warm,exclusion,100,20,219.916,61.6479,86.26875,174110.0,0,0 +datascript,qualified-prefix-10,True,warm,negative,100,20,61.25,44.8979,50.82085,129470.4,0,0 +datascript,qualified-prefix-10,True,warm,page,100,3,1907.084,1224.639,1392.8053333333332,3482362.6666666665,29,1 +datascript,qualified-prefix-10,True,warm,recursive,100,10,153.291,91.725,112.7,233037.6,4,1 +datascript,sparse-10,True,cold,arrow,100,20,148.416,82.1208,88.50835000000001,214364.8,3,1 +datascript,sparse-10,True,cold,cache-hit,100,50,335.542,32.47084,36.2925,89617.6,0,0 +datascript,sparse-10,True,cold,continuation-roundtrip,100,3,1639.792,1357.9166666666667,1600.361,3786586.6666666665,24,1 +datascript,sparse-10,True,cold,count,100,5,3645.0,3669.9166,4127.1668,10957208.0,129,1 +datascript,sparse-10,True,cold,direct,100,20,169.125,43.75625,52.6625,125490.0,0,0 +datascript,sparse-10,True,cold,exclusion,100,20,183.75,55.47295,65.4875,163177.2,0,0 +datascript,sparse-10,True,cold,negative,100,20,60.166,40.4229,45.325,118197.2,0,0 +datascript,sparse-10,True,cold,page,100,3,1402.417,669.9166666666666,762.403,1941370.6666666667,13,1 +datascript,sparse-10,True,cold,recursive,100,10,76.542,51.775,75.3041,137980.8,0,0 +datascript,sparse-10,True,warm,arrow,100,20,192.542,87.53125,104.13539999999999,225854.4,3,1 +datascript,sparse-10,True,warm,cache-hit,100,50,111.625,31.96,35.621660000000006,89617.12,0,0 +datascript,sparse-10,True,warm,continuation-roundtrip,100,3,1926.334,1480.0,1818.5416666666667,4151794.6666666665,24,1 +datascript,sparse-10,True,warm,count,100,5,3984.542,3664.2668,4146.5833999999995,11571131.2,55,1 +datascript,sparse-10,True,warm,direct,100,20,118.333,48.925,57.4792,135802.4,0,0 +datascript,sparse-10,True,warm,exclusion,100,20,327.125,59.96875,68.85625,174517.2,0,0 +datascript,sparse-10,True,warm,negative,100,20,69.625,44.989599999999996,49.927099999999996,129465.6,0,0 +datascript,sparse-10,True,warm,page,100,3,1786.583,730.222,841.3053333333334,2122666.6666666665,13,1 +datascript,sparse-10,True,warm,recursive,100,10,100.083,54.4125,65.62910000000001,146037.6,0,0 +datascript,sparse-5,True,cold,arrow,100,20,276.25,80.64375,98.81045,210605.2,3,1 +datascript,sparse-5,True,cold,cache-hit,100,50,1114.25,32.265,38.19918,89617.6,0,0 +datascript,sparse-5,True,cold,continuation-roundtrip,100,3,1684.458,1151.7776666666668,1486.847,3077306.6666666665,14,1 +datascript,sparse-5,True,cold,count,100,5,2710.375,2560.4667999999997,2974.5,7987536.0,67,1 +datascript,sparse-5,True,cold,direct,100,20,211.875,43.05835,48.9583,122967.6,0,0 +datascript,sparse-5,True,cold,exclusion,100,20,165.208,53.30835,61.177099999999996,158842.4,0,0 +datascript,sparse-5,True,cold,negative,100,20,82.625,40.0146,44.866699999999994,118166.4,0,0 +datascript,sparse-5,True,cold,page,100,3,2477.5,594.2916666666666,774.9443333333334,1702866.6666666667,9,1 +datascript,sparse-5,True,cold,recursive,100,10,158.166,51.1625,85.325,136853.6,0,0 +datascript,sparse-5,True,warm,arrow,100,20,186.083,65.31455,74.68955,174577.2,1,1 +datascript,sparse-5,True,warm,cache-hit,100,50,119.416,31.99082,34.21168,89617.6,0,0 +datascript,sparse-5,True,warm,continuation-roundtrip,100,3,1507.542,1090.903,1217.1806666666666,3207634.6666666665,4,1 +datascript,sparse-5,True,warm,count,100,5,2633.333,2512.0332000000003,2923.3,8498832.0,2,1 +datascript,sparse-5,True,warm,direct,100,20,113.708,47.6125,61.8104,131880.8,0,0 +datascript,sparse-5,True,warm,exclusion,100,20,388.291,58.00415,70.68545,172034.4,0,0 +datascript,sparse-5,True,warm,negative,100,20,80.958,45.83955,52.372949999999996,129433.2,0,0 +datascript,sparse-5,True,warm,page,100,3,1347.416,567.778,643.1943333333334,1672338.6666666667,2,1 +datascript,sparse-5,True,warm,recursive,100,10,96.0,53.904199999999996,65.1083,144692.8,0,0 +datomic,ordinary,False,cold,arrow,100,20,423.833,51.44585,87.86875,129558.8,0,0 +datomic,ordinary,False,cold,cache-hit,100,50,4674.042,27.0775,29.21418,78977.6,0,0 +datomic,ordinary,False,cold,continuation-roundtrip,100,3,4128.042,1087.4863333333333,1298.2223333333332,2860962.6666666665,0,0 +datomic,ordinary,False,cold,count,100,5,2153.791,1659.3,1911.8834,5091992.0,0,0 +datomic,ordinary,False,cold,direct,100,20,1063.5,51.37915,65.7646,124499.6,0,0 +datomic,ordinary,False,cold,exclusion,100,20,983.25,60.7,114.36460000000001,154642.4,0,0 +datomic,ordinary,False,cold,negative,100,20,140.084,41.9625,46.085449999999994,123992.4,0,0 +datomic,ordinary,False,cold,page,100,3,22312.958,668.2083333333334,842.0833333333334,1543466.6666666667,0,0 +datomic,ordinary,False,cold,recursive,100,10,302.083,54.091699999999996,64.3709,150768.0,0,0 +datomic,ordinary,False,warm,arrow,100,20,612.958,55.85625,69.27295,162101.2,0,0 +datomic,ordinary,False,warm,cache-hit,100,50,94.667,27.49332,31.72916,79209.6,0,0 +datomic,ordinary,False,warm,continuation-roundtrip,100,3,2384.708,1710.4166666666667,2148.1806666666666,4424997.333333333,0,0 +datomic,ordinary,False,warm,count,100,5,2829.0,2303.7334,2634.2916,7231064.0,0,0 +datomic,ordinary,False,warm,direct,100,20,484.875,54.59375,74.11664999999999,153439.6,0,0 +datomic,ordinary,False,warm,exclusion,100,20,2178.875,271.92295,315.65,621732.4,0,0 +datomic,ordinary,False,warm,negative,100,20,108.791,54.3958,71.3,153192.8,0,0 +datomic,ordinary,False,warm,page,100,3,3645.166,896.0693333333334,1059.236,2323002.6666666665,0,0 +datomic,ordinary,False,warm,recursive,100,10,462.75,61.2125,72.7792,172744.8,0,0 +datomic,expired-prefix-10,True,cold,arrow,100,20,342.541,113.7375,168.97085,248185.2,3,1 +datomic,expired-prefix-10,True,cold,cache-hit,100,50,676.708,37.488339999999994,69.695,98313.6,0,0 +datomic,expired-prefix-10,True,cold,continuation-roundtrip,100,3,3060.25,3178.5136666666667,5791.472333333333,5583690.666666667,128,1 +datomic,expired-prefix-10,True,cold,count,100,5,3367.416,3536.3833999999997,6359.75,7993816.0,128,1 +datomic,expired-prefix-10,True,cold,direct,100,20,227.792,57.227050000000006,103.66669999999999,142662.8,0,0 +datomic,expired-prefix-10,True,cold,exclusion,100,20,388.166,67.66664999999999,95.7354,171638.8,0,0 +datomic,expired-prefix-10,True,cold,negative,100,20,61.875,53.0,74.47705,137240.4,0,0 +datomic,expired-prefix-10,True,cold,page,100,3,3888.0,2271.9583333333335,4967.597333333333,4201330.666666667,128,1 +datomic,expired-prefix-10,True,cold,recursive,100,10,113.125,75.66669999999999,95.3375,172620.8,2,1 +datomic,expired-prefix-10,True,warm,arrow,100,20,1274.125,148.25625,510.33125,213320.0,3,1 +datomic,expired-prefix-10,True,warm,cache-hit,100,50,165.458,38.0575,53.1725,98313.6,0,0 +datomic,expired-prefix-10,True,warm,continuation-roundtrip,100,3,2925.208,2354.8473333333336,7168.819333333333,4492394.666666667,53,1 +datomic,expired-prefix-10,True,warm,count,100,5,3759.583,3571.9168,5490.441599999999,8098832.0,53,1 +datomic,expired-prefix-10,True,warm,direct,100,20,204.708,62.9583,97.94165,151305.6,0,0 +datomic,expired-prefix-10,True,warm,exclusion,100,20,1906.917,77.73125,408.97290000000004,184824.8,0,0 +datomic,expired-prefix-10,True,warm,negative,100,20,66.75,82.03545,193.35205,148720.4,0,0 +datomic,expired-prefix-10,True,warm,page,100,3,102770.709,1409.5693333333334,4452.555666666667,2937906.6666666665,53,1 +datomic,expired-prefix-10,True,warm,recursive,100,10,351.5,147.8916,817.1959,179842.4,2,1 +datomic,ordinary,True,cold,arrow,100,20,173.042,48.5583,62.9125,142278.8,0,0 +datomic,ordinary,True,cold,cache-hit,100,50,856.625,34.77084,44.40334,98313.6,0,0 +datomic,ordinary,True,cold,continuation-roundtrip,100,3,2051.75,982.361,1266.125,2742154.6666666665,0,0 +datomic,ordinary,True,cold,count,100,5,2022.666,1842.1334,2185.8582,5691216.0,0,0 +datomic,ordinary,True,cold,direct,100,20,317.584,46.0271,53.74375,136536.8,0,0 +datomic,ordinary,True,cold,exclusion,100,20,243.333,59.02915,74.5771,168201.2,0,0 +datomic,ordinary,True,cold,negative,100,20,224.125,46.01875,51.85835,136288.4,0,0 +datomic,ordinary,True,cold,page,100,3,3683.291,525.1943333333334,867.0556666666666,1360514.6666666667,0,0 +datomic,ordinary,True,cold,recursive,100,10,157.417,57.841699999999996,68.4125,165428.0,0,0 +datomic,ordinary,True,warm,arrow,100,20,143.125,51.45625,64.30835,150805.2,0,0 +datomic,ordinary,True,warm,cache-hit,100,50,129.25,32.43916,34.63918,98313.6,0,0 +datomic,ordinary,True,warm,continuation-roundtrip,100,3,1455.584,1172.2363333333333,1552.9583333333333,3081250.6666666665,0,0 +datomic,ordinary,True,warm,count,100,5,2430.583,2305.8,2643.5416,7395088.0,0,0 +datomic,ordinary,True,warm,direct,100,20,179.916,57.447900000000004,60.22295,148016.4,0,0 +datomic,ordinary,True,warm,exclusion,100,20,266.583,63.029199999999996,74.87705,183880.8,0,0 +datomic,ordinary,True,warm,negative,100,20,89.792,50.775,64.65625,147768.0,0,0 +datomic,ordinary,True,warm,page,100,3,1526.959,514.9026666666667,660.7636666666666,1527522.6666666667,0,0 +datomic,ordinary,True,warm,recursive,100,10,122.708,59.541599999999995,77.5375,167635.2,0,0 +datomic,qualified-prefix-10,True,cold,arrow,100,20,332.542,139.0146,310.4875,248402.0,5,1 +datomic,qualified-prefix-10,True,cold,cache-hit,100,50,1172.375,46.26582,177.8175,104345.6,0,0 +datomic,qualified-prefix-10,True,cold,continuation-roundtrip,100,3,3724.167,3490.9306666666666,4353.639,7604026.666666667,133,1 +datomic,qualified-prefix-10,True,cold,count,100,5,4313.833,4607.5582,6272.7916,10391120.0,128,1 +datomic,qualified-prefix-10,True,cold,direct,100,20,280.666,57.59165,85.37915,145106.0,0,0 +datomic,qualified-prefix-10,True,cold,exclusion,100,20,2663.542,74.51665,117.84795,176790.4,0,0 +datomic,qualified-prefix-10,True,cold,negative,100,20,84.75,54.69375,85.0479,137240.8,0,0 +datomic,qualified-prefix-10,True,cold,page,100,3,3942.25,2115.5693333333334,2834.4306666666666,4635306.666666667,89,1 +datomic,qualified-prefix-10,True,cold,recursive,100,10,202.25,122.425,170.60829999999999,268396.8,4,1 +datomic,qualified-prefix-10,True,warm,arrow,100,20,395.375,117.07915,186.4083,258397.6,3,1 +datomic,qualified-prefix-10,True,warm,cache-hit,100,50,314.042,42.76666,56.91833999999999,104345.6,0,0 +datomic,qualified-prefix-10,True,warm,continuation-roundtrip,100,3,3360.333,3372.514,7546.347,6484634.666666667,57,1 +datomic,qualified-prefix-10,True,warm,count,100,5,4339.417,4426.108200000001,5523.375,10700136.0,54,1 +datomic,qualified-prefix-10,True,warm,direct,100,20,489.167,66.0875,102.31039999999999,153762.0,0,0 +datomic,qualified-prefix-10,True,warm,exclusion,100,20,303.583,77.8896,100.45,190688.4,0,0 +datomic,qualified-prefix-10,True,warm,negative,100,20,88.5,59.97295,123.18335,148720.8,0,0 +datomic,qualified-prefix-10,True,warm,page,100,3,4374.167,1822.1526666666668,3148.153,3824546.6666666665,38,1 +datomic,qualified-prefix-10,True,warm,recursive,100,10,287.542,126.7709,706.2292,274578.4,4,1 +datomic,sparse-10,True,cold,arrow,100,20,179.333,99.81039999999999,129.84585,248424.0,3,1 +datomic,sparse-10,True,cold,cache-hit,100,50,379.875,36.89832,39.48332,104345.6,0,0 +datomic,sparse-10,True,cold,continuation-roundtrip,100,3,1806.25,1440.7363333333333,1679.2636666666667,3987458.6666666665,24,1 +datomic,sparse-10,True,cold,count,100,5,4229.167,4130.6334,4624.4834,11564680.0,129,1 +datomic,sparse-10,True,cold,direct,100,20,225.0,56.14165,72.30414999999999,143857.6,0,0 +datomic,sparse-10,True,cold,exclusion,100,20,147.083,63.675,78.54164999999999,178909.6,0,0 +datomic,sparse-10,True,cold,negative,100,20,62.875,51.4333,69.67710000000001,137287.2,0,0 +datomic,sparse-10,True,cold,page,100,3,1882.875,716.375,872.875,2045826.6666666667,13,1 +datomic,sparse-10,True,cold,recursive,100,10,131.0,62.6375,72.9292,170340.0,0,0 +datomic,sparse-10,True,warm,arrow,100,20,228.458,103.6375,177.26875,258445.6,5,1 +datomic,sparse-10,True,warm,cache-hit,100,50,226.709,43.67582,101.83082,104345.6,0,0 +datomic,sparse-10,True,warm,continuation-roundtrip,100,3,2178.208,2037.0,3267.8193333333334,4354306.666666667,24,1 +datomic,sparse-10,True,warm,count,100,5,5517.75,4999.808400000001,6587.5416,11857720.0,56,1 +datomic,sparse-10,True,warm,direct,100,20,138.333,54.71875,63.1625,154155.2,0,0 +datomic,sparse-10,True,warm,exclusion,100,20,315.0,66.90835000000001,77.53545,192043.6,0,0 +datomic,sparse-10,True,warm,negative,100,20,64.084,50.85625,63.552099999999996,148767.2,0,0 +datomic,sparse-10,True,warm,page,100,3,2173.833,935.9443333333334,1306.125,2228306.6666666665,13,1 +datomic,sparse-10,True,warm,recursive,100,10,104.875,70.6167,97.425,171436.8,0,0 +datomic,sparse-5,True,cold,arrow,100,20,501.459,99.12710000000001,135.1375,244727.2,3,1 +datomic,sparse-5,True,cold,cache-hit,100,50,907.583,37.6225,45.076660000000004,104345.6,0,0 +datomic,sparse-5,True,cold,continuation-roundtrip,100,3,1670.541,1176.222,1397.6806666666666,3224690.6666666665,14,1 +datomic,sparse-5,True,cold,count,100,5,3118.916,2863.3,3307.7332,8340224.0,67,1 +datomic,sparse-5,True,cold,direct,100,20,271.917,51.45625,64.77085,140168.0,0,0 +datomic,sparse-5,True,cold,exclusion,100,20,181.625,61.029199999999996,67.30625,174230.4,0,0 +datomic,sparse-5,True,cold,negative,100,20,91.667,46.38125,54.075,137288.8,0,0 +datomic,sparse-5,True,cold,page,100,3,2249.75,624.9723333333334,729.8473333333334,1788546.6666666667,9,1 +datomic,sparse-5,True,cold,recursive,100,10,280.458,63.6125,78.64580000000001,166456.0,0,0 +datomic,sparse-5,True,warm,arrow,100,20,209.958,73.475,86.55625,199153.2,1,1 +datomic,sparse-5,True,warm,cache-hit,100,50,144.083,39.635,46.0175,104345.6,0,0 +datomic,sparse-5,True,warm,continuation-roundtrip,100,3,1542.417,1420.764,2816.6666666666665,3322666.6666666665,4,1 +datomic,sparse-5,True,warm,count,100,5,3543.708,2930.2583999999997,3745.675,8640392.0,2,1 +datomic,sparse-5,True,warm,direct,100,20,128.084,53.2771,65.3625,151339.2,0,0 +datomic,sparse-5,True,warm,exclusion,100,20,282.875,64.2854,74.74375,187208.4,0,0 +datomic,sparse-5,True,warm,negative,100,20,111.708,50.958349999999996,58.45625,148769.2,0,0 +datomic,sparse-5,True,warm,page,100,3,1420.458,587.6666666666666,656.3053333333334,1731482.6666666667,2,1 +datomic,sparse-5,True,warm,recursive,100,10,111.25,61.575,71.5625,168650.4,0,0 diff --git a/docs/benchmarks/results/qualified-authorization-2026-09-05/provenance.json b/docs/benchmarks/results/qualified-authorization-2026-09-05/provenance.json new file mode 100644 index 00000000..38198f9e --- /dev/null +++ b/docs/benchmarks/results/qualified-authorization-2026-09-05/provenance.json @@ -0,0 +1,166 @@ +{ + "implementation_commit": "89e25860f198c1c00279e78e047d962ae902077b", + "date": "2026-09-05", + "measurement_version": 5, + "protocol": "One fresh nREPL JVM per backend, sequential cases, -Xms512m -Xmx2g; no concurrent test/proof/build work. Budgets unchanged from before the first measurement.", + "host": { + "model": "Mac16,9", + "cpu": "Apple M4 Max", + "memory_bytes": 38654705664, + "os": "macOS 26.4.1 (25E253)", + "java": "26.0.2" + }, + "datalevin_fork_commit": "a7e29c25a3034b54814e58a2d317e8c6877d1933", + "source_sha256": { + "deps.edn": "8376d4f89e1855c5e8e9f5913e11116bfc83dba682715304e84cc0c85f3f7039", + "docs/benchmarks/qualified-authorization-budgets.edn": "a89e56a844bb01e3bad7fe9e6f2f47eaec33a777816bfc26ec33ded91320bd49", + "formal/toolchain.lock.json": "eaf43c394eea2b44e61da58c128e542591ffe90d3968eac470d07eacba836dad", + "modules/eacl-caveats-jvm/src/eacl/caveats/jvm.clj": "90c702138611b88a8671fc116e7241d5215b57527af289731ccaebe586628c65", + "modules/eacl-caveats-jvm/src/eacl/caveats/jvm/program_cache.clj": "a1bce4190437e540441afef374b53e8530c9d6ffabe11c6b40c18e6ec3dfe60e", + "modules/eacl-datahike/src/eacl/datahike/backend.clj": "e37f8e3d437cd4144bbef31801529b46f3d1f2ae295cd37f1fa80544918dcd0d", + "modules/eacl-datahike/src/eacl/datahike/core.clj": "e1b53d84b962c9504b3acdbca89d9dca7af0f34a0adb01d9f99b6cf8a6790750", + "modules/eacl-datahike/src/eacl/datahike/db.clj": "f76b818002f0376aa3b96f5816a2e48d7f95a0bb2bd6eef42ff40b21bc8afe4e", + "modules/eacl-datahike/src/eacl/datahike/direct_membership.clj": "16bf3f21fe5231da6307d86e9f5ade7731ce27b8f258d4d5b784bfa84ff42814", + "modules/eacl-datahike/src/eacl/datahike/impl.clj": "b7569e5e420c88038604feb378a517a37907f441ed9cc01155bc1f9a47e25111", + "modules/eacl-datahike/src/eacl/datahike/integrity.clj": "b4468b60470b1374ba74cac3d55d2c217ca06f6defd0b0af6156b2a3bc3b80ab", + "modules/eacl-datahike/src/eacl/datahike/io.clj": "e9e2d715faab6314d5c120cd0faf7220ba5cbcb21b18933a2e210dd5fd5ce83d", + "modules/eacl-datahike/src/eacl/datahike/migrations/v7_to_v8.clj": "68d0464385dbe04f37edf8585ad99d76095ec0c112c896d871d380541729068c", + "modules/eacl-datahike/src/eacl/datahike/migrations/v7_to_v9.clj": "2051c10d59e20fa526a4b7074e59bc7b6eb5226f097eed210a9dfaff19e5a1b3", + "modules/eacl-datahike/src/eacl/datahike/qualifiers.clj": "aa8df2f1cca95c975a43f570143b0a33ec22c8dfa731c2de447f78c5d4609ce3", + "modules/eacl-datahike/src/eacl/datahike/safe_retraction.clj": "f815291c5bd0ab615925b802af1af3165be52ac7b24edf76ee74729d279d2a04", + "modules/eacl-datahike/src/eacl/datahike/schema.clj": "abb40b5609222f0a320d88854ad97a31e41785c839e669d139e42eed8ba88ba3", + "modules/eacl-datahike/src/eacl/datahike/storage.clj": "b136f2b5e59fc1471eed7ab83b4e66c65ed6ea643bb1aef8487055acc0521d5c", + "modules/eacl-datalevin/src/eacl/datalevin/backend.cljc": "ec62b01cf668014fe16df81662f0a9ac52eadeda4f3332fc46dcc22f320644ba", + "modules/eacl-datalevin/src/eacl/datalevin/core.cljc": "0eb49f82a2498c1fc7185131f862f76f565e6814b78e1f4ece3d6bc0b3e2d054", + "modules/eacl-datalevin/src/eacl/datalevin/db.cljc": "eee072b40db45285ba74c5b806ae21f4bb2eaf8a382dea91ca3cce7c8106af4f", + "modules/eacl-datalevin/src/eacl/datalevin/fork.cljc": "fa566f9e53f99e0ec970b9377a5743f4930b96e830dc6411983bbd6956c08a53", + "modules/eacl-datalevin/src/eacl/datalevin/impl.cljc": "f9e98889c5d7b8786e87326ec74ffc52c9f12f2437183bee255c637a93b752a7", + "modules/eacl-datalevin/src/eacl/datalevin/integrity.cljc": "89045f3ff9b7eb79bed6b57bcec41a2dfd2fa198c624d1cfd8e3c8a1b7aa6ba3", + "modules/eacl-datalevin/src/eacl/datalevin/migrations/v7_to_v9.clj": "0a2f52538efbda4397bc1d9c5993838f81fca33764135cc7594b7270b8951b61", + "modules/eacl-datalevin/src/eacl/datalevin/qualifiers.cljc": "ceae01082cb657863c329220487eacfffdc056d1bcff4ba176a858a47cd5c8f1", + "modules/eacl-datalevin/src/eacl/datalevin/safe_retraction.cljc": "d3ca57bd6ccff2972831b0687e41cd9ea8fc988c64b023e7126315ea317daf36", + "modules/eacl-datalevin/src/eacl/datalevin/schema.cljc": "ef798c13c3062181a9f66f0cde6c1b06a513e5fe19c2579a3f532c1498380a40", + "modules/eacl-datalevin/src/eacl/datalevin/storage.cljc": "981bc5f3f6a793628323175fee2e634e5628ddebee5ceeea4ad6c3e8af7b6b21", + "modules/eacl-datalevin/test/eacl/bench/qualified_authorization_test.clj": "7c6a20239b855510501d05bb64548e2d822411a86004d6a733e985022075f0bb", + "modules/eacl-datalevin/test/eacl/bench/qualifier_storage_test.clj": "5aef5c76d4f05afc933391f4cbf97fcacbf9da998895d801b441db8c9fa68e4e", + "modules/eacl-datascript/src/eacl/datascript/backend.cljc": "a33241c102ccbc7911e9da6749891b8d2927eb2f9615fd1ec9a9a0d5fa78ffa4", + "modules/eacl-datascript/src/eacl/datascript/core.cljc": "c3499246ef0c7e83cc99e4f2492c8108904f51423f51ea0298c685ae7a1901a7", + "modules/eacl-datascript/src/eacl/datascript/db.cljc": "fcfff81a471f67709d9c34b15394b2506f718d2c20dc754ad081046b2856a3a0", + "modules/eacl-datascript/src/eacl/datascript/impl.cljc": "b510f176cc6caedc61c194698c7892fca079eb6d9c88aa12e8ce6391e3f592ea", + "modules/eacl-datascript/src/eacl/datascript/integrity.cljc": "765b139b1e038c0c93a9b7b25271c85b93f495c96c0e555cd08aa69ffb44c508", + "modules/eacl-datascript/src/eacl/datascript/migrations/v7_to_v9.cljc": "dc931ea95bebcf872794b2211bdf72e9ba1dd17cb313f0159968527a66c70048", + "modules/eacl-datascript/src/eacl/datascript/qualifiers.cljc": "06bcee005ebd444f2e425d6f28a6c8dedb53034069d4da3bc7d70c1ef1fa047e", + "modules/eacl-datascript/src/eacl/datascript/safe_retraction.cljc": "ab844047466284649deb6ee16b07ba0592f7ac5925df8725f83051c6e40e5235", + "modules/eacl-datascript/src/eacl/datascript/schema.cljc": "ead4ee2cc14ad4d06df31606f40530b7145884c759df4e16d42cfe49cc2d4de1", + "modules/eacl-datascript/src/eacl/datascript/storage.cljc": "3d4379a9635d60083742991626ab728ef8dbfb5ad769d125313cf83a058c46e1", + "modules/eacl-datomic/src/eacl/datomic/backend.clj": "ec53e7d41207e94b545d8b1258b6eb6f65462a34720277c1076ecf7a64bbe09e", + "modules/eacl-datomic/src/eacl/datomic/core.clj": "bf478146977ef1294cb52a9ab8eb9801acbd976a43e62ec8299501ad6d471504", + "modules/eacl-datomic/src/eacl/datomic/db.clj": "78220639d329006945748a800651330fc741af2a553f699e2dfa5b8ff65101eb", + "modules/eacl-datomic/src/eacl/datomic/impl.clj": "4d794c4838ba3018422990ed1b219c58e20f5df79d18c8983538f9a67f16224a", + "modules/eacl-datomic/src/eacl/datomic/impl/base.clj": "a4fb7dbbe27fad7da240935924340b7c6ed44b7d0c3473e99d9966e67af64086", + "modules/eacl-datomic/src/eacl/datomic/impl/indexed.clj": "60b021bbe200e4d2d0a8a182437fe30b052e16d67361fdb49e5f06c8a7ef147d", + "modules/eacl-datomic/src/eacl/datomic/integrity.clj": "44ba794a93101028c2bc18b22e3945e4298d6429ed074ed7c6203209fb6f7ece", + "modules/eacl-datomic/src/eacl/datomic/migrations/v7_to_v9.clj": "e869a3ad025e71c2b8643f02665c6c81ea3792dd05857e0ee56e377365da101d", + "modules/eacl-datomic/src/eacl/datomic/qualifier_functions.clj": "0a7f93c1283b78647fa0843e2c6e17d29d933a4ed9c0786030dabd6268e32dd8", + "modules/eacl-datomic/src/eacl/datomic/qualifiers.clj": "98914a1b5f171706dfe00e9784ddf7f54e659526824472079bf088e6d078fd3f", + "modules/eacl-datomic/src/eacl/datomic/safe_retraction.clj": "3495826c47331bba9b9bad84a4be5951d72444a4b3b6f6abc8b505a13581037c", + "modules/eacl-datomic/src/eacl/datomic/schema.clj": "d0a49911449e926370ea08acc8b73eebbd809bfc63dc654dc7399c992e2301c9", + "modules/eacl-datomic/src/eacl/datomic/storage.clj": "f3c737891c3b9944f79004317b8ad591c90b1e67d63474d8395dfda48bf00183", + "modules/eacl-datomic/src/eacl/migrations/v6_to_v7.clj": "32c706c5c90efcbde879c45e840fd363604decef1c8b71e9211c641c31d9d094", + "modules/eacl-datomic/src/eacl/migrations/v7_to_v8.clj": "99ab46f817143abd8a0a50a44c495c24f4b6ba011b16f4f320db4c32cbcc4a6f", + "modules/eacl/src/deps.cljs": "e9464618773d614cdc72ae5a80eb4cf0f712ae052edaecc41f0d5d47fc4d4ef7", + "modules/eacl/src/eacl/authorization/batch.cljc": "a280503cbe177c3e740a13838d33090f4b44f06b95903a80dbab8d10cf7a95e5", + "modules/eacl/src/eacl/authorization/clock.cljc": "f16b9e291c64396b107865c827c1359ef97ba77c9890bebad0834440c297b660", + "modules/eacl/src/eacl/authorization/context.cljc": "933aad77e5eb10766e4990c9efdc2412b6b469e3789d8a8bc051b4ae84116c53", + "modules/eacl/src/eacl/authorization/data.cljc": "4400ae2507946bf0eeb9494d9b8e1fde6e8caf97301ce811790add3b402c9031", + "modules/eacl/src/eacl/authorization/evidence.cljc": "c0d3b99080e254526d8eea036f6ed0aca5a3bed2e50e1f1187a62997c61fd521", + "modules/eacl/src/eacl/authorization/evidence_index.cljc": "45c7397b575564487a851f71f6b071de0b983c3e984511f15bf3ab621b462757", + "modules/eacl/src/eacl/authorization/filters.cljc": "860501100d712776e65bb8fc1951407d6f4f9a98cf1ab7ab6bcf583662f8c4da", + "modules/eacl/src/eacl/authorization/qualification.cljc": "6af83de843c3c1e326d9d74912f9496794f2082ec91bb71cd36262a11df00675", + "modules/eacl/src/eacl/authorization/qualifier_cache.cljc": "9f14da0aed30f64701ee301c56cf508de26adb5398bd2c6a9b65bb12d315bc62", + "modules/eacl/src/eacl/authorization/result.cljc": "2d21873e9ffdbd4f05fb3ab531b8202862b4e4f35c475e3b548ea3c5c2c51e5c", + "modules/eacl/src/eacl/authorization/temporal.cljc": "5e22a9f45ae139c5e19496d96c36fff6052532e67b82fef8872dbba0ff9c1f10", + "modules/eacl/src/eacl/backend/direct_membership.cljc": "925928cb2592d7b095cd307510243b10c59e9905d2a1fc909f9b9e3312e071dc", + "modules/eacl/src/eacl/backend/source.cljc": "3d1c312704c8fa0843b723d3fde9a99bd99a43d646d8582a8628fb0faf90c213", + "modules/eacl/src/eacl/backend/v8.cljc": "ab1b6ea5ff018e419f70ebb7b3569ac87e7eb1568d39b00bb919a95b1b3f5041", + "modules/eacl/src/eacl/backend/writer.cljc": "9af3fb0126894c9b83deb28317cc48ebb2501adafcd3ce63746fc6a17f857bb2", + "modules/eacl/src/eacl/cache.cljc": "76fa911d3c6bad39553a3c9a67ade6272b17871ecc2518308326b8b5c438f98f", + "modules/eacl/src/eacl/cache/derived_schema.cljc": "6ac1785aca5fb4a929101658220d2ec0801eea61b2f744d0c5f60c32c070adb0", + "modules/eacl/src/eacl/cache/key.cljc": "563126ee5e71bc92636497e6fdc1630143508e39c3df500660a1cc33639b825b", + "modules/eacl/src/eacl/cache/standard_lru.cljc": "e5f8a6dbea3cbec26cf249b35eb98e56e4629e1ae27d4b285eeae33c3a033514", + "modules/eacl/src/eacl/cache_identity.cljc": "8f0d895edb16c436f33a887b370d90b2b0599dcc9cf3f2e2c08dff6441203493", + "modules/eacl/src/eacl/causal_token.cljc": "ae4a4806fb5ff253cfc6ce74a6bef7b77a2912a8ff5cb095f3cd3871811d66c2", + "modules/eacl/src/eacl/caveats/definition.cljc": "422464af8b143bafd0fd3ea2b5b23b27866d6fb4b7ada9919e90428308d42f2b", + "modules/eacl/src/eacl/caveats/evaluator.cljc": "45607939c720e8667d92745f398ef142283cb172a63f4f2a99302090b06a9f4c", + "modules/eacl/src/eacl/caveats/partial.cljc": "df7ca88eb4bdf85a8fa726965e73430159cabe61f0dc389d7e50e5b20b2ad226", + "modules/eacl/src/eacl/caveats/plan.cljc": "d5f42524e58a7d7d4fcec5b3b80531e492c45ab62dae253c18547d8a0aebbaf3", + "modules/eacl/src/eacl/caveats/schema.cljc": "aa0964f9af52c17caa3907bd12890016f0f1f1718fe4f7cc1ffb16e28e77a030", + "modules/eacl/src/eacl/caveats/values.cljc": "904c3d7b3afde845c305b4bea3ae09ea4938ef40e83c9947f8eaeccc99bc683d", + "modules/eacl/src/eacl/client/lookahead.cljc": "013d4301ee19f07f2c59f41025f07276a0aef25a3862526362d36752fcef7a61", + "modules/eacl/src/eacl/client/orchestration.cljc": "9f109aee0547162233d5fd16782bf46e316450eb93c4351ec8153f09c68e0622", + "modules/eacl/src/eacl/client/range_reuse.cljc": "cc5a72f5addbcfb8cedc47cfa09ab610c8d96e71ced2bf4399592746dc63a1b4", + "modules/eacl/src/eacl/consistency.cljc": "f0261ca5afcf93470d67212194f26d1b466d9761883009dc10876b140268dd90", + "modules/eacl/src/eacl/continuation.cljc": "3b78e2f8c7cecaf76fffd7163dc99ec9af37b19dfb34df0381fe6fd03fcc65f9", + "modules/eacl/src/eacl/core.cljc": "e2a24dcc9fea502dd34ecb43c47eb0512a993c7367beb1e533e98770362e2eb4", + "modules/eacl/src/eacl/cursor.cljc": "7a389c7439a0bceb325640833cef036b363694a454f2d05e44a683dcca1b462f", + "modules/eacl/src/eacl/engine/least_path.cljc": "86c072243caaab00b7100ae83e5a645887e7f3f9172e684f9f255413430fb980", + "modules/eacl/src/eacl/engine/physical.cljc": "eeff89d82f8835c8330739c6992f326ece7a8cb5fedd6c575b8cd6d6dca0f5c9", + "modules/eacl/src/eacl/engine/portable_decisions.cljc": "4aebc9f983561897171fad3ca850bf6fe90aaf3ee84cb12c32c415f887b5f0e5", + "modules/eacl/src/eacl/engine/portable_indexed.cljc": "111ab067e88bd5ae81e1073385fd93e1014e70a773276290078a8259285b6c17", + "modules/eacl/src/eacl/engine/relationships.cljc": "fa310c2c014c809b041baf06f284e4d8be4074433b422f3f494c4a153008412e", + "modules/eacl/src/eacl/engine/scan_cache.cljc": "46eeccfae48546223bb4fa791006ec1c46c7291b62b513462099adf0c0c844a9", + "modules/eacl/src/eacl/engine/sealed_plan.cljc": "c28581b62fbd51642bbf517cf5a1e6fb34398ee61721c406cb36ce2545a3aeee", + "modules/eacl/src/eacl/engine/stable_page.cljc": "9dafd3873a6e4ce2e25ba26d6788ad90670636194b81f38b4770d073245c77c3", + "modules/eacl/src/eacl/engine/stable_reducer.cljc": "4d7ac641a6587d7ef370db3d48e3138d7c9c6760bf547b00602071f056190f1b", + "modules/eacl/src/eacl/engine/stable_route.cljc": "f01731d62c851b238562453f6dce277f47962f313ca22c5bfcfb0d125c8da6ea", + "modules/eacl/src/eacl/engine/v8.cljc": "95aad961a1922f7bf6fb66844d772a36fab1da89018f0e559498b9c79643c556", + "modules/eacl/src/eacl/exact_integer.cljc": "96d54a830b6d98ab50102f471a8fc16e267cfa78c89de46b4c7c6618dd227c0c", + "modules/eacl/src/eacl/execution.cljc": "b5bc961ec642077a9acf35f92f4d93a10e12f678991dd0a7e206e0e08a85da94", + "modules/eacl/src/eacl/formal/generated_runtime.clj": "77c84a3dfd074f623ae5833cfde49317c276573557dbcdfad7e874f6df2eafb5", + "modules/eacl/src/eacl/formal/production_kernel.clj": "97e040c352acd36c22f857ab306c13a412806e9251e05369639669ce87e107aa", + "modules/eacl/src/eacl/formal/production_kernel_cljs.cljs": "410f605fc912c384bfc1f8ffeec3757f9ffe42554654afa3e3b6bc60df062495", + "modules/eacl/src/eacl/operator/batch_schedule.cljc": "3a89d3a7f78be9f15dcba94aed249a613ef16ed7e08b786b26d7206a22f0597f", + "modules/eacl/src/eacl/operator/bitmask.cljc": "adb67a16d4f513be6c2bdf39fe10f3d6b64ad833b2a2c5cc7f0d12e055295e81", + "modules/eacl/src/eacl/operator/cover_plan.cljc": "3d40bf5e7f304a9789471f6425beba4c3f6d8405d285789b3088ee293d41548d", + "modules/eacl/src/eacl/operator/cursor_scope.cljc": "859138c287b51000f4f2ec0c8f0c34aef68021fd7b9f221137107a6d1aa68d6a", + "modules/eacl/src/eacl/operator/evaluator.cljc": "3b6a78354b2821b70862f83e60ff0079e819db7ec9dcb7c570464dd276753c4c", + "modules/eacl/src/eacl/operator/lookup.cljc": "3ca0a2ad0893cf828f73c272824f85311272adf2f3f06876782c8c4f3b85e9e1", + "modules/eacl/src/eacl/operator/plan.cljc": "239e6003a9ddbbfebac3c282275fe2d629435f9f79eaff14c5cada32724bde70", + "modules/eacl/src/eacl/operator/recursive.cljc": "5a4e5e530fc17cfc5dcddae71b91a1b20c3ad279f745e02113b6cccc1066e02b", + "modules/eacl/src/eacl/operator/seekable.cljc": "519e51def3b278084a06370f454e923343f59b91495c11eff7c756583a9cc38b", + "modules/eacl/src/eacl/operator/vector_evaluator.cljc": "27a5e9751bad9595ca202d17ae8d2763bbae492d112711e51d5efec178e58a71", + "modules/eacl/src/eacl/permission_tree.cljc": "76dd0a3404648838d8b86e7c097bf1880e4c08a64b4e92c0e9aec64f4ada3031", + "modules/eacl/src/eacl/proof_frame.cljc": "b2b850eb124e35b90e541cdb3b94d0c10f7aba88ffeb4a5d763b0bc4c7425495", + "modules/eacl/src/eacl/relationships/edge.cljc": "b72a071c2cc8ef39494ff19dc20123e995894b5b6cc0fa1e70ae0e8ff152879d", + "modules/eacl/src/eacl/relationships/endpoint_pair.cljc": "a097b8d9c6ca96f56050a9d6d60448554b09337f6fc3c7f5de5797cffa6b7d8a", + "modules/eacl/src/eacl/relationships/filters.cljc": "ecdd13f6f4a47b1383786631c2a1635b172780a52c47da5ac9078d1b0fa2cae8", + "modules/eacl/src/eacl/relationships/inspection.cljc": "6e5f57e9b847d1040f51845b4419613320296867f718828035a19043758f0524", + "modules/eacl/src/eacl/relationships/legacy_v7.cljc": "19822c7b1da91dd2fc2b71a4cf25df1e0f673d70d474fbee7ea60bddceeb1ee1", + "modules/eacl/src/eacl/relationships/mutations.cljc": "d34660b98776f6fa54422d44be0e6625f151c9c4d7722f96913be4479b7f8cd3", + "modules/eacl/src/eacl/relationships/qualifier.cljc": "1cccb1a0eab8e8e3e2ca8f8e9467b9ecce1ba3e3be0090c064d91c64dce04979", + "modules/eacl/src/eacl/relationships/qualifier_integrity.cljc": "ae4116d03045332c3ba40c72881ac9f95a888d71256ca74c01a7b6ddefade4d6", + "modules/eacl/src/eacl/relationships/safe_retraction.cljc": "87c8b8a482d43ea4aef20db80f2ad876e5cce5f3942bdd9fe7a70e4680a52493", + "modules/eacl/src/eacl/relationships/staged.cljc": "9d88472e8efd4d9ddab4a388294fb62b106a9cfc535947f62f7bd66639bdad47", + "modules/eacl/src/eacl/relationships/storage.cljc": "3ab17adb98f5c1b8f7af5df7e68eb83fd86b59d804f6fc831e87b8334164981b", + "modules/eacl/src/eacl/relationships/upgrade.cljc": "a912becdb8c2ec4d317442541275b92e2e23e1c02f939c1100bbd2c9fb1620dd", + "modules/eacl/src/eacl/relay.cljc": "fa9279e2ff726d13d6e9507aca4dbe0aeef255cb95b5a01a1919a189d53a55d0", + "modules/eacl/src/eacl/request/context.cljc": "d98e5797fbe227c620e493e8d8dbb9ad426c82998ad11924fb6ef4cb6b3e0c69", + "modules/eacl/src/eacl/request/counters.cljc": "9f0b59809a46926b7f9928bac7077e0e6d8be548f4d6dfb7f636b77aba7d889f", + "modules/eacl/src/eacl/schema/errors.cljc": "d1b1a2c1968f9eb4b7038cae25081df8998985c3569295f166d73a8db8c4ba20", + "modules/eacl/src/eacl/schema/expression.cljc": "3b96fb53c083db22d1260547e05ca0d350d055d1f8cc92e2e5cedace2e6fb01c", + "modules/eacl/src/eacl/schema/expression_graph.cljc": "4466904b5372691039c7599037086a5a6a978886e38f649dc0f9862e814f242f", + "modules/eacl/src/eacl/schema/expression_limits.cljc": "d56b4120f45e06de8c0f3d038840469d749bdc0869d2c1deb7a5968cd9ea8fa8", + "modules/eacl/src/eacl/schema/expression_persistence.cljc": "a2db0da644f77543c339106027aa17abc2631903a618f0520212351c42916868", + "modules/eacl/src/eacl/schema/expression_policy.cljc": "a00f6e82b8df6295c22e42e03be94bfdeb2512a6b5f00f950ea9a8d5fc520ac2", + "modules/eacl/src/eacl/schema/expression_resolver.cljc": "beafc679fc8b8b25bc6fd827b2dea6ae15046afc11daf505cb347d38f6f88849", + "modules/eacl/src/eacl/schema/model.cljc": "656c5800e11cf1f05dbcbbf2c60b5013e46168a77b92011084f109fad87e0db3", + "modules/eacl/src/eacl/schema/qualification_admission.cljc": "f525a0d7faed752753c254df2de19548ff5c9b5dd55326c97a1db7af5963a34d", + "modules/eacl/src/eacl/schema/relation_allowance.cljc": "91a9cd9490ebe8d965f2e06c70b9c5b32234f17f60145a2ae5a9824b0b5ddc58", + "modules/eacl/src/eacl/schema/replacement_plan.cljc": "1810c632ab87687f606a698d89d14a84ef16143044df2635782a2fc3a9c8995f", + "modules/eacl/src/eacl/secure_format.cljc": "c9e9358837d4ff4f3e1054744514f5d30a4041201de94b4d39bd5e617554abc1", + "modules/eacl/src/eacl/spicedb/consistency.cljc": "4cc2bcf5655681b8fca7dc874855f90a91d34bba93022d86531ee83425324f98", + "modules/eacl/src/eacl/spicedb/parser.cljc": "402858792630e72902983f186803e0845109eb03d86b5ca996196dc3063a996f", + "modules/eacl/src/eacl/subproblem_cache.cljc": "b53f4c7dfd7a89922b958bf5eb142d35dec57d240cad3cd9eb3c6e4593b3097e", + "modules/eacl/src/eacl/verified_kernel.cljc": "7549ee4d136190b76db7bcfc5e830582a09e6750a8a059080e86ac9e842e9a9d" + } +} diff --git a/docs/benchmarks/results/qualified-authorization-2026-09-05/raw-reports.tar.gz b/docs/benchmarks/results/qualified-authorization-2026-09-05/raw-reports.tar.gz new file mode 100644 index 00000000..f5a9b70b Binary files /dev/null and b/docs/benchmarks/results/qualified-authorization-2026-09-05/raw-reports.tar.gz differ diff --git a/docs/caveats.md b/docs/caveats.md index bbfb1c1b..03c6c2c3 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -1,11 +1,13 @@ -# Caveat and qualifier foundation +# Caveats and expiring Relationships (v9) -This foundation follows the v8 qualifier-eid storage change (storage ABI 9). -It persists named Caveat definitions and provides portable validation, partial -evaluation, an optional JVM evaluator, and explicitly staged qualifier writers. -Qualified authorization and expiration take effect only in the subsequent -Phase 3 serving change. A Phase 2 client rejects non-nil qualifier references; -never seed them into a serving Phase 2 database. +V9 adds named Caveats, conditional permissions, and an exclusive expiration time +on each Relationship. It uses the qualifier-eid storage ABI 9 landed in v8. +Qualified serving is enabled by default in v9. V8 and foundation-only readers +reject non-nil qualifier references and cannot serve an activated store. + +The [executable example](examples/caveats.clj) covers conditional checks, +expiring grants and bans, pinned time, live-cursor restart, renewal, composed +writes, and deletion using DataScript and the optional JVM evaluator. ## Named definitions and schema admission @@ -18,17 +20,17 @@ caveat in_region(region string, accepted list) { definition user {} definition doc { - relation viewer: user + relation viewer: user | user with in_region permission view = viewer } ``` -This complete schema is accepted by Phase 2 `write-schema!`; the unused named -Caveat does not affect ordinary authorization. The parser also understands -`relation viewer: user with in_region`, but public schema admission rejects -that branch until Phase 3. `user with in_region` requires the Caveat; -`user | user with in_region` permits either plain or Caveated input. An -expiry-only qualifier requires a plain alternative. +Use `eacl/write-schema!` to install this schema. `user with in_region` requires +that named Caveat; `user | user with in_region` permits either plain or Caveated +input. An expiry-only qualifier requires a plain alternative. Admission checks +an evaluator matching `eacl-cel/1` whenever any Relation names a Caveat, including +empty and unvisited Relations. Unused named definitions and expiry-only schemas +do not require an evaluator. Names and typed parameter names use ASCII identifiers, up to 64 bytes. CEL keywords/type names and the `__eacl_` prefix are reserved. Definitions have @@ -46,6 +48,86 @@ including an unattached preparation. A parameter change must remain compatible with every retained bound context. Native schema/reference fences reject competing writes; historical schema reads retain the selected definition. +## Write and check a qualified Relationship + +Create native endpoint entities using the backend's normal transaction API, +then pass public Relationship values to EACL: + +```clojure +(def alice (eacl/spice-object :user "alice")) +(def report (eacl/spice-object :doc "report")) +(def grant + (assoc (eacl/->Relationship alice :viewer report) + :caveat "in_region" + :caveat-context {"accepted" ["za"]} + :valid-until-ms deadline-ms)) ; exclusive UTC epoch milliseconds +(eacl/create-relationship! client grant) + +(eacl/check-permission client + {:subject alice :resource report :permission :view + :caveat-context {"region" "za"}}) +;; includes {:allowed? true :permissionship :has-permission} +``` + +Omitting `region` produces `:conditional-permission`, `:allowed? false`, +`:missing-fields ["region"]`, and a canonical `:residual`. Supply the missing +context in a new request. `can?` returns true only for a definite grant. +At `deadline-ms`, this grant is inactive without a database write or collector. +An expiring ban in `viewer - banned` can conversely turn denial into permission. + +Each Relationship has at most one named Caveat. Identity is still subject, +Relation and resource; differing context or expiry does not create an independent +grant. `:create` conflicts with an existing identity even after expiry. Use +`:touch` to renew, shorten, change context, or remove qualification: + +```clojure +(eacl/write-relationship! client + (assoc grant :operation :touch :valid-until-ms renewed-deadline-ms)) +(eacl/write-relationship! client + {:operation :touch :subject alice :relation :viewer :resource report}) +(eacl/delete-relationship! client grant) +``` + +Batch updates use `{:operation :touch :relationship grant}` entries. Pass +`{:updates [...] :tx-data [...]}` to `write-relationships!` to commit application +datoms with the final publication. Identical intents coalesce; conflicting +qualifier values on one identity fail before allocation. Application datoms +cannot alter EACL's protected state. + +For caller-managed native transaction composition, prepare each qualified value +with `prepare-relationship!`, acquire a snapshot **after** preparation, and pass +its opaque `:prepared-qualifier` handle alongside the corresponding update to +`tx-relationships`. Submit the returned native tx-data, then release the +snapshot. Preparation is inert; `discard-prepared-relationship!` removes an +unchanged, unattached preparation. The executable example shows this sequence. + +## Public results and errors + +Default lookups return definite `SpiceObject` results. `:result-policy :detailed` +returns `{:object ... :allowed? ... :permissionship ...}` items, including +conditional results and their missing fields/residuals. Detailed counts add +`:definite-count` and `:conditional-count`; they sum to `:count` and exclude +lookahead. A conditional interior edge may still compose into a definite result. + +| Outcome or error | Meaning / caller action | +| --- | --- | +| `:has-permission` / `:no-permission` | Completed decision at the captured basis and time | +| `:conditional-permission` | Supply missing context in a new request; do not treat as a grant | +| `:eacl.caveat/invalid` | Invalid context, definition, profile, or resource bound; inspect the typed reason | +| `:eacl/invalid-relationship-qualifier` | Invalid public Caveat/context/expiry input | +| `:eacl/relationship-conflict` | Identity already exists for create, or is absent for replace | +| `:eacl.caveat/evaluator-unavailable` | Install/supply a matching evaluator before serving the schema | +| `:eacl/unsupported-capability` | The backend/writer lacks the required certified operation | +| `:eacl.authorization/evaluation-failure` | Encountered authoritative qualifier/Caveat fault; detailed reads fail | +| `:eacl.schema/relationship-qualifier-in-use` | A schema change would invalidate retained Relationships, including expired ones | +| `:eacl.pagination/restart-required` | Live temporal certificate ended; begin a new lookup without the cursor | +| `:eacl.pagination/invalid-cursor` | Authentication, scope, or envelope mismatch; do not silently reuse its boundary | + +`can?` converts authoritative qualified evaluation failures to false for Boolean +compatibility. Invalid requests, cancellation, execution limits and backend +errors still propagate. Detailed checks and collections expose faults; they +never erase a malformed subtracting edge into an absent ban. + ## Profile 1 values and operations | Type | Portable Clojure/CLJS value | @@ -78,7 +160,7 @@ These are explicit profile limits, not full CEL or SpiceDB compatibility. | Entries in each list or map | 128 | | Total context entries / canonical payload bytes | 1024 / 16384 | | Conservative evaluation work units | 1048576 | -| Cached programs / simultaneous program builds | 256 / 4 | +| Cached portable/native artifacts (shared capacity) / simultaneous builds | 256 / 4 | Time values use exact epoch milliseconds from -62135596800000 through 253402300799999. Input, encoded payload and plan limits are checked before @@ -152,14 +234,10 @@ the owned qualifier. A missing non-nil target is corruption, never nil. | Datahike, including attribute refs | Prepared reference | Native assertion transaction | | Datalevin maintained EACL fork | Inline allocation | Exact snapshot; no creation-version claim | -`eacl..qualifiers/writer` constructs an internal staged writer. -`eacl.relationships.staged/prepare!` creates an inert qualifier and opaque handle -bound to one writer, source, Relationship identity, schema generation and exact -qualifier facts. `plan-current` returns composable fenced transaction data while -releasing its owned snapshot before commit. `write!` prepares automatically on -backends that require it. Caller datoms cannot bypass protected EACL attributes -or execute arbitrary transaction functions. These APIs are for non-serving -fixtures and staged preparation only in Phase 2. +Public `write-relationships!` chooses the certified publication strategy. The +internal staged writers enforce native schema/Relation fences, endpoint identity, +immutable qualifier facts and caller-datom restrictions. Use the public +preparation/planning APIs above for transaction composition. An unattached preparation cannot authorize. Publishing it must use its concrete native qid and atomically attach both halves. Native commit-time facts and @@ -203,6 +281,242 @@ lifecycle/security/watermark configuration, and an admitted write token from physical schema preparation. Reuse the normal backend upgrade procedure when opening an existing protected store; do not bypass its write policy. -`valid-until-ms` is typed and immutable in this phase but entirely inert for -authorization. Time-aware checks, conditional permissionship, request context, -qualified schema activation, and cache/cursor validity enter together in Phase 3. +## Schema replacement and backend admission + +Committed and speculative schema replacement use the same qualified admission +checks. The optional JVM module registers its matching default; CLJS requires +an explicitly supplied, independently certified matching evaluator for Caveated +Relations. Expiry-only Relationships remain portable without an evaluator. + +Datomic and Datalevin advertise certified inline publication. DataScript and +direct-writer Datahike advertise certified prepared-reference publication. +Unsupported writer topologies fail with `:eacl/unsupported-capability` before +qualified authorization cache lookup or schema publication. + +Changing a Relation's Caveat alternatives preserves its native eid. Existing +Relationships, including expired rows, must satisfy the new alternatives; +otherwise replacement fails with +`:eacl.schema/relationship-qualifier-in-use`. Concurrent Relationship changes +are fenced at commit. Datalevin performs schema validation and generation reads +inside one owned snapshot, then releases that snapshot before submitting the +schema transaction. + +## Physical Relationship inspection + +When qualified serving is enabled, `read-relationships` defaults to +`:relationship-state :stored`. It returns retained rows with their optional +`:caveat`, `:caveat-context`, and `:valid-until-ms` metadata, including expired +rows. Select `:relationship-state :expiry-active` to exclude rows whose deadline +is at or before the request's captured trusted time. Responses label the selected +state and `:evaluation-time-ms`. Explicit snapshots retain their captured time. + +```clojure +(eacl/read-relationships client + {:resource/type :doc :resource/id "report" :first 20 + :relationship-state :expiry-active}) +``` + +These modes inspect storage and expiry without evaluating Caveats. A row with +an unsatisfied or conditional Caveat can therefore appear in either view. +Use the existing `:authorization` filter or an authorization operation when a +permission decision is needed. Physical inspection alone does not require a +Caveat evaluator. Qualifier corruption remains a typed fault. + +Expiry-active filtering spends the existing candidate-work budget on every +examined row, including skipped expired rows. A bounded page may be empty and +still return continuation state. It does not scan an arbitrarily long expired +prefix to fill a page. Forward, reverse, exact and partial endpoint queries +preserve qualifier alignment through public rendering and cache reuse. + +Expiry retains Relationship identity. `:create` conflicts with an expired row; +`:touch` renews or shortens its deadline by replacing the immutable qualifier. +Omitting `:valid-until-ms` on a replacement removes expiry. Omitting all qualifier +metadata returns the row to the nil-qid representation when the Relation permits +plain input. No collection job is required for expiration correctness. + +## Decoded qualifier cache + +Qualified clients have an optional bounded local decode cache. Configure +`:qualifier-cache {:max-entries 256}` or disable it with `:qualifier-cache false`. +The capacity counts both exact-basis and content-proof indices together. The +client's `eacl.cache/no-cache` setting disables this tier too; request +`:cache? false` bypasses it, while `:populate-cache? false` permits retained reads +without publishing new entries. Source lifecycle rotation replaces the tier. +It is private decoded data and is not part of exported authorization snapshots. + +Exact-basis hits omit qualifier refetching. Across bases, current adapters compare +all native qualifier fields, the marker assertion version when available, +named Caveat definition, owning Relation content, and source lifecycle before +reusing the decoded structure. Datalevin uses its complete content proof without +claiming a native creation version. A publication-capable backend does not by +itself certify that every external writer preserves immutable qualifiers, so +cross-basis hits still perform bounded native content reads. This detects +unstamped in-place mutations, deletion and entity reuse without scanning the +graph for reverse ownership. Expiry and Caveat evaluation run for each request; +neither Boolean decisions nor conditional evidence enter this decode cache. + +## Point-answer validity intervals + +Point checks retain their completed evidence with its original evaluation time, +exclusive deadline, completeness and permissionship. On the same immutable +basis and canonical request/evaluator scope, a later check may reuse that answer +only within its certified interval. A permanent decisive witness can provide an +unbounded interval. An incomplete certificate permits only the original time +and exact basis. Cache key separation still covers every supplied context field, +including fields not used by the selected Caveat. + +Reaching an interval's deadline is a cache miss even when the entry remains in +memory. EACL recomputes and may replace the expired interval; an expiring ban can +therefore change a denial into a grant without any database write. Replacement +compares the expected immutable entry atomically. An older pinned snapshot may +recompute its original answer but cannot displace a newer interval under the +same cache key. Token lifetimes and cursor continuation remain separate checks. + +Bundled backends use exact-basis qualified answer reuse because their raw-writer +interfaces do not provide a complete immutable-writer dependency proof. Lookup, count, range, and checkpoint caches keep their conservative +exact-time scope. Their retained certificates support the live continuation +checks described below. + +## Trusted clocks and Peer skew + +Each top-level operation captures one trusted evaluation time and uses it for +all traversed edges, batches and retained evidence. Request Caveat context cannot +override this time. The default uses wall-clock UTC epoch milliseconds with a +process-local non-decreasing high-water mark. A configured client `:clock` +function gets its own high-water wrapper. On a backward clock step, time holds +at the previous accepted value until the clock catches up; an already expired +grant cannot revive within that clock's lifetime. + +The high-water mark is not durable and does not synchronize Peers. A slow Peer +can expire a grant late, and a fast Peer can expire it early. Keep serving clocks +synchronized, monitor offset and backward steps, and reject operations through +your trusted clock/service health policy when skew exceeds your application's +acceptable uncertainty. EACL adds no hidden grace period or distributed clock +agreement. A process restart or a newly configured clock must not be treated as +proof of temporal continuity. Causal tokens certify data visibility, not equal +wall time between Peers. + +Explicit snapshots pin both database basis and evaluation time. They deliberately +support historical/simulation decisions, including a grant whose wall-clock +expiry has since passed. Use client-targeted checks and lookups for current +access control. A pinned snapshot is not a live authorization lease. + +## Qualified cursors + +A lookup on a client uses live time. Each resumed request captures a fresh trusted +sample and checks it against the cursor's complete exclusive validity interval. +At the deadline, or after it, EACL returns `:eacl.pagination/restart-required`. +Start a new lookup without `:after` or `:before` to obtain the current view. Keep +the desired filters and context; the new result sequence can include an object +that an expiring ban previously hid before the old boundary. EACL never silently +restarts or applies that old boundary to the changed temporal view. + +A lookup on an explicit `eacl/snapshot` uses its pinned basis and captured time. +Its cursors continue that historical view even after wall-clock expiry. A pinned +snapshot can therefore preserve a past grant; use client-targeted lookups for +current access-control decisions. Live and pinned cursor modes are distinct. +Changing the complete Caveat context, evaluator identity, result policy, or mode +requires a new lookup. Cursor authentication and token TTL apply independently. + +Certificates cover examined candidates, including skipped rows, conditional +results and subtracting evidence, together with retained frontier, lookahead and +checkpoint state. Cache and range reuse preserve those certificates. A missing +or incomplete retained proof allows continuation only at its original evaluation +time; a later live request returns `:temporal-certificate-incomplete` as the +restart reason. EACL does not scan an unseen suffix merely to manufacture a proof. +Stored physical Relationship inspection is timeless; expiry-active inspection +and authorization-filtered inspection retain their observed expiry deadlines. + +## Qualified cache scope and reset traces + +Qualification-dependent answers retain the complete immutable basis, canonical +whole request context and evaluator identity. Exact basis identity covers the +selected schema, Caveat definitions, Relation allowance and qualifier contents. +Conditional result categories and evidence remain part of validated values; +collection values additionally require their versioned temporal certificate. + +| Retained value | Time and qualification scope | +| --- | --- | +| Decoded qualifier | Exact basis or complete native content; never an authorization answer | +| Raw scan response | Structural identity and aligned qualifier refs; independent of time and context | +| Derived schema plan | Schema generation and structural input; independent of request time | +| Point answer | Exact basis and context/evaluator, with resident interval acceptance | +| Lookup, count and range | Exact basis, context/evaluator and captured time, with retained certificates | +| Denotation and recursive checkpoint | Exact qualification scope and canonical evidence certificates | +| Stable page checkpoint | Exact scope and a validated certificate for the complete retained state | +| Rendered page | Exact request/time/mode and authenticated cursor tokens; token TTL remains separate | + +Current bundled backends permit raw external writers. Qualified managed answer +promotion therefore stays disabled: ordinary Relation stamps and publication +capability cannot certify all qualifier and Caveat dependencies against such +writers. Exact-basis fallback is deliberate. It also prevents unknown in-place +changes from borrowing an answer from another native snapshot. Structural cache +reuse does not acquire a clock dependency simply because qualifiers are enabled. + +The same native trace runs on all four backends, including both Datahike attribute +representations. Cold, warm, repeated and read-only-cache reads agree across +context changes, positive and subtracting expiry without writes, qualifier touch, +bound-context replacement, Caveat expression replacement and lifecycle rotation. +The trace checks points, both lookup directions, detailed/default result policies +and limited/unlimited counts. DataScript additionally checks authoritative faults +on subtracting qualifiers, unstamped native changes, and restored entity IDs. +Datalevin persists its new lifecycle and recreates the client; its process-local +cache-clear operation is not a substitute for that recovery procedure. + +## Qualified object deletion + +`delete-object!` removes Relationship state, including expired Relationships. +Each transaction removes both exact endpoint values and their owned qualifier, +with native basis, schema and Relation guards. Transaction limits apply after +pair expansion and guard construction. A batch boundary cannot leave a live +half-pair pointing at an already removed qualifier. Self-relationships and a +surviving peer of an already removed native object use the same cleanup path. + +A stale deletion plan is rejected if the selected native basis changes before +submission, including an unstamped identity change or a qualifier replacement. +Other unattached preparations remain inert and are not collected by this API. +A referenced expired qualifier continues to protect its Caveat from schema +removal; removing the relationship and owned qualifier releases that dependency. + +Datalevin supplies exact retractions from the selected owned read snapshot for +qualified deletion, then releases that selection before submitting a guarded +batch. Its existing ordinary commit-time deletion path remains available while +the qualified semantic epoch is disabled. This change bounds submitted +transactions; adapters that materialize object retractions retain that existing +planning behavior. + +## Coordinated rollout and rollback + +1. Complete the v8 qualifier-eid storage migration and take a recoverable + database/schema checkpoint. Install the additive Caveat and qualifier + attributes through the backend's normal explicit preparation API. Startup + does not migrate or scan every Relationship. +2. Upgrade every serving Peer to the v9-capable implementation before permitting + non-nil qualifier writes. Drain older readers. Install and explicitly require + the optional JVM evaluator, or supply an independently certified evaluator + with the matching profile. Expiry-only schemas need no evaluator. +3. Verify each writer advertises its certified publication strategy, clock + health is acceptable, and live read paths select a fresh operation time. + Apply Caveated Relation alternatives using `write-schema!`; retained-data + validation must succeed before allowing qualified writes. +4. Enable qualified writes as a coordinated application rollout. Exercise + grant/ban expiry without writes, missing-context outcomes, and live cursor + restart. Monitor typed faults and resource limits through the usual request + diagnostics; never substitute an ordinary edge for a missing qualifier. + +Rollback after qualified writes requires stopping those writes and restoring the +pre-activation data/schema checkpoint before returning to older readers. Merely +disabling a serving switch or removing the evaluator does not make qualified +stored data safe for v8 readers. Already issued qualified cursors are scoped to +the v9 semantic contract and must not be silently rebased onto an older reader. + +Datomic, DataScript, direct Datahike, and the optional JVM evaluator remain in the +coordinated release set. The local Datalevin implementation is also tested, but +its Maven adapter stays excluded while +`dev.eacl/datalevin-embedded-eacl:1.0.2-eacl.2` is unpublished. Its local fork is +pinned at `a7e29c25a3034b54814e58a2d317e8c6877d1933`; a deployment needing that +adapter must resolve the publication and cold-consumer audit before release. + +The [acceptance crosswalk](../formal/qualified/acceptance.md) maps production +refinement and killed controls. The [performance qualification](benchmarks/qualified-authorization.md) +records the numerical budgets, exact workload and measurement limits. diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 00000000..9b018cd0 --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,20 @@ +# Executable v9 examples + +`caveats.clj` exercises the documented public API with a fresh DataScript store +and the actual optional JVM CEL evaluator. It uses an injected clock so expiry +and cursor boundaries are deterministic. No external database or secret is +required. The example expects v9 qualified serving to be activated. + +From the repository root, use an existing project nREPL with `:dev:caveats-jvm` +on its classpath: + +```sh +clj-nrepl-eval -p "$EACL_NREPL_PORT" '(do (load-file "docs/examples/caveats.clj") (eacl.examples.caveats/run-example!))' +``` + +Success returns the expected conditional, expiry, pinned/live, renewal and +deletion outcomes. Assertions additionally check detailed count categories, +physical stored-versus-active inspection, and prepared transaction composition. +The client clock starts at 1,000 ms, the ban expires at 1,500 ms, and the grant +expires at 2,000 ms. Those small epoch values are deliberate simulation inputs; +production clients normally use the trusted system clock. diff --git a/docs/examples/caveats.clj b/docs/examples/caveats.clj new file mode 100644 index 00000000..74b17821 --- /dev/null +++ b/docs/examples/caveats.clj @@ -0,0 +1,87 @@ +(ns eacl.examples.caveats + "Executable v9 guide. Load through the project nREPL with :caveats-jvm." + (:require [datascript.core :as ds] + [eacl.caveats.jvm :as caveats] + [eacl.core :as eacl] + [eacl.datascript.core :as api])) + +(def schema + "caveat in_region(region string, accepted list) { + region in accepted + } + definition user {} + definition doc { + relation viewer: user | user with in_region + relation banned: user + permission view = viewer - banned + }") + +(defn error-type [operation] + (try (operation) nil + (catch clojure.lang.ExceptionInfo error (:type (ex-data error))))) + +(defn run-example! [] + (let [conn (api/create-conn) + now (atom 1000) + client (api/make-client conn {:clock #(deref now) :caveat-evaluator (caveats/evaluator)}) + alice (eacl/spice-object :user "alice") + report (eacl/spice-object :doc "report") + other (eacl/spice-object :doc "other") + relationship {:subject alice :relation :viewer :resource report + :caveat "in_region" :caveat-context {"accepted" ["za"]} + :valid-until-ms 2000} + request {:subject alice :resource report :permission :view} + with-region (assoc request :caveat-context {"region" "za"})] + (eacl/write-schema! client schema) + (ds/transact! conn [{:eacl/id "alice"} {:eacl/id "report"} {:eacl/id "other"}]) + (eacl/create-relationship! client relationship) + (eacl/create-relationship! client alice :viewer other) + (let [conditional (eacl/check-permission client request)] + (assert (= :conditional-permission (:permissionship conditional))) + (assert (= ["region"] (:missing-fields conditional))) + (assert (false? (eacl/can? client request)))) + (assert (eacl/can? client with-region)) + (assert (false? (eacl/can? client (assoc request :caveat-context {"region" "us"})))) + (let [query {:subject alice :resource/type :doc :permission :view} + detailed (eacl/count-resources client (assoc query :result-policy :detailed))] + (assert (= 1 (:count (eacl/count-resources client query)))) + (assert (= [2 1 1] ((juxt :count :definite-count :conditional-count) detailed)))) + (eacl/create-relationship! client (assoc (eacl/->Relationship alice :banned report) :valid-until-ms 1500)) + (assert (false? (eacl/can? client with-region))) + (reset! now 1500) + (assert (eacl/can? client with-region) "An expired ban disappears without a write") + (let [pinned (eacl/snapshot client) + query {:subject alice :resource/type :doc :permission :view :first 1 + :caveat-context {"region" "za"}} + page (eacl/lookup-resources client query) + cursor (get-in page [:page-info :end-cursor])] + (try + (assert (= ["report"] (mapv :id (:data page)))) + (reset! now 2000) + (assert (false? (eacl/can? client with-region))) + (assert (eacl/can? pinned with-region) "Pinned time is intentionally historical") + (assert (= :eacl.pagination/restart-required + (error-type #(eacl/lookup-resources client (assoc query :after cursor))))) + (assert (= ["other"] (mapv :id (:data (eacl/lookup-resources client query))))) + (finally (eacl/release! pinned)))) + (let [query {:resource/type :doc :resource/id "report" :first 20} + stored (eacl/read-relationships client query) + active (eacl/read-relationships client (assoc query :relationship-state :expiry-active))] + (assert (= 2 (count (:data stored)))) + (assert (empty? (:data active)))) + (assert (= :eacl/relationship-conflict (error-type #(eacl/create-relationship! client relationship)))) + (eacl/write-relationship! client (assoc relationship :operation :touch :valid-until-ms 3000)) + (assert (eacl/can? client with-region)) + ;; A caller-managed prepared handle is inert until the composed native tx. + (let [prepared-value (assoc relationship :valid-until-ms 4000) + prepared (eacl/prepare-relationship! client prepared-value) + snapshot (eacl/snapshot client)] + (try + (ds/transact! conn (eacl/tx-relationships snapshot + [{:operation :touch :relationship prepared-value + :prepared-qualifier prepared}])) + (finally (eacl/release! snapshot)))) + (eacl/delete-relationship! client relationship) + (assert (false? (eacl/can? client with-region))) + {:conditional :conditional-permission :ban-expiry :granted :grant-expiry :denied + :pinned :historical-grant :live-cursor :restart-required :renewal :granted :delete :denied})) diff --git a/docs/migration-v7-to-v9.md b/docs/migration-v7-to-v9.md index 2d8aed2d..ea349ee0 100644 --- a/docs/migration-v7-to-v9.md +++ b/docs/migration-v7-to-v9.md @@ -2,9 +2,11 @@ EACL **v8** uses permission storage **8** and Relationship storage **9**. Storage 9 adds a fifth, nullable qualifier reference to each endpoint tuple. -This release writes `nil` qualifiers. Caveat and expiry evaluation arrive in -later phases; a non-`nil` qualifier encountered by an authorization or public -Relationship read raises `:eacl/unsupported-qualifier`. +V8 writes `nil` qualifiers; a non-`nil` qualifier encountered by a v8 +authorization or public Relationship read raises `:eacl/unsupported-qualifier`. +V9 [Caveats and expiring Relationships](caveats.md) build on this completed +storage migration with a separate coordinated serving activation. Complete this +migration before allowing any qualified writes. Every logical Relationship remains exactly two datoms: diff --git a/formal/assurance_contract.clj b/formal/assurance_contract.clj index b4c59e08..ba25a46f 100644 --- a/formal/assurance_contract.clj +++ b/formal/assurance_contract.clj @@ -8,7 +8,13 @@ are generated under target/formal and are deliberately absent here.") (def theorem-policies - {:caveat-qualifier-foundation + {:qualified-relationship-evaluation + {:sources ["formal/dafny/QualifiedEvidence.dfy" + "formal/dafny/QualifiedTemporal.dfy" + "formal/dafny/QualifiedReuse.dfy"] + :claim :proof-only-conditional-denotation-exclusive-expiry-and-witness-scoped-reuse + :minimum-proof-efforts 71} + :caveat-qualifier-foundation {:sources ["formal/dafny/CaveatOutcomes.dfy" "formal/dafny/CaveatProfile.dfy" "formal/dafny/CaveatSchema.dfy" @@ -129,7 +135,29 @@ :minimum-proof-efforts 73}}) (def operation-contracts - [{:operation :staged-caveat-qualifier-foundation + [{:operation :qualified-evaluation-model-gate + :entry-points ["formal/qualified/model.clj" + "formal/qualified/model_test.clj" + "formal/qualified/mutation_test.clj"] + :theorems [:pointwise-residual-and-authoritative-fault-algebra + :finite-positive-least-fixed-point + :inert-preparation-and-atomic-temporal-publication + :exclusive-expiry-and-non-monotonic-permission + :decisive-witness-stability-intervals + :context-evaluator-proof-and-result-kind-scoped-cache + :pinned-and-live-continuation-validity] + :dafny ["formal/dafny/QualifiedEvidence.dfy" + "formal/dafny/QualifiedTemporal.dfy" + "formal/dafny/QualifiedReuse.dfy"] + :adapter-obligations [:canonical-residual-refinement + :complete-recursive-and-cursor-evidence + :trusted-clock-and-native-qualifier-proof] + :runtime-targets [:clj-java :cljs-javascript] + :remaining [:production-refinement-and-mutations + :performance-and-cross-backend-qualification + :semantic-epoch-activation + :independent-review]} + {:operation :staged-caveat-qualifier-foundation :entry-points ['eacl.relationships.qualifier/normalize 'eacl.relationships.qualifier/decode 'eacl.caveats.values/encode-context diff --git a/formal/dafny/QualifiedEvidence.dfy b/formal/dafny/QualifiedEvidence.dfy new file mode 100644 index 00000000..ca6bca48 --- /dev/null +++ b/formal/dafny/QualifiedEvidence.dfy @@ -0,0 +1,178 @@ +// Proof-only permission evidence. Worlds are Boolean completions of the fixed +// request's residual Caveat atoms, not database entities or a runtime encoding. +module QualifiedEvidence { + datatype Outcome = Value(worlds: set) | Fault(reasons: set) + datatype Operator = Union | Intersection | Exclusion | Arrow + datatype Kind = Has | No | Conditional | Failure + + function Classify(universe: set, x: Outcome): Kind { + if x.Fault? then Failure + else if x.worlds == {} then No + else if x.worlds == universe then Has else Conditional + } + + function Errors(x: Outcome): set { + if x.Fault? then x.reasons else {} + } + + function Compose(op: Operator, a: Outcome, b: Outcome): Outcome { + if a.Fault? || b.Fault? then Fault(Errors(a) + Errors(b)) + else Value(if op.Union? then a.worlds + b.worlds + else if op.Exclusion? then a.worlds - b.worlds + else a.worlds * b.worlds) + } + + predicate NoNewFaults(before: Outcome, after: Outcome) { + after.Fault? ==> before.Fault? && after.reasons <= before.reasons + } + + lemma PointwiseBooleanAlgebra(op: Operator, a: set, b: set, w: nat) + ensures w in Compose(op, Value(a), Value(b)).worlds <==> + (if op.Union? then w in a || w in b + else if op.Exclusion? then w in a && w !in b + else w in a && w in b) + {} + + lemma FaultAlwaysPropagates(op: Operator, a: Outcome, b: Outcome) + requires a.Fault? || b.Fault? + ensures Compose(op, a, b).Fault? + ensures Errors(a) + Errors(b) == Compose(op, a, b).reasons + {} + + lemma ConditionalCannotGrant(universe: set, x: Outcome) + requires Classify(universe, x) == Conditional || Classify(universe, x) == Failure + ensures Classify(universe, x) != Has + {} + + lemma FaultsCannotAppearByComposition(op: Operator, a: Outcome, b: Outcome, laterA: Outcome, laterB: Outcome) + requires NoNewFaults(a, laterA) && NoNewFaults(b, laterB) + ensures NoNewFaults(Compose(op, a, b), Compose(op, laterA, laterB)) + {} + + lemma DecisiveUnionWitness(universe: set, b: Outcome, laterB: Outcome) + requires b.Value? && b.worlds <= universe + requires laterB.Value? && laterB.worlds <= universe + ensures Compose(Union, Value(universe), b) == Compose(Union, Value(universe), laterB) + ensures Compose(Union, b, Value(universe)) == Compose(Union, laterB, Value(universe)) + {} + + lemma DecisiveIntersectionWitness(b: Outcome, laterB: Outcome) + requires b.Value? && laterB.Value? + ensures Compose(Intersection, Value({}), b) == Compose(Intersection, Value({}), laterB) + ensures Compose(Arrow, b, Value({})) == Compose(Arrow, laterB, Value({})) + {} + + lemma DecisiveExclusionWitness(universe: set, a: set, b: set, laterA: set, laterB: set) + requires a <= universe && laterA <= universe + ensures Compose(Exclusion, Value({}), Value(b)) == Compose(Exclusion, Value({}), Value(laterB)) + ensures Compose(Exclusion, Value(a), Value(universe)) == Compose(Exclusion, Value(laterA), Value(universe)) + {} + + // Positive recursion is a least fixed point over finite (node, world) facts. + // Negative dependencies are evaluated in earlier strata, never in this SCC. + datatype Fact = Fact(node: nat, world: nat) + datatype Rule = Rule(head: Fact, body: set) + + function Step(base: set, rules: set, prior: set): set { + base + prior + set r | r in rules && r.body <= prior :: r.head + } + + lemma PositiveStepIsMonotone(base: set, rules: set, a: set, b: set) + requires a <= b + ensures a <= Step(base, rules, a) + ensures Step(base, rules, a) <= Step(base, rules, b) + {} + + function Iterate(base: set, rules: set, n: nat): set { + if n == 0 then {} else Step(base, rules, Iterate(base, rules, n - 1)) + } + + lemma IterationIsAscending(base: set, rules: set, n: nat) + ensures Iterate(base, rules, n) <= Iterate(base, rules, n + 1) + { + if n > 0 { + IterationIsAscending(base, rules, n - 1); + PositiveStepIsMonotone(base, rules, Iterate(base, rules, n - 1), Iterate(base, rules, n)); + } + } + + lemma IterationIsLeast(base: set, rules: set, closed: set, n: nat) + requires Step(base, rules, closed) <= closed + ensures Iterate(base, rules, n) <= closed + { + if n > 0 { + IterationIsLeast(base, rules, closed, n - 1); + PositiveStepIsMonotone(base, rules, Iterate(base, rules, n - 1), closed); + } + } + + lemma FixedPointRemainsFixed(base: set, rules: set, n: nat, extra: nat) + requires Iterate(base, rules, n) == Iterate(base, rules, n + 1) + ensures Iterate(base, rules, n + extra) == Iterate(base, rules, n) + { + if extra > 0 { + FixedPointRemainsFixed(base, rules, n, extra - 1); + } + } + + lemma StableRecursiveInputs(base: set, rules: set, laterBase: set, laterRules: set, n: nat) + requires base == laterBase && rules == laterRules + ensures Iterate(base, rules, n) == Iterate(laterBase, laterRules, n) + {} + + predicate BoundedInputs(possible: set, base: set, rules: set) { + base <= possible && (forall r | r in rules :: r.head in possible && r.body <= possible) + } + + lemma StepStaysFinite(possible: set, base: set, rules: set, prior: set) + requires BoundedInputs(possible, base, rules) && prior <= possible + ensures prior <= Step(base, rules, prior) <= possible + {} + + lemma StrictFiniteProgress(possible: set, prior: set, next: set) + requires prior <= next && prior != next && next <= possible + ensures |possible - next| < |possible - prior| + { + if next - prior == {} { + forall x | x in next + ensures x in prior + { + assert x !in next - prior; + } + assert next <= prior; + assert next == prior; + } + assert next - prior != {}; + assert |next - prior| > 0; + assert possible - prior == (possible - next) + (next - prior); + assert (possible - next) !! (next - prior); + assert |possible - prior| == |possible - next| + |next - prior|; + } + + function Closure(possible: set, base: set, rules: set, prior: set): set + requires BoundedInputs(possible, base, rules) && prior <= possible + ensures prior <= Closure(possible, base, rules, prior) <= possible + ensures Step(base, rules, Closure(possible, base, rules, prior)) == Closure(possible, base, rules, prior) + decreases |possible - prior| + { + StepStaysFinite(possible, base, rules, prior); + var next := Step(base, rules, prior); + if next == prior then prior else + (StrictFiniteProgress(possible, prior, next); Closure(possible, base, rules, next)) + } + + lemma ClosureIsLeast(possible: set, base: set, rules: set, prior: set, closed: set) + requires BoundedInputs(possible, base, rules) && prior <= possible + requires prior <= closed && Step(base, rules, closed) <= closed + ensures Closure(possible, base, rules, prior) <= closed + decreases |possible - prior| + { + StepStaysFinite(possible, base, rules, prior); + PositiveStepIsMonotone(base, rules, prior, closed); + var next := Step(base, rules, prior); + if next != prior { + StrictFiniteProgress(possible, prior, next); + ClosureIsLeast(possible, base, rules, next, closed); + } + } +} diff --git a/formal/dafny/QualifiedReuse.dfy b/formal/dafny/QualifiedReuse.dfy new file mode 100644 index 00000000..5bd653c2 --- /dev/null +++ b/formal/dafny/QualifiedReuse.dfy @@ -0,0 +1,103 @@ +include "QualifiedTemporal.dfy" + +module QualifiedReuse { + import opened E = QualifiedEvidence + import opened T = QualifiedTemporal + + // These identities are collision-checked canonical values, not bare hashes. + datatype Scope = Scope(source: nat, schema: nat, relations: seq, qualifiers: seq, context: seq, evaluator: seq, policy: nat, abi: seq, query: seq) + datatype Entry = Entry(authenticated: bool, scope: Scope, basis: nat, start: int, evidence: Evidence, kind: Kind) + datatype Mode = Pinned | Live + datatype Cursor = Cursor(entry: Entry, mode: Mode, tokenExpiry: int, retainedComplete: bool) + + predicate ScopeMatches(entry: Entry, scope: Scope) { entry.authenticated && entry.scope == scope } + + predicate TimeMatches(entry: Entry, basis: nat, time: int) { + (entry.start == time && entry.basis == basis) || + (entry.evidence.complete && entry.start <= time && Before(time, entry.evidence.end)) + } + + predicate AcceptCache(universe: set, entry: Entry, scope: Scope, basis: nat, ancestors: set, time: int) { + ScopeMatches(entry, scope) && + Before(entry.start, entry.evidence.end) && + (entry.basis == basis || entry.basis in ancestors) && + entry.kind == Classify(universe, entry.evidence.value) && entry.kind != Failure && + TimeMatches(entry, basis, time) + } + + // Certificate completeness includes skipped/subtracting evidence before the + // boundary and retained frontier/lookahead, not just the last emitted item. + predicate AcceptCursor(universe: set, cursor: Cursor, scope: Scope, basis: nat, ancestors: set, rawNow: int, evaluationTime: int, keyAvailable: bool) { + keyAvailable && rawNow < cursor.tokenExpiry && + AcceptCache(universe, cursor.entry, scope, basis, ancestors, evaluationTime) && + (if cursor.mode.Pinned? + then basis == cursor.entry.basis && evaluationTime == cursor.entry.start + else cursor.entry.start <= evaluationTime && + (evaluationTime == cursor.entry.start || (cursor.retainedComplete && cursor.entry.evidence.complete && Before(evaluationTime, cursor.entry.evidence.end)))) + } + + datatype DecodeProof = DecodeProof(source: nat, basis: nat, qid: nat, version: nat, format: nat, relation: nat, writerCertified: bool, content: seq) + + predicate AcceptDecode(prior: DecodeProof, selected: DecodeProof) { + prior.source == selected.source && prior.qid == selected.qid && prior.format == selected.format && + (prior.basis == selected.basis || + (prior.writerCertified && selected.writerCertified && prior.version != 0 && prior.version == selected.version && prior.relation == selected.relation) || + (prior.content != [] && prior.content == selected.content)) + } + + lemma CacheAcceptsOnlyCompleteScope(universe: set, entry: Entry, scope: Scope, basis: nat, ancestors: set, time: int) + requires AcceptCache(universe, entry, scope, basis, ancestors, time) + ensures entry.authenticated && entry.scope == scope + ensures entry.kind == Classify(universe, entry.evidence.value) && entry.kind != Failure + ensures (entry.start == time && entry.basis == basis) || (entry.evidence.complete && entry.start <= time && Before(time, entry.evidence.end)) + {} + + lemma DeadlineIsRejectedWithoutEviction(universe: set, entry: Entry, scope: Scope, basis: nat, ancestors: set, time: int) + requires entry.evidence.end.Until? && entry.evidence.end.at <= time + requires entry.start < entry.evidence.end.at + ensures !AcceptCache(universe, entry, scope, basis, ancestors, time) + {} + + lemma ConditionalAliasIsRejected(universe: set, entry: Entry, scope: Scope, basis: nat, ancestors: set, time: int) + requires Classify(universe, entry.evidence.value) == Conditional && entry.kind == Has + ensures !AcceptCache(universe, entry, scope, basis, ancestors, time) + {} + + lemma NoCertificateMeansExactTimeAndBasis(universe: set, entry: Entry, scope: Scope, basis: nat, ancestors: set, time: int) + requires !entry.evidence.complete + requires AcceptCache(universe, entry, scope, basis, ancestors, time) + ensures time == entry.start && basis == entry.basis + {} + + lemma PinnedRetainsOriginalView(universe: set, cursor: Cursor, scope: Scope, basis: nat, ancestors: set, rawNow: int, evaluationTime: int, keyAvailable: bool) + requires cursor.mode.Pinned? + requires AcceptCursor(universe, cursor, scope, basis, ancestors, rawNow, evaluationTime, keyAvailable) + ensures evaluationTime == cursor.entry.start && basis == cursor.entry.basis + ensures rawNow < cursor.tokenExpiry && keyAvailable + {} + + lemma LiveCannotCrossIncompleteOrExpiredState(universe: set, cursor: Cursor, scope: Scope, basis: nat, ancestors: set, rawNow: int, evaluationTime: int, keyAvailable: bool) + requires cursor.mode.Live? && evaluationTime != cursor.entry.start + requires AcceptCursor(universe, cursor, scope, basis, ancestors, rawNow, evaluationTime, keyAvailable) + ensures cursor.entry.start < evaluationTime + ensures cursor.retainedComplete && cursor.entry.evidence.complete && Before(evaluationTime, cursor.entry.evidence.end) + {} + + lemma DecodeCannotCrossLifecycle(prior: DecodeProof, selected: DecodeProof) + requires prior.source != selected.source + ensures !AcceptDecode(prior, selected) + {} + + lemma UnknownWriterNeedsExactOrContent(prior: DecodeProof, selected: DecodeProof) + requires !selected.writerCertified && prior.basis != selected.basis + requires AcceptDecode(prior, selected) + ensures prior.content != [] && prior.content == selected.content + {} + + lemma ManagedDecodeNeedsVersionAndRelation(prior: DecodeProof, selected: DecodeProof) + requires prior.basis != selected.basis && (prior.content == [] || prior.content != selected.content) + requires AcceptDecode(prior, selected) + ensures prior.writerCertified && selected.writerCertified && prior.version != 0 + ensures prior.version == selected.version && prior.relation == selected.relation + {} +} diff --git a/formal/dafny/QualifiedTemporal.dfy b/formal/dafny/QualifiedTemporal.dfy new file mode 100644 index 00000000..aa294af0 --- /dev/null +++ b/formal/dafny/QualifiedTemporal.dfy @@ -0,0 +1,181 @@ +include "QualifiedEvidence.dfy" + +module QualifiedTemporal { + import opened E = QualifiedEvidence + datatype Deadline = Forever | Until(at: int) + datatype Qualifier = Qualifier(valid: bool, expiry: Deadline, caveat: Outcome) + datatype Snapshot = Snapshot(forward: map, reverse: map, qualifiers: map, stamp: nat) + datatype Evidence = Evidence(value: Outcome, end: Deadline, complete: bool) + + predicate Before(t: int, d: Deadline) { d.Forever? || t < d.at } + + function Meet(a: Deadline, b: Deadline): Deadline { + if a.Forever? then b else if b.Forever? then a + else Until(if a.at <= b.at then a.at else b.at) + } + + function Qualify(universe: set, qid: nat, qs: map, t: int): Evidence { + if qid == 0 then Evidence(Value(universe), Forever, true) + else if qid !in qs || !qs[qid].valid then Evidence(Fault({0}), Forever, true) + else if !Before(t, qs[qid].expiry) then Evidence(Value({}), Forever, true) + else Evidence(qs[qid].caveat, qs[qid].expiry, true) + } + + function Edge(universe: set, s: Snapshot, identity: nat, t: int): Evidence { + if identity !in s.forward && identity !in s.reverse then Evidence(Value({}), Forever, true) + else if identity !in s.forward || identity !in s.reverse || s.forward[identity] != s.reverse[identity] + then Evidence(Fault({1}), Forever, true) + else Qualify(universe, s.forward[identity], s.qualifiers, t) + } + + predicate LeftDecides(universe: set, op: Operator, x: Outcome) { + x.Value? && (if op.Union? then x.worlds == universe else x.worlds == {}) + } + + predicate RightDecides(universe: set, op: Operator, x: Outcome) { + x.Value? && (if op.Union? || op.Exclusion? then x.worlds == universe else x.worlds == {}) + } + + function Need(universe: set, op: Operator, a: Evidence, b: Evidence): nat { + if a.value.Fault? || b.value.Fault? then 3 + else if LeftDecides(universe, op, a.value) && a.complete then 1 + else if RightDecides(universe, op, b.value) && b.complete then 2 + else 3 + } + + function Combine(universe: set, op: Operator, a: Evidence, b: Evidence): Evidence { + var needed := Need(universe, op, a, b); + Evidence(Compose(op, a.value, b.value), + if needed == 1 then a.end else if needed == 2 then b.end else Meet(a.end, b.end), + if needed == 1 then a.complete else if needed == 2 then b.complete else a.complete && b.complete) + } + + lemma MeetIsIntersection(t: int, a: Deadline, b: Deadline) + ensures Before(t, Meet(a, b)) <==> Before(t, a) && Before(t, b) + {} + + lemma ExclusiveBoundary(universe: set, qid: nat, qs: map) + requires qid != 0 && qid in qs && qs[qid].valid && qs[qid].expiry.Until? + ensures Qualify(universe, qid, qs, qs[qid].expiry.at).value == Value({}) + ensures Qualify(universe, qid, qs, qs[qid].expiry.at - 1).value == qs[qid].caveat + {} + + lemma NilHasNoQualifierDependency(universe: set, a: map, b: map, t: int, later: int) + ensures Qualify(universe, 0, a, t) == Qualify(universe, 0, b, later) + ensures Qualify(universe, 0, a, t).value == Value(universe) + {} + + lemma NonNilInvalidIsAuthoritative(universe: set, qid: nat, qs: map, t: int) + requires qid != 0 && (qid !in qs || !qs[qid].valid) + ensures Qualify(universe, qid, qs, t).value.Fault? + {} + + lemma ExpirySuppressesCaveat(universe: set, qid: nat, qs: map, t: int, other: Outcome) + requires qid != 0 && qid in qs && qs[qid].valid && !Before(t, qs[qid].expiry) + ensures Qualify(universe, qid, qs, t) == Qualify(universe, qid, qs[qid := Qualifier(true, qs[qid].expiry, other)], t) + {} + + lemma QualifierCertificateIsSound(universe: set, qid: nat, qs: map, start: int, later: int) + requires start <= later && Before(later, Qualify(universe, qid, qs, start).end) + ensures Qualify(universe, qid, qs, later).value == Qualify(universe, qid, qs, start).value + {} + + lemma ExpiryCannotCreateFaults(universe: set, qid: nat, qs: map, start: int, later: int) + requires start <= later + ensures NoNewFaults(Qualify(universe, qid, qs, start).value, Qualify(universe, qid, qs, later).value) + {} + + lemma PreparationHasNoDenotation(universe: set, s: Snapshot, qid: nat, q: Qualifier, identity: nat, t: int) + requires qid != 0 && qid !in s.qualifiers + requires qid !in s.forward.Values && qid !in s.reverse.Values + ensures Edge(universe, s, identity, t) == Edge(universe, + Snapshot(s.forward, s.reverse, s.qualifiers[qid := q], s.stamp + 1), identity, t) + {} + + function Publish(s: Snapshot, identity: nat, qid: nat): Snapshot { + Snapshot(s.forward[identity := qid], s.reverse[identity := qid], s.qualifiers, s.stamp + 1) + } + + lemma PublicationIsOneTemporalCommit(universe: set, s: Snapshot, identity: nat, qid: nat, t: int) + ensures Publish(s, identity, qid).stamp > s.stamp + ensures Publish(s, identity, qid).forward[identity] == Publish(s, identity, qid).reverse[identity] == qid + ensures Edge(universe, Publish(s, identity, qid), identity, t) == Qualify(universe, qid, s.qualifiers, t) + {} + + lemma WitnessCertificateIsSound(universe: set, op: Operator, a: Evidence, b: Evidence, laterA: Outcome, laterB: Outcome, later: int) + requires a.value.Value? ==> a.value.worlds <= universe + requires b.value.Value? ==> b.value.worlds <= universe + requires laterA.Value? ==> laterA.worlds <= universe + requires laterB.Value? ==> laterB.worlds <= universe + requires NoNewFaults(a.value, laterA) && NoNewFaults(b.value, laterB) + requires a.complete && Before(later, a.end) ==> laterA == a.value + requires b.complete && Before(later, b.end) ==> laterB == b.value + requires Combine(universe, op, a, b).complete && Before(later, Combine(universe, op, a, b).end) + ensures Compose(op, laterA, laterB) == Combine(universe, op, a, b).value + { + MeetIsIntersection(later, a.end, b.end); + } + + predicate Publishable(e: Evidence, start: int) { e.complete && Before(start, e.end) && !e.value.Fault? } + + lemma IncompleteEvidenceCannotPublish(e: Evidence, start: int) + requires !e.complete + ensures !Publishable(e, start) + {} + + lemma ExpiredBanCanGrant(universe: set, deadline: int) + requires universe != {} + ensures Classify(universe, Compose(Exclusion, Value(universe), + Qualify(universe, 1, map[1 := Qualifier(true, Until(deadline), Value(universe))], deadline - 1).value)) == No + ensures Classify(universe, Compose(Exclusion, Value(universe), + Qualify(universe, 1, map[1 := Qualifier(true, Until(deadline), Value(universe))], deadline).value)) == Has + {} + + function AcceptClock(prior: int, sample: int): int { if prior <= sample then sample else prior } + + lemma ClockCannotRevive(prior: int, sample: int, expiry: int) + requires expiry <= prior + ensures prior <= AcceptClock(prior, sample) + ensures !(AcceptClock(prior, sample) < expiry) + {} + + datatype Tree = Tip(id: nat) | Join(op: Operator, left: Tree, right: Tree) + + function Lookup(leaves: map, id: nat): Evidence { + if id in leaves then leaves[id] else Evidence(Fault({0}), Forever, true) + } + + function TreeEvidence(universe: set, tree: Tree, leaves: map): Evidence { + match tree + case Tip(id) => Lookup(leaves, id) + case Join(op, a, b) => Combine(universe, op, TreeEvidence(universe, a, leaves), TreeEvidence(universe, b, leaves)) + } + + predicate Within(universe: set, x: Outcome) { x.Value? ==> x.worlds <= universe } + + lemma ArbitraryEvidenceTreeCertificate(universe: set, tree: Tree, leaves: map, laterLeaves: map, later: int) + requires leaves.Keys == laterLeaves.Keys + requires forall id | id in leaves :: Within(universe, leaves[id].value) && Within(universe, laterLeaves[id].value) + requires forall id | id in leaves :: NoNewFaults(leaves[id].value, laterLeaves[id].value) + requires forall id | id in leaves :: leaves[id].complete && Before(later, leaves[id].end) ==> leaves[id].value == laterLeaves[id].value + ensures Within(universe, TreeEvidence(universe, tree, leaves).value) + ensures Within(universe, TreeEvidence(universe, tree, laterLeaves).value) + ensures NoNewFaults(TreeEvidence(universe, tree, leaves).value, TreeEvidence(universe, tree, laterLeaves).value) + ensures TreeEvidence(universe, tree, leaves).complete && Before(later, TreeEvidence(universe, tree, leaves).end) ==> + TreeEvidence(universe, tree, leaves).value == TreeEvidence(universe, tree, laterLeaves).value + { + match tree + case Tip(id) => + case Join(op, a, b) => + ArbitraryEvidenceTreeCertificate(universe, a, leaves, laterLeaves, later); + ArbitraryEvidenceTreeCertificate(universe, b, leaves, laterLeaves, later); + var left := TreeEvidence(universe, a, leaves); + var right := TreeEvidence(universe, b, leaves); + var laterLeft := TreeEvidence(universe, a, laterLeaves).value; + var laterRight := TreeEvidence(universe, b, laterLeaves).value; + FaultsCannotAppearByComposition(op, left.value, right.value, laterLeft, laterRight); + if Combine(universe, op, left, right).complete && Before(later, Combine(universe, op, left, right).end) { + WitnessCertificateIsSound(universe, op, left, right, laterLeft, laterRight, later); + } + } +} diff --git a/formal/qualified/README.md b/formal/qualified/README.md new file mode 100644 index 00000000..9063a4d5 --- /dev/null +++ b/formal/qualified/README.md @@ -0,0 +1,169 @@ +# Qualified authorization model gate + +This Phase 3 gate runs before production engine, cache, or cursor changes. +`EACL_NREPL_PORT= bin/formal fast` runs the Phase 2 foundation gate +first, then this gate. Models are verification tools and are never request-path +dependencies. Passing this gate does not activate qualified authorization. + +`QualifiedEvidence.dfy` models a residual as a set of Boolean completions of +the selected request's remaining Caveat atoms. This is a denotation, not the +production residual representation. Union, intersection, exclusion, and arrow +composition are pointwise Boolean operations. Any encountered authoritative +fault propagates before Boolean absorbers; CEL's internal expression-error +rules remain the separate Phase 2 contract. Positive recursion has monotone, +finite, least-fixed-point rules; negative dependencies are already stratified. + +`QualifiedTemporal.dfy` models sparse references, inert preparation, atomic +pair publication, one captured time, the exclusive expiry boundary, and +decisive witness certificates. Structural qualifier faults precede expiry; +expiry precedes Caveat work. With fixed context and immutable data, time cannot +introduce a new leaf fault, but can remove an evaluator fault by expiring its +edge. This property justifies witness pruning without masking a newly arising +fault. The operator theorem composes over arbitrary finite evidence trees. +Recursive false evidence must retain completeness through the fixed point; +stopping when membership alone stabilizes is deliberately killed. + +`QualifiedReuse.dfy` adds canonical (collision-checked) context/evaluator/query +identity, source/schema/Relation/qualifier dependencies, result kind, causal +basis compatibility, and half-open temporal intervals. Missing completeness +allows only the identical time and immutable basis. Live continuation requires +complete retained-state evidence for cross-time reuse. Pinned continuation +keeps basis/time while independently checking token lifetime and key +availability. Decode reuse requires exact basis, certified immutable writer +and Relation/version evidence, or complete native content proof. + +The finite Clojure oracle uses completion sets so a production symbolic +encoding can be compared independently. It exercises all 16 residual sets for +two Boolean atoms, all operator outcome combinations, 512 three-node directed +graphs, expiry on recursive grant and subtracting inputs, incomplete +certificates, scope changes, and cursor skipped/frontier evidence. Recursive +membership is checked against a separate per-completion reachability closure. +The Phase 2 lifecycle model supplies preparation/publication transitions. + +The production evidence bridge compares reduced ordered decision diagrams with +the independent completion sets across all four operators, all 16 residuals, +two authoritative faults, and complete/incomplete temporal certificates. Its +46,726 assertions cover denotation, deadlines, completeness, missing fields, +and canonical wire round trips. The qualification bridge adds 3,024 assertions +over expiry boundaries, bound/request contexts, malformed facts, allowance, +version, one-fetch request memoization, and evaluator suppression. The combined +finite gate also compares the existing scalar and vector operator machines across 93,312 +assertions for demanded intersection, exclusion, and arrow evidence, including +faults and incomplete certificates. Incremental join tests add 1,341 assertions against independent completion sets and bound updates to one ancestor path. The recursive production bridge adds 12,288 assertions across all 512 three-node graphs, conditional grants, expiring-ban bases, and future validity of complete certificates. The stable-route bridge adds 88,064 assertions covering bidirectional arms at two chunk widths, all bounded completion sets, and cyclic path prefixes against an independent positive fixed point. Both bridges check certificates against future expiry states. The native seekable bridge adds 28,672 assertions for both direct generators, both traversal and order directions, two chunk widths, emitted evidence, and certificates for exhausted walks. The general-cover bridge adds 36,864 assertions for exact composed membership, count policies, ascending/descending resume coordinates, and temporal certificates. The arrow bridge adds 79,875 assertions for known-binding reuse, ordered pages and counts through both target kinds, future validity, and the physical shorter-side work bound under an expired prefix. Fifty-four executable mutations at production seams add 109 assertions: ignored qualifier refs, expiry retention, fault/conditional coercion, incorrect horizons, stale reuse, omitted scan shape or qualifier flag, omitted context/time scope, recursive certificate convergence, skipped evidence-witness validation, a regressing clock, snapshot resampling, and a cached grant hiding an already encountered witness fault, coercion of conditional scan heads, lost emission expiry, conditional lookup grants, and count lookahead included in reported categories, coercion of general-cover child evidence, and discarded exact witnesses causing redundant probes, omitted arrow-witness scope validation, and repeated known arrow bindings, lost joint residuals, lost resumed binding evidence, and filtering an entire expired prefix before alternating witness sides. Four native-data controls additionally remove the assertion version, hide unknown fields, omit fetched-fact accounting, or refetch one entity across semantic roles. Each mapped gate must pass before mutation and fail under mutation. A fifth control omits data reads from aggregate resource budgets. Bounded data and request-cache conformance add 220 assertions. Known stable-path refinement adds 12,288 assertions across direct rules, child permissions, and both arrow target kinds; three controls remove its scope check or repeat known paths/bindings. Legacy union lookups add 28,672 assertions over native plans, both directions, resumed coordinates, bounded chunk widths, and future validity of emitted evidence. Three controls activate an inactive path, treat one partial path as the whole node, or force qualified context work on ordinary paths. The combined gate has 580,091 assertions. +Portable evidence tests additionally reject malformed/noncanonical encodings +and enforce node, depth, work, missing-field, and serialized-size bounds. + +The lock records 15 seconds and 5,000,000 resources per proof effort, the exact +proof/assertion inventories, and SHA-256 pins for the oracle and mutation +inputs. The whole-tree formal gate retains its stronger existing reporting +requirements. Production refinement, native conformance, mutation controls +against production, performance qualification, and semantic activation remain +Phase 3 implementation obligations. The repository's broader mechanized host +refinement and independent review obligations remain explicit in the assurance +manifest; model success does not discharge them. + +Bundled adapters expose a bounded `:qualification-data` operation only with the paired `:bounded-snapshot-data-v1` capability. The read preserves unknown attributes, returns the qualifier marker assertion version from the same basis, and charges every consumed fact, including the overflow witness. Datalevin uses its native prefix limit within the selected owned read snapshot and returns no uncertified assertion version. Shared native fixtures cover the four adapters, Datahike attribute refs, ordinary zero-read behavior, per-request fetch reuse, and fact/command accounting. + +Aggregate command and fetched-value budgets use the mandatory request ledger, including qualification data and operator probes. Queue transitions remain sourced from traversal work. Optional observations cannot omit qualified work from command, fact, or allocation-proxy limits. + +Qualified acyclic union plans retain the existing sealed-rule order. Their local point checks seed the exact path already found and complete only the remaining alternatives. Has paths take the ordinary fast path; inactive paths cannot claim a boundary from another active rule. Local node evidence is bounded per raw page. Recursive first-discovery pagination and public temporal cursor certification remain separate pending obligations. + +Public request context is bounded and canonicalized before snapshot selection or cache lookup, including empty batches and warm requests. Each Caveat receives its declared fields while the complete supplied context remains in the exact qualification scope. The direct Phase 2 evaluator keeps strict unknown-field validation. Three controls bypass context admission, pass unprojected fields, or omit unused fields from identity. + +Qualified public point checks bind one request to the existing stable, vector, and recursive routes. Detailed results retain conditional residuals; only the Boolean compatibility boundary converts authoritative evaluation failure to false. Operational errors propagate. The point answer cache currently uses complete exact basis/time/context/evaluator scope and canonical evidence values; managed temporal reuse remains pending. Public integration tests exercise expiring bans, batch sharing, pinned snapshots, changed unused fields, and unstamped qualifier corruption. Three controls drop public qualification, grant conditional results, or erase operational errors. + +Authorization schema readers in every bundled adapter validate permission structure without compiling named Caveat programs. Diagnostic schema reads retain complete Caveat validation. Expired public point checks exercise a cold schema path with program compilation disabled; a mutation restores eager full-schema compilation and must fail. + +Weighted first-discovery propagation is modeled independently as completion-set reachability. Across 9,216 graph/seed traces it checks closure, bounded evidence growth, unique discoveries, and exact prefix/resume order. Targeted controls omit revisits or path conjunction. These 64,515 assertions precede changes to the production reducer. + +The production first-discovery bridge adds 34,432 assertions across 3,072 graph cases in both directions, independently checking emitted completion sets, physical-width invariance, and resumed prefixes. Portable conformance adds 309 assertions for cyclic conditional paths, exact root completion, nil-eid fast paths, definite/detailed counts and windows, lookahead exclusion, bounded skipped prefixes, staged memory/work limits, and checkpoint scope. A temporal cycle can change a queued scan's certificate while a physical buffer remains active; its revision must rewind the prefix independently of chunk width. Five controls discard propagated evidence, claim a partial root, omit the evidence bound, repeat a known direct tuple, or reuse an earlier buffer revision. Qualified checkpoints preserve basis/context/evaluator scope; the page boundary can rebind time only when a complete certificate covers both the original and current observation. Direct reducer resume remains exact. + +First-discovery pages retain sparse result evidence through lookahead, replay, backward windows, and last windows. Pending lookahead has explicit evidence even for timeless true. Missing, faulty, or scope-incompatible checkpoint data becomes a replay miss. Checkpoint keys partition complete qualified request scope and result policy; standalone tokens bind a compact digest of the same scope. The 209 portable page assertions cover retained versus replayed equality, scope isolation, no-repeat lookahead, and malformed retained state. Four controls remove pending evidence, bypass its validation, omit checkpoint scope, or omit standalone token scope. These internal exact-scope tokens remain distinct from the public Relay live-time certificate contract. + +Schema admission now checks a matching evaluator for every Caveated Relation, +including empty and unvisited Relations, and a certified native publication +strategy before entering qualified answer-cache routes. Named alternatives +share one typed Relation identity. Schema replacement validates both stored +endpoint streams and the retained qualifier values before tightening those +alternatives, keeps the Relation eid, and fences concurrent Relationship +changes. Datalevin derives validation and commit guards inside one owned read +snapshot, releases it before submission, and scans large Relation prefixes in +bounded native batches without truncation. Six production controls omit the +evaluator, schema or request publication admission, retained-data validation, +Relation identity retention, or the schema replacement's Relation fence. The +combined gate has 583,204 assertions and 77 detected production mutations. + +Physical Relationship inspection shares the bounded qualifier decoder and native +endpoint scan. Stored pages retain expired rows and canonical qualifier metadata; +expiry-active pages apply the exclusive boundary without executing Caveat +programs. The same contract covers all four adapters and both Datahike attribute +modes, both directions and partial/exact anchors, cache-disabled equality, and +renewal/shortening/removal through touch. Six production controls make expiry +inclusive, demand definite Caveat evidence during inspection, drop the aligned +qid or rendered metadata, remove the candidate-work limit, or admit noncanonical +cached metadata. Qualification deadline and cancellation tests stop immediately +after a native data read and preserve the operational error on retry. The combined +gate has 584,088 assertions and 83 detected production mutations. + +Optional qualifier decode retention now uses exact-basis or complete native +content proof. The latter includes source lifecycle, qid, assertion version, +format, all qualifier fields, the owning Relation and named Caveat definition. +It does not infer a source-wide immutable-writer contract from publication +capability. Portable and public native tests add 93 assertions for same-basis +fetch omission, cross-basis decode reuse with content reads, fresh per-request +context/expiry evaluation, unchanged-marker mutation, deletion, source reset, +capacity, and read-only/disabled cache controls. Five controls omit native +content, definition content, lifecycle, Relation proof or exact native basis. +The combined gate has 584,191 assertions and 88 detected production mutations. + +Completed point answers now carry their evidence, original evaluation time, +exclusive deadline, completeness and result kind. Exact-basis reuse checks time +on every resident hit; completeness is required for a later time. Five production +controls bypass resident interval validation, claim missing completeness, detach +the certificate from evidence, allow older publication to displace a newer +interval, or erase an expiring-ban witness. The independent reuse-model bridge +adds 432 comparisons; portable acceptance/retention tests add 886 assertions. +Native checks cover ban expiry, a permanent decisive witness, unchanged context +scope and pinned evaluation. A cache publication race test verifies conditional +replacement cannot overwrite a concurrent winner. The combined qualified gate +has 586,029 assertions and 99 detected production mutations. Cross-basis +managed qualified result proof integration remains pending. + +Public qualified cursors now refine the pinned/live time model through the +production temporal bridge. The request ledger observes examined expiry bounds +and imports accepted retained proofs. Internal collection values, range +segments and stable checkpoints preserve their certificates; six additional +mutation controls cover skipped-ban deadlines, live time acceptance, incomplete +proof, context/evaluator scope, range retention and closed cursor envelopes. +Public tests include forward/backward continuation, both lookup directions and +evaluation modes, pinned historical time, active/stored inspection and typed +restart without extra qualification work. + +Cache coherence conformance compares cold, warm, repeated and read-only-cache +results at eight temporal/write/lifecycle states on Datomic, DataScript, both +Datahike modes and Datalevin (the latter in its native module gate). The trace +covers three contexts, five permissions, both lookup directions/result policies, +and capped/exact counts. Healthy traces also assert that results contain no +fault. Unknown-writer subtracting faults and restored DataScript identities +have a separate trace. Two controls omit shared-denotation time or whole request +context; both fail only their mutated runs. The combined qualified gate now has +598,592 assertions and 101 detected production mutations. + +Qualified object deletion is checked after every transaction with a forced +12-operation cap, on all native backends and both Datahike attribute modes. +The contract includes expired edges, self-edges, inert preparation preservation, +and stale-plan rejection after an unstamped identity change or qualifier touch. +DataScript additionally checks surviving-peer cleanup and schema orphan release. +Three controls remove qualifier cleanup, split peer retractions, or omit the +native selected-basis guard. Each passes its unmodified gate and fails mutated. + +The combined qualified gate now has 598,713 assertions and 104 detected +production mutations, with 71 verified model obligations. + +The final acceptance crosswalk is in `acceptance.md`. The compound-expression +campaign adds 8,000 assertions with seed 20260905, and an unresolved-qid +publication control, collection/rendered interval controls, and checkpoint interval +control bring the qualified inventory to 108 detected production mutations and +607,000 assertions. The foundation gate separately retains its +82 verified obligations and 35,148 assertions. Performance qualification checks +the actual public backend matrix against pre-recorded numerical budgets. diff --git a/formal/qualified/acceptance.md b/formal/qualified/acceptance.md new file mode 100644 index 00000000..8d2ae9c4 --- /dev/null +++ b/formal/qualified/acceptance.md @@ -0,0 +1,117 @@ +# Phase 3 acceptance crosswalk + +The independent finite models, native conformance, and public serving tests are +complementary. Models never execute in the authorization dependency closure. +`bin/formal fast` runs both the foundation and qualified gates. Every production +mutation first runs its unchanged test gate successfully, then must make that +same gate fail. These checks do not substitute for the broader host refinement +and independent review obligations recorded in the assurance manifest. + +| Obligation | Production refinement / conformance | +|---|---| +| Qualifier lookup, faults, bound context, exclusive expiry | `qualification_bridge.clj`; four native qualification-data suites; JVM evaluator corpus | +| Correlated Caveat algebra and decisive temporal evidence | `evidence_bridge.clj`, `operator_bridge.clj`, `recursive_bridge.clj` | +| Native scans, arrows, stable discovery, counts, retained evidence | `seekable_bridge.clj`, `arrow_bridge.clj`, `stable_route_bridge.clj`, `discovery_bridge.clj`, `lookup_bridge.clj`, `legacy_lookup_bridge.clj` | +| Point-cache and live/pinned cursor interval acceptance | `temporal_bridge.clj`; public `qualified_check_test.cljc` and `qualified_cursor_test.cljc` | +| Complete content identity and unknown native writers | DataScript `qualifier_cache_test.cljc`, `qualified_cache_trace_test.cljc`; shared native cache-trace contract | +| Pair publication, retained definition references, deletion | Foundation `native_bridge.clj`; shared publication, batch, schema-allowance, and deletion contracts | + +The evidence bridge additionally runs a reproducible 2,000-operation compound +expression campaign (seed `20260905`). It carries independent completion-set +oracle values through generated intermediate expressions, checking denotation, +deadline, completeness, and wire round trips. Fresh leaves remain in the sample +pool so absorbing faults do not collapse the campaign into only fault results. + +| Required killed control | Registered production control | +|---|---| +| Omit qualifier lookup | qualified `:qualifier-reference-ignored` | +| Missing qualifier becomes ordinary | foundation `:missing-qualifier-becomes-nil`; qualified `:authoritative-failure-becomes-plain` | +| Publish only one endpoint | foundation `:one-half-publication` | +| Publish unresolved qualifier eid | qualified `:prepared-publication-leaks-unresolved-qid` | +| Inclusive expiry boundary | qualified `:expiry-boundary-retains-permission` | +| Reverse bound/request precedence | foundation `:bound-context-loses` | +| Conditional becomes true | qualified `:conditional-becomes-truthy` | +| Fault becomes absence | qualified `:fault-becomes-absence` | +| Unsafe decoded-data reuse | qualified `:qualifier-cache-omits-native-content`, `:qualifier-cache-omits-definition-content`, `:qualifier-cache-exact-scope-omits-native-basis` | +| Omit Relation stamp / commit fence | foundation `:publication-stamp-stalls`; qualified `:batch-publication-omits-relation-fence` | +| Reuse past an expiring ban | qualified `:temporal-point-loses-expiring-ban-witness`, `:qualified-cursor-loses-skipped-ban-deadline` | + +Numerical release budgets and the executable four-backend workload are described +in `docs/benchmarks/qualified-authorization.md`. The nil-eid path must issue no +qualification-data reads. Compiled portable plans and native programs share one +bounded evaluator cache; it holds data/programs, never authorization decisions. +Every evaluator call validates the current definition envelope before content +reuse. Partial inputs still avoid native program construction and evaluation. + +The exact content cache includes the original parameter payload, name, source, +and profile. A hit reuses the decoded parameter data as well as the compiled +plan. Bounds and closed field presence are checked before lookup; any changed +content misses and undergoes complete decoding/compilation. Dedicated tests +reject malformed or changed parameters, profile, name, and source after a hit. +The qualified gate explicitly runs all six Evidence unit tests in addition to +the refinement bridges and individual mutation controls. + +The performance audit found repeated serialization of timeless Boolean evidence, +portable-plan compilation, byte-vector construction for UTF-8 length, and parsing +of freshly encoded host contexts. Their removal preserves canonical bytes and +input admission. BMP/surrogate conformance and host-context differential tests +check the changed boundaries, alongside the existing adversarial format suites. + +The activated v9 default passes the fresh JVM battery: 1,431 tests / 149,296 +assertions, plus 67 Datalevin tests / 8,172 assertions and 782 advanced +ClojureScript tests / 110,313 assertions. All have zero failures and errors. +The executable Caveat/expiry guide runs without an internal feature binding. +The coordinated five-module 9.0.0-SNAPSHOT build, dependency audit, cold local +Maven installation, and consumer smoke tests also pass. Datalevin remains +excluded from Maven release eligibility until its fork artifact is published. + +The foundation gate verifies 82 obligations and 35,148 assertions; the qualified +gate verifies 71 obligations and 607,000 assertions across 392 tests, including +108 killed production controls. Source-closure reports 115 public roots, 2,939 +reachable definitions, and no forbidden match. Qualified checkpoint checks +reject a certificate that excludes the original observation time even when it +includes the later request time. Strict OpenSpec validation passes. + +The four-backend performance gate passes all 24 reports and 1,440 fixed budget +comparisons. Raw samples, source hashes, the checker result, and the full metric +matrix are retained in `docs/benchmarks/results/qualified-authorization-2026-09-05/`. +The benchmark explicitly selected legacy and qualified epochs before changing +the default and release/continuation ABI metadata. Numerical budgets were not +relaxed. Before activation, the full legacy JVM battery also passed 1,431 tests / +149,323 assertions; Datalevin's affected concurrent-write and cache contracts +passed in both epochs. + +Activated historical counterexample replay passes 71 tests / 18,228 assertions, +the registered mutation-control gate passes 3 tests / 176 assertions, and the +complete generated Java boundary suite passes 52 tests / 18,280 assertions. +Historical state traces preserve their authorization and structural-reuse +oracles while selecting exact qualified versus scalar-proof legacy answer reuse. + +## Native contention qualification after activation + +The generated-authority CI benchmark exposed two previously unexercised v9 +paths. Its unrelated-commit check now asserts exact-basis fallback in v9 and +managed hits only in the retained v8 compatibility binding; its result labels +state which reuse contract was measured. DataScript and Datahike writers now +recognize their native CAS failures through bounded exception wrappers and +replan from the current basis, with the existing eight-attempt public +contention limit. A changed cleanup source is also retryable; qualifier +validation faults remain terminal. + +Deterministic shared tests commit a competing Relationship between planning +and submission, verify one fresh retry publishes both Relationships, verify +persistent contention stops after eight attempts, and verify validation faults +are attempted once. The following checks passed on this correction: + +- Generated-authority heavy suite: 9 tests, 574 assertions, all three backends + exercised generated cursor/relationship-page authority. +- Generated-authority nonbenchmark suite: 625 tests, 117,495 assertions. +- Affected DataScript/Datahike qualified writers: 17 tests, 7,256 assertions. +- Advanced DataScript ClojureScript: 783 tests, 110,321 assertions. +- Foundation gate: 82 Dafny obligations and 35,148 finite assertions. +- Qualified gate: 71 Dafny obligations, 394 tests, 607,016 assertions. +- Public source closure: 115 roots, 2,940 definitions, no forbidden matches. + +The published 24-case performance evidence measures unchanged successful read +and write paths. The new retry policy affects failed native submissions; +concurrent-write behavior has separate deterministic and benchmark evidence. diff --git a/formal/qualified/arrow_bridge.clj b/formal/qualified/arrow_bridge.clj new file mode 100644 index 00000000..78c292e0 --- /dev/null +++ b/formal/qualified/arrow_bridge.clj @@ -0,0 +1,95 @@ +(ns eacl.formal.qualified.arrow-bridge + "Completing a known arrow binding against independent completion sets. + The existing scalar machine visits the remaining bindings exactly once." + (:require [clojure.test :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.engine.least-path :as least-path] + [eacl.formal.qualified.evidence-bridge :as bridge] + [eacl.formal.qualified.model :as model] + [eacl.operator.arrow-evidence-test :as fixtures] + [eacl.operator.evaluator :as scalar] + [eacl.operator.lookup :as lookup] + [eacl.operator.lookup-evidence-test :as ordered])) + +(defn semantic [layout a b time] + (let [negative-a (evidence/combine :exclusion true a) + values (case layout + 0 [true a true b] + 1 [a b b negative-a] + 2 [a negative-a true b])] + (into {} (map (fn [key value end] + [key (if (< time end) (evidence/with-certificate value end true) false)]) + [[:via 0] [:target 0] [:via 1] [:target 1]] values [100 110 120 130])))) + +(defn oracle [leaves] + (model/compose :union + (model/compose :arrow (bridge/model-value (get leaves [:via 0])) + (bridge/model-value (get leaves [:target 0]))) + (model/compose :arrow (bridge/model-value (get leaves [:via 1])) + (bridge/model-value (get leaves [:target 1]))))) + +(deftest seeded-arrow-completion-refines-union-of-exact-bindings + (let [{:keys [qids] :as env} (fixtures/fixture) + inputs (remove evidence/fault? (take-nth 3 (bridge/inputs)))] + (doseq [permission [:view :direct_inherited]] + (let [base (fixtures/options env permission)] + (doseq [layout [0 1 2] a inputs b inputs] + (let [leaves (semantic layout a b 99) + known (evidence/combine :arrow (get leaves [:via 0]) (get leaves [:target 0])) + result (with-redefs-fn {#'qualification/qualify (fixtures/resolver qids leaves)} + #(scalar/check-eids (assoc-in base [:arrow-witness :evidence] known)))] + (is (= (oracle leaves) (bridge/model-value result))) + (doseq [time [100 110 120]] + (is (or (not (evidence/reusable? result 99 time)) + (= (oracle (semantic layout a b time)) (bridge/model-value result))))))))))) + +(defn check-ordered-case! [env base traversal width policy layout a b] + (let [leaves (semantic layout a b 99) + options (assoc base :traversal traversal :page-size width :result-policy policy + :anchor-eid (if (= traversal :forward) (:user env) (:document env))) + expected-value (oracle leaves) + expected (if (if (= policy :definite) + (= (model/worlds 2) (:worlds expected-value)) + (seq (:worlds expected-value))) + (if (= traversal :forward) (:documents env) (:users env)) []) + [rows descending counted] + (with-redefs-fn {#'qualification/qualify (fixtures/resolver (:qids env) leaves)} + #(vector (ordered/drain options) + (ordered/drain (assoc options :order-direction :desc)) + (lookup/count-results options)))] + (is (= (zipmap expected (repeat expected-value)) + (into {} (map (juxt :value (comp bridge/model-value :evidence))) rows))) + (is (= (count expected) (count rows) (:count counted))) + (is (= (mapv (juxt :value :coords (comp evidence/value :evidence)) rows) + (mapv (juxt :value :coords (comp evidence/value :evidence)) (rseq descending)))) + (doseq [time [100 110 120]] + (is (every? (fn [row] + (or (not (evidence/reusable? (:evidence row) 99 time)) + (= (oracle (semantic layout a b time)) (bridge/model-value (:evidence row))))) rows))))) + +(deftest ordered-arrow-covers-refine-completion-sets-and-resume-coordinates + (let [env (fixtures/lookup-fixture) + inputs (remove evidence/fault? (take-nth 3 (bridge/inputs)))] + (doseq [permission [:inherited :inherited_direct]] + (let [base (fixtures/lookup-options env permission :forward :asc 1 :detailed)] + (doseq [layout [0 1 2] traversal [:forward :reverse] width [1 2] policy [:definite :detailed] + a inputs b inputs] + (check-ordered-case! env base traversal width policy layout a b)))))) + +(deftest expired-prefix-witness-work-is-bounded-by-the-physical-shorter-side + (let [probes (atom 0) + ctx (least-path/make-context + {:qualification {} :physical-chunk-size 1 :max-values 2 :max-commands 3 + :fetch-fn (fn [descriptor] + (if (= :large (:side descriptor)) + [[(inc (or (:bound-eid descriptor) 0)) 10]] []))}) + stream (fn [side] + (#'least-path/stream + (fn [bound width] {:side side :relation-eid 1 :bound-eid bound :limit width}) nil)) + member? (fn [_] (swap! probes inc) true)] + (with-redefs [qualification/qualify (constantly false)] + (is (false? (#'least-path/isect2? ctx (stream :large) member? (stream :empty) member? nil)))) + (is (= {:commands 2 :fetched-values 1} + (select-keys @(:counters ctx) [:commands :fetched-values]))) + (is (zero? @probes)))) diff --git a/formal/qualified/check-boundary.mjs b/formal/qualified/check-boundary.mjs new file mode 100644 index 00000000..4121d733 --- /dev/null +++ b/formal/qualified/check-boundary.mjs @@ -0,0 +1,17 @@ +import {readFileSync, readdirSync} from 'node:fs'; +import {createHash} from 'node:crypto'; + +const lock = JSON.parse(readFileSync('formal/qualified/gate.lock.json', 'utf8')); +const fail = message => { throw new Error(message); }; +const models = readdirSync('formal/dafny').filter(n => /^Qualified.*\.dfy$/.test(n)).sort(); +if (JSON.stringify(models) !== JSON.stringify([...lock.models].sort())) fail('Qualified model inventory changed'); +for (const [file, expected] of Object.entries(lock.resourceInputs)) { + if (createHash('sha256').update(readFileSync(file)).digest('hex') !== expected) fail(`Qualified model input changed: ${file}`); +} +for (const file of models) { + const source = readFileSync(`formal/dafny/${file}`, 'utf8'); + if (/\b(assume|axiom)\b|\{:\s*(extern|axiom)\b|\{:\s*verify\s+false\b|decreases\s+\*/.test(source)) fail(`Proof escape hatch: ${file}`); +} +if (!Number.isInteger(lock.verifiedObligations) || lock.verifiedObligations < 1 || + !Number.isInteger(lock.assertions) || lock.assertions < 1) fail('Empty proof/assertion inventory'); +console.log(`Qualified boundary locked: ${models.length} models, ${lock.verifiedObligations} obligations`); diff --git a/formal/qualified/discovery_bridge.clj b/formal/qualified/discovery_bridge.clj new file mode 100644 index 00000000..5be282d5 --- /dev/null +++ b/formal/qualified/discovery_bridge.clj @@ -0,0 +1,48 @@ +(ns eacl.formal.qualified.discovery-bridge + "Production first-discovery results against independent per-world closure." + (:require [clojure.test :refer [deftest is]] + [eacl.formal.qualified.discovery-model :as model] + [eacl.formal.qualified.discovery-model-test :as contract] + [eacl.engine.stable-reducer-evidence-test :as fixture] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.evidence-test :as residual] + [eacl.authorization.qualification :as qualification] + [eacl.engine.stable-reducer :as reducer])) + +(def nodes [100 200 300]) +(def universe #{0 1 2 3}) +(defn atom-value [worlds] + (reduce #(evidence/combine :union %1 %2) false + (for [world worlds] + (evidence/combine :intersection + (if (bit-test world 0) residual/x (evidence/combine :exclusion true residual/x)) + (if (bit-test world 1) residual/y (evidence/combine :exclusion true residual/y)))))) + +(deftest generated-discovery-refines-completion-set-reachability + (doseq [mask (range 512) style (range 6)] + (let [graph (contract/graph mask style) + seeds [[0 #{1 3}] [1 #{2 3}]] + expected (model/denotation graph seeds universe) + edges (for [[from targets] graph [to worlds] targets] + [(nodes from) 20 (nodes to) (+ 200 (* from 3) to) worlds]) + rows (into [[1 10 100 101] [1 10 200 102]] (map #(subvec % 0 4)) edges) + leaves (into {101 residual/x 102 residual/y} + (map (fn [[_ _ _ q worlds]] [q (atom-value worlds)])) edges) + env (fixture/environment rows leaves {}) + forward (fixture/run env) + wide (fixture/run (assoc-in env [:options :physical-chunk-size] 4))] + (is (= (set (map nodes (keys expected))) (set (:results forward)))) + (is (= (:results forward) (:results wide))) + (doseq [eid (:results forward)] + (is (= (get expected (.indexOf nodes eid)) + (residual/completions (fixture/value-at forward eid) [residual/x residual/y])))) + (doseq [node (range 3)] + (let [reverse (fixture/run (fixture/environment rows leaves {:direction :reverse :resource-eid (nodes node)}))] + (is (= (if (seq (get expected node)) [1] []) (:results reverse))) + (when (seq (:results reverse)) + (is (= (get expected node) + (residual/completions (fixture/value-at reverse 1) [residual/x residual/y])))))) + (let [prefix (fixture/run (assoc-in env [:options :target] 1)) + resumed (with-redefs [qualification/qualify (:qualify env)] + (reducer/resume (:options env) (reducer/history-free prefix)))] + (is (= (:results forward) (into (:results prefix) (:results resumed)))))))) diff --git a/formal/qualified/discovery_model.clj b/formal/qualified/discovery_model.clj new file mode 100644 index 00000000..e4505ede --- /dev/null +++ b/formal/qualified/discovery_model.clj @@ -0,0 +1,59 @@ +(ns eacl.formal.qualified.discovery-model + "Finite completion-set oracle for weighted first-discovery propagation. + This model is verification-only and has no production dependencies." + (:require [clojure.set :as set])) + +(defn denotation + "Independent per-world reachability, then union worlds at each vertex." + [graph seeds universe] + (reduce + (fn [answer world] + (loop [pending (vec (for [[node worlds] seeds :when (contains? worlds world)] node)) + visited #{}] + (if-let [node (peek pending)] + (if (contains? visited node) + (recur (pop pending) visited) + (recur (into (pop pending) + (for [[target worlds] (get graph node) :when (contains? worlds world)] target)) + (conj visited node))) + (reduce #(update %1 %2 (fnil conj #{}) world) answer visited)))) + {} universe)) + +(defn admit + "Coalesces a changed prefix at its queued position, rewinding its scan." + [state node incoming] + (let [previous (get-in state [:values node] #{}) + joined (set/union previous incoming)] + (if (= previous joined) + state + (-> state + (assoc-in [:values node] joined) + (assoc-in [:pending node] {:index 0 :worlds joined}) + (cond-> (not (contains? (:pending state) node)) (update :stack conj node)) + (update :changes inc))))) + +(defn initial [seeds] + ;; Initial seeds are staged together in canonical order, as in enumeration. + (let [state (reduce (fn [state [node worlds]] (admit state node worlds)) + {:values {} :pending {} :stack [] :changes 0 :steps 0 :emitted []} seeds)] + (update state :stack #(vec (reverse %))))) + +(defn run + "Runs width-one transitions, stopping only after a complete node discovery." + [graph initial target] + (loop [state initial] + (when (> (:steps state) 10000) + (throw (ex-info "Finite discovery model exceeded its termination bound" {}))) + (if (or (empty? (:stack state)) (>= (count (:emitted state)) target)) + state + (let [node (peek (:stack state)) + {:keys [index worlds]} (get-in state [:pending node]) + edges (get graph node []) + state (-> state + (update :steps inc) + (cond-> (not (some #{node} (:emitted state))) (update :emitted conj node)))] + (if (= index (count edges)) + (recur (-> state (update :stack pop) (update :pending dissoc node))) + (let [[child edge-worlds] (nth edges index) + state (assoc-in state [:pending node :index] (inc index))] + (recur (admit state child (set/intersection worlds edge-worlds))))))))) diff --git a/formal/qualified/discovery_model_test.clj b/formal/qualified/discovery_model_test.clj new file mode 100644 index 00000000..2790dac5 --- /dev/null +++ b/formal/qualified/discovery_model_test.clj @@ -0,0 +1,45 @@ +(ns eacl.formal.qualified.discovery-model-test + (:require [clojure.test :refer [deftest is]] + [clojure.set :as set] + [eacl.formal.qualified.discovery-model :as discovery])) + +(def universe #{0 1 2 3}) +(def a #{0 1}) +(def b #{0 2}) +(def weights [universe #{} a b #{2 3} #{1 3}]) + +(defn graph [mask style] + (into {} + (for [from (range 3)] + [from (vec (for [to (range 3) :when (bit-test mask (+ (* from 3) to))] + [to (nth weights (mod (+ style from (* 2 to)) (count weights)))]))]))) + +(deftest weighted-discovery-is-complete-bounded-and-resumable + (doseq [mask (range 512) style (range 6) + seeds [[[0 a] [1 b]] [[0 universe]] [[0 a] [1 #{2 3}]]]] + (let [graph (graph mask style) + expected (discovery/denotation graph seeds universe) + initial (discovery/initial seeds) + full (discovery/run graph initial Long/MAX_VALUE)] + (is (= expected (:values full))) + (is (= (set (keys expected)) (set (:emitted full)))) + (is (<= (:changes full) (* 3 (count universe)))) + (doseq [target [1 2]] + (let [prefix (discovery/run graph initial target) + resumed (discovery/run graph prefix Long/MAX_VALUE)] + (is (= (:emitted full) (:emitted resumed))) + (is (= (:values full) (:values resumed)))))))) + +(deftest changed-prefixes-must-revisit-and-conjoin + (let [graph {0 [[2 universe]] 1 [[2 universe]] 2 [[3 b]]} + seeds [[0 a] [1 #{2 3}]] + expected (discovery/denotation graph seeds universe) + run #(discovery/run graph (discovery/initial seeds) Long/MAX_VALUE) + admit discovery/admit] + (is (= expected (:values (run)))) + (with-redefs [discovery/admit (fn [state node worlds] + (if (contains? (:values state) node) state + (admit state node worlds)))] + (is (not= expected (:values (run))))) + (with-redefs [set/intersection (fn [prefix _] prefix)] + (is (not= expected (:values (run))))))) diff --git a/formal/qualified/evidence_bridge.clj b/formal/qualified/evidence_bridge.clj new file mode 100644 index 00000000..40e91276 --- /dev/null +++ b/formal/qualified/evidence_bridge.clj @@ -0,0 +1,67 @@ +(ns eacl.formal.qualified.evidence-bridge + (:require [clojure.test :refer [deftest is]] + [eacl.authorization.evidence :as e] + [eacl.authorization.evidence-test :as fixtures] + [eacl.formal.qualified.model :as model] + [eacl.formal.qualified.model-test :as contract])) + +(defn production-for-worlds [worlds] + (reduce (fn [result world] + (let [term (reduce (fn [prior [i atom]] + (e/combine :intersection prior + (if (bit-test world i) atom (e/combine :exclusion true atom)))) + true [[0 fixtures/x] [1 fixtures/y]])] + (e/combine :union result term))) + false worlds)) + +(defn model-value [production] + (if (e/fault? production) + {:fault (set (map second (second (e/value production))))} + (model/value (fixtures/completions production [fixtures/x fixtures/y])))) + +(defn model-evidence [production] + {:value (model-value production) :end (e/valid-until production) :complete? (e/complete? production)}) + +(defn inputs [] + (let [values (concat (map production-for-worlds contract/subsets) + [(e/fault :eacl.qualifier/invalid :invalid) + (e/fault :eacl.caveat/evaluation :evaluator)])] + (vec (for [v values [end complete] [[nil true] [100 true] [100 false]]] + (e/with-certificate v end complete))))) + +(deftest symbolic-evidence-refines-completion-set-denotation + (doseq [worlds contract/subsets] + (is (= (model/value worlds) (model-value (production-for-worlds worlds))))) + (doseq [production (inputs)] + (is (= production (e/decode (e/encode production))))) + (doseq [op contract/operators a (inputs) b (inputs)] + (let [expected (model/combine contract/universe op (model-evidence a) (model-evidence b)) + actual (e/combine op a b) + missing (model/missing-fields contract/universe (:value expected) 2)] + (is (= (:value expected) (model-value actual))) + (is (= (:end expected) (e/valid-until actual))) + (is (= (:complete? expected) (e/complete? actual))) + (is (= (set (map ["x" "y"] missing)) (set (e/missing-fields actual))))))) + +(deftest reproducible-compound-evidence-campaign + ;; Independent oracle values are carried through the generated expression + ;; graph; they are never reconstructed from a production intermediate. + (let [seed 20260905 + random (java.util.Random. seed) + initial (mapv (fn [production] [production (model-evidence production)]) (inputs))] + (loop [step 0 pool initial] + (when (< step 2000) + (let [pick (fn [] (let [choices (if (.nextBoolean random) initial pool)] + (nth choices (.nextInt random (count choices))))) + [left expected-left] (pick) + [right expected-right] (pick) + op (nth contract/operators (.nextInt random (count contract/operators))) + expected (model/combine contract/universe op expected-left expected-right) + actual (e/combine op left right) + label (pr-str {:seed seed :step step :operation op})] + (is (= (:value expected) (model-value actual)) label) + (is (= (:end expected) (e/valid-until actual)) label) + (is (= (:complete? expected) (e/complete? actual)) label) + (is (= actual (e/decode (e/encode actual))) label) + (recur (inc step) (conj (if (< (count pool) 128) pool (subvec pool 1)) + [actual expected]))))))) diff --git a/formal/qualified/gate.lock.json b/formal/qualified/gate.lock.json new file mode 100644 index 00000000..50517b94 --- /dev/null +++ b/formal/qualified/gate.lock.json @@ -0,0 +1,114 @@ +{ + "version": 1, + "models": [ + "QualifiedEvidence.dfy", + "QualifiedTemporal.dfy", + "QualifiedReuse.dfy" + ], + "verificationTimeLimitSeconds": 15, + "proofResourceLimit": 5000000, + "verifiedObligations": 71, + "assertions": 607016, + "resourceInputs": { + "formal/qualified/model.clj": "d0fea4cfa49e51639a242daed8ac4d506a13d2d6aaf0930db9b3e2d6bab529d3", + "formal/qualified/model_test.clj": "6dc64b4006be889aa33286f713f0215c257d951c77555dbf8b78367a0085060b", + "formal/qualified/mutation_test.clj": "45395ac8411b27ce116651e95eed8dc3a69ffa793e2a022811df761cf08115e7", + "formal/qualified/mutations.edn": "4e617ac2dce38486c511c7965f6b39fd89ac5b77c85a73228e9ea071e810b9ac", + "formal/qualified/evidence_bridge.clj": "e4f754c64fbfe5919d630852d9c1472821879a6c76d556f5509371c3f55e8a89", + "modules/eacl/test/eacl/authorization/evidence_test.cljc": "8f1535f24433396ae00d10eb1d34bd371f2cba83fe03e99ec35e257b1bdf03e5", + "formal/qualified/qualification_bridge.clj": "a7c827bb9c60a885635591172f99676ecfed1706518a676329a6f0235359beba", + "modules/eacl/test/eacl/authorization/qualification_test.cljc": "e956b169ad9625c9c08e6e57d6d47f082778d3bf7f155b7c0999d7fbe3872fd2", + "formal/qualified/operator_bridge.clj": "7583c20f0a29e949719368fd67f71ca4167f76fabb0d5c04f0743facf2b32648", + "modules/eacl-datascript/test/eacl/operator/evaluator_test.cljc": "4bc874d19ef67408ec84924c11b05bcfbf1d1329cd465ab79725d084fce69d6b", + "modules/eacl-datascript/test/eacl/operator/vector_evaluator_test.cljc": "e66dab6b0c5f376db91cc5255a29307df78bd38bb64e26b30798b8ff22badf3d", + "modules/eacl/test/eacl/authorization/evidence_index_test.cljc": "6880dddab5bf70e1fd14ebf22a7535b00f1687fc75818ffa6eff40fac64b3251", + "formal/qualified/recursive_bridge.clj": "c4700e5a72794894091971219a229c815fd61102946702b431cbe1c0a234acc7", + "modules/eacl-datascript/test/eacl/operator/recursive_test.cljc": "d01eff2ea296d4734f8b3818613e6b16f668d62b66369312debbac74cdd5d3f1", + "formal/qualified/stable_route_bridge.clj": "79057574de4fa3ee2dc0aba63166037a3648df51f9060b2d3bdb597ee7b85577", + "modules/eacl/test/eacl/engine/stable_route_evidence_test.cljc": "4e4a1990f3ff461539309919b14fc9bae8df9def37a61fc91085d96ed07c4d84", + "formal/qualified/production_mutations.clj": "baac0f4fcd354f8eca2a820ef282e9e6ff9e6a795835f23894ad5286a6883d5f", + "modules/eacl-datascript/test/eacl/engine/stable_route_native_evidence_test.cljc": "bbe7fb3ce769a0abf3c1bc3b471780fad44aa6ec262beb35335da8dfe29d108d", + "modules/eacl/test/eacl/engine/scan_cache_test.cljc": "59afc0cfe74af16b17801d18b513b86efd4634949f187a2fd58a656333d18925", + "modules/eacl-datascript/test/eacl/datascript/evaluation_clock_test.cljc": "6a53273d259a534c0feab05d8c5818c7da8685ec7757cfd436804c859cde3435", + "formal/qualified/seekable_bridge.clj": "3e55bcb6899513421d9e9c0ca58e1b0047d8d2a29ee4dd261f08969db87d5de2", + "modules/eacl-datascript/test/eacl/operator/seekable_evidence_test.cljc": "c0353060f8eff001dc144f5e433c3977b22a636749c26f76bb3f11bd92404f55", + "formal/qualified/lookup_bridge.clj": "a412922d87e1e23a19f8eea99976de415c7a4f12b7d90b4765129de38f34d999", + "modules/eacl-datascript/test/eacl/operator/lookup_evidence_test.cljc": "a2e18d742009c508839a7bf2396ad4cb503945e241fd199454363a599b709c1c", + "formal/qualified/arrow_bridge.clj": "71e8f613f3bda21a35f3dc88cb66fb5eb9869adce01ba65d8402d532e3fa6e10", + "modules/eacl-datascript/test/eacl/operator/arrow_evidence_test.cljc": "19ec8e7a2e745433ab6338e885ebb06e8c27faa92ddadf62769aca6b31622841", + "modules/eacl/test/eacl/authorization/data_test.cljc": "0fef8418f26e2d1e8092357caf6268fd5bf811ec83d752e2e57db2235b93818d", + "modules/eacl/test/eacl/relationships/edge_contract.cljc": "f289efdcb3e6f410209ea94819e079457df8d5c9a0e3a66026b87c2b113f74a0", + "modules/eacl/test/eacl/backend/v8_test.cljc": "042cd57add094a23c80c5e94d2621c65cd70a75837aec1063e3f322765b1cd9e", + "modules/eacl-datascript/test/eacl/datascript/caveat_schema_test.cljc": "102eae0bfb70f928a7a7c2ff3662dfe77a5e162643e16a97d6161ea3a1afdfce", + "modules/eacl-datomic/test/eacl/datomic/caveat_schema_test.clj": "c82b8a817a26a50e8782ad41c96650a245f31138e480b6b0c3ccc5430562dd79", + "modules/eacl-datahike/test/eacl/datahike/caveat_schema_test.clj": "cae038eeae2a3d7df747b1de7dc2cbac8c675e6e76138dac2797e364787ce36e", + "modules/eacl-datalevin/test/eacl/datalevin/caveat_schema_test.clj": "8305279820b19939fcf0ac4d395e5432503823d14d1bac388e1b419764622d96", + "formal/qualified/legacy_lookup_bridge.clj": "a228b0d56c6820c7b7f8b9294349c56e1d63a16cf9098220f292f9c909350706", + "modules/eacl-datascript/test/eacl/engine/least_path_evidence_test.cljc": "5a7879763f333b162903e73aa481029e9c3762be59d5eb896eba2018fda96ba6", + "modules/eacl/test/eacl/authorization/context_test.cljc": "a655c8bc3c2196c16ff9bb56460ffb6da919a314ace41e31111ea6ea4df1e52b", + "modules/eacl-datascript/test/eacl/datascript/caveat_context_test.cljc": "1a8a992eeca17194fc103bace28480f407aa11b37e37765fed14e904e9d905aa", + "modules/eacl/test/eacl/authorization/result_test.cljc": "495357fd33bffaf4df759a11d08a48ea32ea1bcbf3e00cb6e284eeae207c3b8f", + "modules/eacl/test/eacl/core_test.cljc": "08e1e7389a6bbf67dc3ed701f07214577957126856bf0c88ec69b5c17e4428d7", + "modules/eacl-datascript/test/eacl/datascript/qualified_check_test.cljc": "9655fe924a21555abe559fdae1d38be29b0af9c5c863a251a179b75488fecdb2", + "formal/qualified/discovery_model.clj": "40b5217acab3bf5d1fffecf55cfed75c6331e6f8d6cb01105560afd95f194b2a", + "formal/qualified/discovery_model_test.clj": "7b8235f04f0348e6b8717167d197266c2d9eae8e86de15f7e09bf16fcb5fb14d", + "formal/qualified/discovery_bridge.clj": "f22f144af46ce8017daca9235e6e2c43a4ef15b07593776aef2638ab6ea74bdf", + "modules/eacl/test/eacl/engine/stable_reducer_evidence_test.cljc": "885f0c3c9666095fd055eeaa18af089b6a945412795b34fd7d907c3169629f17", + "modules/eacl/test/eacl/engine/stable_page_evidence_test.cljc": "7de82cc0dd1099c634a85e318766177412010786e142a3f2021892816684f69b", + "modules/eacl-datascript/test/eacl/datascript/qualified_lookup_test.cljc": "fd707cbdf2a056c7e82af40c59e8e80be12ddec651aecefd09c5e5f04778f5a8", + "modules/eacl/test/eacl/relationships/mutations_test.cljc": "50ee4f3e2094924e51bc269db80fc1e284d8c97519640f8e329a1e7dc350a903", + "modules/eacl/test/eacl/caveats/public_write_contract.cljc": "f6c8a7a4c153aa5bd0c17863034ed929b8d3fc4bf9c05501f03114d65062a64c", + "modules/eacl/test/eacl/caveats/publication_batch_contract.cljc": "b13b03269d2e57853fe23d8540ab352378ee0c7cea977cd96624b0d624599a1f", + "modules/eacl-datascript/test/eacl/datascript/qualified_write_test.cljc": "eeee93881abc4eb0f145be2d48105ba4ffaf2c23faa69aa25569fdbdda7b414d", + "modules/eacl-datomic/test/eacl/datomic/qualified_write_test.clj": "0a2e6f643e12cc265d338296e1337b196a57f74f70fbf642ea274e1f14847362", + "modules/eacl-datahike/test/eacl/datahike/qualified_write_test.clj": "9de83ea26f342c45b7de420e3a1588af182919ecbc01b04d3a1a7c0868bf2d2e", + "modules/eacl/src/eacl/schema/qualification_admission.cljc": "f525a0d7faed752753c254df2de19548ff5c9b5dd55326c97a1db7af5963a34d", + "modules/eacl/test/eacl/caveats/schema_admission_test.cljc": "a9ed3898800f418fd583c45ae6776285aa47ec4f8b3362360b0c6d23def04e83", + "modules/eacl/test/eacl/caveats/schema_allowance_contract.cljc": "d20a2026260dbc9ec833fd106baa5e2342aadf8caee6ef148f50d732110117c4", + "modules/eacl/test/eacl/schema/relation_allowance_test.cljc": "9de0830710d62abdd89692133b24095b71b5a9497d2ae9ad036ffd272e389203", + "modules/eacl-datascript/test/eacl/datascript/qualified_schema_test.cljc": "a70ae0f1c73a1d5333135b158f8930fe093b641d2f98687c6755ac17e00cad3e", + "modules/eacl/src/eacl/relationships/inspection.cljc": "6e5f57e9b847d1040f51845b4419613320296867f718828035a19043758f0524", + "modules/eacl/test/eacl/authorization/inspection_test.cljc": "dd2c6013b2236085a07dfdc362095912369902e71e43435fd653b1c40a73fa0e", + "modules/eacl/test/eacl/caveats/inspection_contract.cljc": "052d8e05b7cfd82e29059576d2e9d76a80dd3aa16f573f38d9411e6fee9ec114", + "modules/eacl-datascript/test/eacl/datascript/qualified_inspection_test.cljc": "4ddef4cd36c3e8a1d55b0521311ce5f006e604a6104d8a3ff4b15adecc7729f4", + "modules/eacl/src/eacl/authorization/qualifier_cache.cljc": "9f14da0aed30f64701ee301c56cf508de26adb5398bd2c6a9b65bb12d315bc62", + "modules/eacl/test/eacl/authorization/qualifier_cache_test.cljc": "80b5d47f9587ee14b79300e202fd9d4d68407401431524b332dd01f7cadb9990", + "modules/eacl-datascript/test/eacl/datascript/qualifier_cache_test.cljc": "de0e81bf10ddd46cf8386d2b14b0de86e71c35958b855b31466e7089a9a7f2ef", + "modules/eacl/src/eacl/authorization/temporal.cljc": "5e22a9f45ae139c5e19496d96c36fff6052532e67b82fef8872dbba0ff9c1f10", + "modules/eacl/test/eacl/authorization/temporal_test.cljc": "595fc73aca4619a9321186f74fcde7b3b64bc69d302b4da52c04c3b2450edc0a", + "formal/qualified/temporal_bridge.clj": "727cdb6f789c728745ab8157328dabaa83be0191ce9fda5c5bbd52b4cdfc7f83", + "modules/eacl-datascript/test/eacl/datascript/qualified_cursor_test.cljc": "ee79e4efe9f89641f042df21bba0642ac141c66007ad8362d4cfc73726801ed3", + "modules/eacl/test/eacl/client/range_reuse_test.cljc": "9d4467fba8e2ce79ce71368f4d0c559a4c32860861d511c27436d58ee647bde7", + "modules/eacl-datascript/test/eacl/datascript/qualified_cache_trace_test.cljc": "a9ac9815bf5ad7780a7c67857d484198a7c5d168eb449d23b652e29b9e2006e8", + "modules/eacl/test/eacl/caveats/cache_trace_contract.cljc": "0b2aa2b41a4cbbd1756a80f9b4e965d3a51d3909cf08f7af5b8cd83248593a0d", + "modules/eacl/test/eacl/caveats/deletion_contract.cljc": "7d1d262a531fcb35351f7d0031816b25142c4991ba1c63aeee58c3faa21447e5", + "modules/eacl/src/eacl/relationships/staged.cljc": "a229ae77f5c40682f2be79fe820e2d2d014439b0374e49af37205d2d18a41bcc", + "modules/eacl/src/eacl/authorization/evidence.cljc": "c0d3b99080e254526d8eea036f6ed0aca5a3bed2e50e1f1187a62997c61fd521", + "modules/eacl/src/eacl/caveats/values.cljc": "904c3d7b3afde845c305b4bea3ae09ea4938ef40e83c9947f8eaeccc99bc683d", + "modules/eacl/src/eacl/secure_format.cljc": "c9e9358837d4ff4f3e1054744514f5d30a4041201de94b4d39bd5e617554abc1", + "modules/eacl-caveats-jvm/src/eacl/caveats/jvm.clj": "90c702138611b88a8671fc116e7241d5215b57527af289731ccaebe586628c65", + "modules/eacl/test/eacl/caveats/values_test.cljc": "dd80bd55d13ff91735cd7cf3685feb980f3f0754d1cf8c0d5398adb1ca8fe42e", + "modules/eacl/test/eacl/secure_format_test.cljc": "8df33bfb3e8b07f500f8a70068a5f8e6eebfdb4719501b60b762bd6e9a58293e", + "modules/eacl-caveats-jvm/test/eacl/caveats/jvm/evaluator_test.clj": "243a03471201fb6b92e3948be39e17b7d814db1d30bad6d45c7cad63873a8fd2", + "modules/eacl/src/eacl/relationships/qualifier.cljc": "1cccb1a0eab8e8e3e2ca8f8e9467b9ecce1ba3e3be0090c064d91c64dce04979", + "modules/eacl/src/eacl/caveats/definition.cljc": "422464af8b143bafd0fd3ea2b5b23b27866d6fb4b7ada9919e90428308d42f2b", + "modules/eacl/src/eacl/cache.cljc": "76fa911d3c6bad39553a3c9a67ade6272b17871ecc2518308326b8b5c438f98f", + "modules/eacl/src/eacl/client/orchestration.cljc": "c22a4703d7213c76aa75bcfeccfa6005d498fd2ecc5eb6c0ed2b40a694db62d9", + "modules/eacl-datomic/src/eacl/datomic/migrations/v7_to_v9.clj": "e869a3ad025e71c2b8643f02665c6c81ea3792dd05857e0ee56e377365da101d", + "modules/eacl-datahike/src/eacl/datahike/backend.clj": "e37f8e3d437cd4144bbef31801529b46f3d1f2ae295cd37f1fa80544918dcd0d", + "modules/eacl-datahike/src/eacl/datahike/migrations/v7_to_v9.clj": "2051c10d59e20fa526a4b7074e59bc7b6eb5226f097eed210a9dfaff19e5a1b3", + "modules/eacl-datahike/src/eacl/datahike/qualifiers.clj": "aa8df2f1cca95c975a43f570143b0a33ec22c8dfa731c2de447f78c5d4609ce3", + "modules/eacl-datalevin/src/eacl/datalevin/qualifiers.cljc": "ceae01082cb657863c329220487eacfffdc056d1bcff4ba176a858a47cd5c8f1", + "modules/eacl-datascript/src/eacl/datascript/migrations/v7_to_v9.cljc": "dc931ea95bebcf872794b2211bdf72e9ba1dd17cb313f0159968527a66c70048", + "modules/eacl-datascript/src/eacl/datascript/qualifiers.cljc": "06bcee005ebd444f2e425d6f28a6c8dedb53034069d4da3bc7d70c1ef1fa047e", + "modules/eacl-datomic/src/eacl/datomic/qualifiers.clj": "98914a1b5f171706dfe00e9784ddf7f54e659526824472079bf088e6d078fd3f", + "modules/eacl/src/eacl/engine/stable_page.cljc": "9dafd3873a6e4ce2e25ba26d6788ad90670636194b81f38b4770d073245c77c3", + "modules/eacl-datalevin/src/eacl/datalevin/core.cljc": "0eb49f82a2498c1fc7185131f862f76f565e6814b78e1f4ece3d6bc0b3e2d054", + "modules/eacl/src/eacl/engine/v8.cljc": "a7ed5ae4319e8301bff3d78d5bbc725dd93928b1a79e57ae3e9b7e9251896da0", + "modules/eacl/src/eacl/relationships/storage.cljc": "f4c44560c494a993ba098eafe0d1c9465fdc10530d6f6607ea840378ea7bf3f2", + "modules/eacl/src/eacl/relay.cljc": "3c7941c314f3ba10b10f3ab04cecad8a06a120de7ca21247fefa582225267238", + "modules/eacl/test/eacl/caveats/write_contention_contract.cljc": "2a6437a28b7662cafad68b6aec9a5c7a01062dc968c252a6aeeb935cf3a0eb1e", + "modules/eacl-datahike/src/eacl/datahike/core.clj": "57de2d56eb877ac4ff4af56a3ec1b5e169b8886789bc7dccd1da19c5e52f73f2", + "modules/eacl-datascript/src/eacl/datascript/core.cljc": "df4f192e65d33d9d16078c6f303d99f356fe59677cdbec892d8df9dc9e48b3d3" + } +} diff --git a/formal/qualified/hot-path-audit.md b/formal/qualified/hot-path-audit.md new file mode 100644 index 00000000..3ff0ad29 --- /dev/null +++ b/formal/qualified/hot-path-audit.md @@ -0,0 +1,32 @@ +# Qualified authorization hot-path review + +Review scope: Phase 3 changes from the merged Caveat foundation through the +qualified release candidate. The source-closure gate independently checks the +public request dependency ledger; its generated report remains under `target/`. + +| Path | Reviewed behavior | Executable check | +|---|---|---| +| Native Relationship scans | One endpoint stream, ordinary eid or compact `[eid qid]`; no second attribute merge | Shared edge contract and four native scan suites | +| Ordinary qualification | `qualification/qualify` returns directly for an ordinary eid, without realizing request memo or temporal state | Nil-eid instrumentation and ordinary benchmark read profiles | +| Qualified data | Selected-basis bounded entity reads; one request memo for qid, definition, and Relation; no serving ownership graph scan | Native qualification-data suites, data-read benchmark counters, missing/changed-content controls | +| Retained decoded data | One bounded store, exact basis or complete current content; no cached truth and no writer-certification assumption for arbitrary native writers | Qualifier-cache lifecycle and raw-writer traces | +| Caveat evaluation | Complete input uses the native CEL program once; incomplete input uses only the portable partial evaluator | `complete-engine-does-not-run-a-shadow-evaluator`, partial/expired compile suppression | +| Algebra | Definite Boolean operations avoid residual traversal; symbolic evidence retains bounded canonical algebra and fault propagation | Exhaustive operator bridges, compound-expression campaign, evidence wire differentials | +| Trusted time | One request sample, exclusive deadlines, sparse evidence ledger; no scheduler dependency | Clock, expiry, cache, and cursor conformance | +| Pagination | Existing traversal owns discovery, limits, frontier, and lookahead; certificate collection follows examined work | Stable-route and cursor bridges; no-fill-loop bounded-work tests | +| Result reuse | Exact qualified semantic scope and checked temporal interval; expired resident entries miss | Native cached/uncached traces and killed temporal mutants | +| Writes and deletion | Native publication guards both endpoint refs, owned qualifier cleanup, and Relation fences | Shared publication/batch/deletion contracts and stale-plan controls | + +No Phase 3 runtime path invokes the independent formal models or compares an +authorization result with a shadow traversal. Existing legacy generated-kernel +namespaces remain outside the public serving dependency closure. Offline +integrity scans and migration audits remain explicit administrative operations. +Deletion uses bounded submitted transaction batches; it does not claim that +adapters which materialize deletion input now stream their planning input. + +Profiling identified redundant canonical work in Boolean evidence serialization, +UTF-8 length measurement, host-context normalization, qualifier decoding, and +portable-plan compilation. The release candidate removes that work at the +validated data boundary. It retains strict wire decoding, aggregate input bounds, +complete content identity, and bounded program/data caches. Numerical acceptance +is recorded separately in the qualified authorization benchmark results. diff --git a/formal/qualified/legacy_lookup_bridge.clj b/formal/qualified/legacy_lookup_bridge.clj new file mode 100644 index 00000000..3a0c7879 --- /dev/null +++ b/formal/qualified/legacy_lookup_bridge.clj @@ -0,0 +1,43 @@ +(ns eacl.formal.qualified.legacy-lookup-bridge + "Exhaustive finite denotation and coordinate checks for native legacy plans." + (:require [clojure.test :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.engine.least-path-evidence-test :as fixture] + [eacl.formal.qualified.evidence-bridge :as bridge] + [eacl.formal.qualified.model :as model] + [eacl.formal.qualified.model-test :as contract] + [eacl.formal.qualified.stable-route-bridge :as temporal])) + +(defn oracle [permission subject resource a b time] + (let [leaves {:a (bridge/model-value (temporal/active a 100 time)) + :b (bridge/model-value (temporal/active b 110 time))}] + (reduce #(model/compose :union %1 %2) (model/value #{}) + (map (fn [path] (reduce #(model/compose :arrow %1 (leaves %2)) (model/value contract/universe) path)) + (fixture/path-roles permission subject resource))))) + +(deftest native-legacy-unions-refine-membership-order-and-temporal-evidence + (let [env (fixture/fixture) + inputs (remove evidence/fault? (take-nth 3 (bridge/inputs)))] + (doseq [permission [:direct :delegated :via :inherited] traversal [:forward :reverse] + width [1 2] a inputs b inputs] + (let [a (temporal/active a 100 99) b (temporal/active b 110 99) + asc (fixture/evaluate env permission traversal :asc width a b) + desc (fixture/evaluate env permission traversal :desc width a b) + names (if (= traversal :forward) ["d0" "d1" "d2"] ["u0" "u1"]) + at-time (fn [time] + (into {} (map (fn [name] + [(get-in env [:ids name]) + (if (= traversal :forward) + (oracle permission "u0" name a b time) + (oracle permission name "d1" a b time))])) names)) + expected (into {} (remove (comp empty? :worlds val)) (at-time 99))] + (is (= expected (into {} (map (juxt :value (comp bridge/model-value :evidence))) asc))) + (is (= (count expected) (count asc))) + (is (= (mapv #(select-keys % [:value :coords]) (reverse asc)) + (mapv #(select-keys % [:value :coords]) desc))) + (doseq [time [100 109 110 120]] + (let [expected (at-time time)] + (is (every? (fn [{:keys [value evidence]}] + (or (not (evidence/reusable? evidence 99 time)) + (= (get expected value) (bridge/model-value evidence)))) + (concat asc desc))))))))) diff --git a/formal/qualified/lookup_bridge.clj b/formal/qualified/lookup_bridge.clj new file mode 100644 index 00000000..846840a8 --- /dev/null +++ b/formal/qualified/lookup_bridge.clj @@ -0,0 +1,54 @@ +(ns eacl.formal.qualified.lookup-bridge + "Qualified general covers against completion-set denotations. Ascending + and descending continuation walks must agree on each least coordinate." + (:require [clojure.test :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.formal.qualified.evidence-bridge :as bridge] + [eacl.formal.qualified.model :as model] + [eacl.formal.qualified.seekable-bridge :as seekable] + [eacl.operator.lookup :as lookup] + [eacl.operator.lookup-evidence-test :as traversal] + [eacl.operator.seekable-evidence-test :as fixtures] + [eacl.relationships.edge :as edge])) + +(defn oracle [permission semantic i] + (if (= permission :either) + (model/compose :union (seekable/oracle :both semantic i) (seekable/oracle :allowed semantic i)) + (seekable/oracle permission semantic i))) + +(defn check-case! [{:keys [docs qids]} base permission traversal width policy a b] + (let [semantic (seekable/leaves a b 99) + positions (zipmap docs (range)) + options (assoc base :traversal traversal :page-size width :candidate-window 2 + :result-policy policy :direct-specializations? false) + resolve-edge (fn [_ _ compact] + (if compact (get semantic (get qids (edge/qualifier-id compact))) false)) + [rows descending counted] + (with-redefs-fn {#'qualification/qualify resolve-edge} + #(vector (traversal/drain options) + (traversal/drain (assoc options :order-direction :desc)) + (lookup/count-results options))) + expected (into {} (for [doc docs :let [value (oracle permission semantic (positions doc))] + :when (if (= policy :definite) + (= (model/worlds 2) (:worlds value)) (seq (:worlds value)))] + [doc value]))] + (is (= expected (into {} (map (juxt :value (comp bridge/model-value :evidence))) rows))) + (is (= (count expected) (count rows) (:count counted))) + (is (= (mapv (juxt :value :coords (comp evidence/value :evidence)) rows) + (mapv (juxt :value :coords (comp evidence/value :evidence)) (rseq descending)))) + (doseq [time [100 109 110]] + (let [later (seekable/leaves a b time)] + (is (every? (fn [row] + (or (not (evidence/reusable? (:evidence row) 99 time)) + (= (bridge/model-value (:evidence row)) + (oracle permission later (positions (:value row)))))) rows)))))) + +(deftest qualified-general-covers-and-continuations-refine-completion-sets + (let [env (seekable/fixture) + inputs (remove evidence/fault? (take-nth 3 (bridge/inputs)))] + (doseq [permission [:both :allowed :either]] + (let [base (fixtures/options env permission 99 {} :asc)] + (doseq [traversal [:forward :reverse] width [1 2] policy [:definite :detailed] + a inputs b inputs] + (check-case! env base permission traversal width policy a b)))))) diff --git a/formal/qualified/model.clj b/formal/qualified/model.clj new file mode 100644 index 00000000..7a2ef437 --- /dev/null +++ b/formal/qualified/model.clj @@ -0,0 +1,174 @@ +(ns eacl.formal.qualified.model + "Finite verification oracle only; never loaded by production. Residuals are + sets of Boolean completions, deliberately unlike the serving encoding." + (:require [clojure.set :as set] + [eacl.formal.caveats.model :as lifecycle])) + +(defn worlds [fields] (set (range (bit-shift-left 1 fields)))) +(defn value [xs] {:worlds xs}) +(defn fault [reason] {:fault #{reason}}) +(defn kind [universe x] + (cond (:fault x) :failure + (empty? (:worlds x)) :no + (= universe (:worlds x)) :has + :else :conditional)) + +(defn atom-value [universe field] + (value (set (filter #(bit-test % field) universe)))) + +(defn compose [op a b] + (if (or (:fault a) (:fault b)) + {:fault (set/union (:fault a #{}) (:fault b #{}))} + (value ((case op :union set/union :exclusion set/difference + (:intersection :arrow) set/intersection) + (:worlds a) (:worlds b))))) + +(defn missing-fields [universe x fields] + (if (= :conditional (kind universe x)) + (set (filter (fn [field] + (some (fn [w] + (not= (contains? (:worlds x) w) + (contains? (:worlds x) (bit-flip w field)))) + universe)) + (range fields))) + #{})) + +(defn before? [time end] (or (nil? end) (< time end))) +(defn meet [a b] (cond (nil? a) b (nil? b) a :else (min a b))) +(defn evidence [v end] {:value v :end end :complete? true}) +(defn no-new-faults? [before after] + (or (not (:fault after)) + (and (:fault before) (set/subset? (:fault after) (:fault before))))) + +(defn qualify [universe qualifiers qid time] + (if (nil? qid) + (evidence (value universe) nil) + (let [q (get qualifiers qid)] + (cond + (not (:valid? q)) (evidence (fault :invalid-qualifier) nil) + (not (before? time (:expiry q))) (evidence (value #{}) nil) + :else (evidence (:caveat q) (:expiry q)))))) + +(defn edge [universe state identity time] + (let [f (:forward state) r (:reverse state)] + (cond + (and (not (contains? f identity)) (not (contains? r identity))) + (evidence (value #{}) nil) + (or (not= (contains? f identity) (contains? r identity)) + (not= (get f identity) (get r identity))) + (evidence (fault :asymmetric-pair) nil) + :else (qualify universe (:qualifiers state) (get f identity) time)))) + +(defn needed [universe op a b] + (let [ak (kind universe (:value a)) bk (kind universe (:value b))] + (cond + (or (= :failure ak) (= :failure bk)) :both + (and (:complete? a) (= ak (if (= :union op) :has :no))) :left + (and (:complete? b) (= bk (if (#{:union :exclusion} op) :has :no))) :right + :else :both))) + +(defn combine [universe op a b] + (assoc (case (needed universe op a b) + :left (select-keys a [:end :complete?]) + :right (select-keys b [:end :complete?]) + :both {:end (meet (:end a) (:end b)) + :complete? (and (:complete? a) (:complete? b))}) + :value (compose op (:value a) (:value b)))) + +(defn evaluate [universe state tree time budget] + (letfn [(walk [[op a b] remaining] + (if (zero? remaining) + [(assoc (evidence (fault :work-limit) nil) :complete? false) 0] + (case op + :edge [(edge universe state a time) (dec remaining)] + :constant [(evidence a nil) (dec remaining)] + (let [[left n] (walk a (dec remaining)) + [right m] (walk b n)] + [(combine universe op left right) m]))))] + (first (walk tree budget)))) + +(defn lifecycle-state + "Connect the already certified storage transitions to the temporal oracle. + The extra semantic table stands for decoding valid native qualifier facts." + [state semantics] + (assoc state :qualifiers + (into {} (map (fn [[qid _]] [qid (get semantics qid)]) (:qualifiers state))))) + +(defn stored-transition [state action] (lifecycle/transition state action)) + +(defn recursive-step [universe base rules prior] + (reduce-kv + (fn [out node initial] + (assoc out node + (reduce (fn [acc [edge-evidence target]] + (combine universe :union acc + (combine universe :arrow edge-evidence (get prior target)))) + initial (get rules node)))) + {} base)) + +(defn fixed-point + "Bounded positive SCC iteration. Fault propagation is an additional finite + component; a stopped iteration is a typed failure, never a complete denial." + [universe base rules budget] + (loop [prior (zipmap (keys base) (repeat (evidence (value #{}) nil))) + remaining budget] + (if (zero? remaining) + {:fault :work-limit :complete? false} + (let [next (recursive-step universe base rules prior)] + (if (= next prior) + {:values next :complete? true} + (recur next (dec remaining))))))) + +(defn publishable? [e time] + (and (:complete? e) (not (:fault (:value e))) (before? time (:end e)))) + +(def scope-fields + [:source :schema :relations :qualifiers :context :evaluator :policy :abi :query]) + +(defn scope-valid? [scope] (every? #(contains? scope %) scope-fields)) + +(defn accept-cache? [universe entry selected] + (let [{:keys [scope basis ancestors time]} selected + {:keys [start evidence]} entry] + (boolean + (and (:authenticated? entry) (scope-valid? scope) (= scope (:scope entry)) + (before? start (:end evidence)) + (or (= basis (:basis entry)) (contains? ancestors (:basis entry))) + (= (:kind entry) (kind universe (:value evidence))) + (not= :failure (:kind entry)) + (or (and (= basis (:basis entry)) (= time start)) + (and (:complete? evidence) (<= start time) (before? time (:end evidence)))))))) + +(defn cursor-decision [universe cursor selected] + (let [{:keys [entry mode token-expiry retained-complete?]} cursor + {:keys [time wall-time key-available? basis]} selected] + (cond + (not (and key-available? (< wall-time token-expiry))) :invalid-token + (not= (:scope entry) (:scope selected)) :scope-mismatch + (not (accept-cache? universe entry selected)) :restart-required + (= :pinned mode) (if (and (= basis (:basis entry)) (= time (:start entry))) + :continue :restart-required) + (= :live mode) (if (and (<= (:start entry) time) + (or (= (:start entry) time) + (and retained-complete? (get-in entry [:evidence :complete?]) + (before? time (get-in entry [:evidence :end]))))) + :continue :restart-required) + :else :invalid-token))) + +(defn cursor-certificate + "All retained roles, including skipped bans, participate. No unseen suffix + is fetched to invent completeness. Callers explicitly report missing roles." + [examined retained complete?] + {:end (reduce meet nil (map :end (concat examined retained))) + :complete? (and complete? (every? :complete? (concat examined retained)))}) + +(defn accept-decode? [old selected] + (and (= (select-keys old [:source :qid :format]) + (select-keys selected [:source :qid :format])) + (or (= (:basis old) (:basis selected)) + (and (:writer-certified? old) (:writer-certified? selected) + (integer? (:version old)) (pos? (:version old)) (= (:version old) (:version selected)) + (= (:relation old) (:relation selected))) + (and (seq (:content old)) (= (:content old) (:content selected)))))) + +(defn capture-time [high-water raw-sample] (max high-water raw-sample)) diff --git a/formal/qualified/model_test.clj b/formal/qualified/model_test.clj new file mode 100644 index 00000000..94b504ee --- /dev/null +++ b/formal/qualified/model_test.clj @@ -0,0 +1,193 @@ +(ns eacl.formal.qualified.model-test + (:require [clojure.set :as set] + [clojure.test :refer [deftest is testing]] + [eacl.formal.caveats.model :as lifecycle] + [eacl.formal.qualified.model :as m])) + +(def universe (m/worlds 2)) +(def operators [:union :intersection :exclusion :arrow]) +(def subsets (mapv (fn [mask] (set (filter #(bit-test mask %) universe))) (range 16))) +(def outcomes (into (mapv m/value subsets) [(m/fault :invalid) (m/fault :evaluator)])) + +(defn boolean-compose [op a b] + (case op :union (or a b) :intersection (and a b) + :arrow (and a b) :exclusion (and a (not b)))) + +(deftest exhaustive-residual-and-authoritative-fault-algebra + (doseq [op operators a outcomes b outcomes] + (let [actual (m/compose op a b) + expected (if (or (:fault a) (:fault b)) + {:fault (set/union (:fault a #{}) (:fault b #{}))} + (m/value (set (filter #(boolean-compose op (contains? (:worlds a) %) + (contains? (:worlds b) %)) universe))))] + (is (= expected actual)))) + (doseq [x outcomes] + (is (= (= :has (m/kind universe x)) (= x (m/value universe))))) + (is (= #{0} (m/missing-fields universe (m/atom-value universe 0) 2))) + (is (= #{1} (m/missing-fields universe (m/atom-value universe 1) 2))) + (let [residual (m/compose :exclusion (m/atom-value universe 0) (m/atom-value universe 1))] + (is (= #{0 1} (m/missing-fields universe residual 2))))) + +(def qualifier-cases + (vec (for [v [(m/value universe) (m/value #{}) (m/atom-value universe 0) (m/fault :evaluator)] + expiry [nil 1 3]] + {:valid? true :caveat v :expiry expiry}))) + +(defn certificate-contract? [op a b start later incomplete] + (let [at (fn [q time] (m/qualify universe {1 q} 1 time)) + x (cond-> (at a start) (contains? incomplete :left) (assoc :complete? false)) + y (cond-> (at b start) (contains? incomplete :right) (assoc :complete? false)) + result (m/combine universe op x y) + future (m/compose op (:value (at a later)) (:value (at b later)))] + (or (not (and (:complete? result) (<= start later) (m/before? later (:end result)))) + (= (:value result) future)))) + +(deftest temporal-witness-certificates-cover-all-outcome-roles + (doseq [op operators a qualifier-cases b qualifier-cases + start [0 1 3] later [0 1 2 3 4] + incomplete [#{} #{:left} #{:right} #{:left :right}]] + (is (certificate-contract? op a b start later incomplete))) + (doseq [q qualifier-cases start [0 1 3] later [0 1 2 3 4] + :when (<= start later)] + (is (m/no-new-faults? (:value (m/qualify universe {1 q} 1 start)) + (:value (m/qualify universe {1 q} 1 later))))) + (let [permanent (m/evidence (m/value universe) nil) + temporary (m/evidence (m/atom-value universe 0) 1) + union (m/combine universe :union permanent temporary)] + (is (nil? (:end union))) + (is (m/publishable? union 1000))) + (is (not (m/publishable? (assoc (m/evidence (m/value universe) nil) :complete? false) 0)))) + +(deftest publication-and-one-captured-time + (let [identity [1 2 3 4 5] + prepared (:state (m/stored-transition lifecycle/empty-state + [:prepare nil 1 {:valid-until-ms 100} nil])) + semantics {1 {:valid? true :expiry 100 :caveat (m/value universe)}} + published (:state (m/stored-transition prepared [:publish identity 1 nil #{:app}])) + state (m/lifecycle-state published semantics) + grant [:edge identity] + ban [:exclusion [:constant (m/value universe)] grant] + check-at (fn [s tree time budget] + (m/kind universe (:value (m/evaluate universe s tree time budget))))] + (doseq [time [99 100 101]] + (let [before (m/lifecycle-state prepared semantics)] + (is (= (m/value #{}) (:value (m/edge universe before identity time)))))) + (is (= (:forward published) (:reverse published))) + (is (= #{:app} (:facts published))) + (is (< (:generation prepared) (:generation published))) + (doseq [[time expected] [[99 :has] [100 :no] [101 :no]]] + (is (= expected (check-at state grant time 10)))) + (is (= :no (check-at state ban 99 10))) + (is (= :has (check-at state ban 100 10))) + (is (= :failure (check-at (update state :qualifiers dissoc 1) ban 99 10))) + (is (= :failure (check-at (assoc state :reverse {}) grant 99 10))) + (is (= :failure (check-at state ban 99 1))) + (let [q {1 {:valid? true :expiry 100 :caveat (m/fault :evaluator)}}] + (is (= :no (m/kind universe (:value (m/qualify universe q 1 100)))))) + (is (= [90 110 110 120] (rest (reductions m/capture-time 0 [90 110 95 120])))))) + +(defn world-reachable [base edges world] + (loop [reached (set (for [[node e] base :when (contains? (get-in e [:value :worlds]) world)] node))] + (let [next (into reached (for [[head pairs] edges [edge target] pairs + :when (and (contains? reached target) + (contains? (get-in edge [:value :worlds]) world))] head))] + (if (= next reached) reached (recur next))))) + +(deftest finite-positive-recursion-agrees-with-independent-reachability + (doseq [mask (range 512) time [0 1 3]] + (let [base {0 (m/qualify universe {1 {:valid? true :expiry 3 :caveat (m/atom-value universe 0)}} 1 time) + 1 (m/evidence (m/value #{}) nil) 2 (m/evidence (m/value #{}) nil)} + edges (into {} (for [head (range 3)] + [head (vec (for [target (range 3) :when (bit-test mask (+ (* head 3) target))] + [(m/qualify universe {1 {:valid? true :expiry (when (= target 2) 1) + :caveat (m/atom-value universe 1)}} 1 time) + target]))])) + result (m/fixed-point universe base edges 32)] + (is (:complete? result)) + (doseq [node (range 3)] + (is (= (set (filter #(contains? (world-reachable base edges %) node) universe)) + (get-in result [:values node :value :worlds])))))) + (let [base {0 (m/evidence (m/value universe) nil)}] + (is (= {:fault :work-limit :complete? false} (m/fixed-point universe base {} 0))))) + +(def scope {:source 1 :schema 2 :relations [3 4] :qualifiers [5 6] + :context [7 8] :evaluator [9 10] :policy :detailed :abi [9 3] :query [11]}) +(def selected {:scope scope :basis 10 :ancestors #{9} :time 90 + :wall-time 90 :key-available? true}) +(def entry {:authenticated? true :scope scope :basis 9 :start 90 + :evidence (m/evidence (m/value universe) 100) :kind :has}) +(def cursor {:entry entry :mode :live :token-expiry 200 :retained-complete? true}) + +(deftest cache-and-cursor-scope-is-complete + (is (m/accept-cache? universe entry selected)) + (doseq [field m/scope-fields] + (is (not (m/accept-cache? universe entry (assoc-in selected [:scope field] [:changed])))) + (is (= :scope-mismatch (m/cursor-decision universe cursor (assoc-in selected [:scope field] [:changed]))))) + (doseq [time [90 99 100 101 89]] + (is (= (<= 90 time 99) (m/accept-cache? universe entry (assoc selected :time time)))) + (is (= (if (<= 90 time 99) :continue :restart-required) + (m/cursor-decision universe cursor (assoc selected :time time))))) + (doseq [x outcomes reported-kind [:has :no :conditional :failure]] + (is (= (and (= reported-kind (m/kind universe x)) (not= :failure reported-kind)) + (m/accept-cache? universe (assoc entry :evidence (m/evidence x 100) :kind reported-kind) selected)))) + (doseq [time [90 91] basis [9 10]] + (is (= (and (= time 90) (= basis 9)) + (m/accept-cache? universe (assoc-in entry [:evidence :complete?] false) + (assoc selected :time time :basis basis))))) + (is (not (m/accept-cache? universe (assoc entry :authenticated? false) selected))) + (is (not (m/accept-cache? universe entry (assoc selected :ancestors #{})))) + (is (= :restart-required (m/cursor-decision universe (assoc cursor :retained-complete? false) + (assoc selected :time 91)))) + (is (= :continue (m/cursor-decision universe (assoc cursor :mode :pinned) + (assoc selected :basis 9 :time 90 :wall-time 110)))) + (is (= :restart-required (m/cursor-decision universe (assoc cursor :mode :pinned) + (assoc selected :basis 9 :time 110 :wall-time 110)))) + (is (= :invalid-token (m/cursor-decision universe cursor (assoc selected :key-available? false)))) + (is (= :invalid-token (m/cursor-decision universe cursor (assoc selected :wall-time 200))))) + +(deftest cursor-certification-includes-skipped-bans-and-lookahead + (let [skipped-ban (m/evidence (m/value #{}) 100) + emitted (m/evidence (m/value universe) nil) + lookahead (m/evidence (m/atom-value universe 0) 120) + certificate (m/cursor-certificate [skipped-ban emitted] [lookahead] true)] + (is (= {:end 100 :complete? true} certificate)) + (is (not (:complete? (m/cursor-certificate [emitted] [] false)))) + (is (not (:complete? (m/cursor-certificate [emitted] + [(assoc lookahead :complete? false)] true)))))) + +(deftest recursive-certificate-follows-expiring-subtracting-base-evidence + (doseq [mask (range 512)] + (let [build (fn [time] + (let [truth (m/evidence (m/value universe) nil) + ban (m/qualify universe {1 {:valid? true :expiry 100 :caveat (m/value universe)}} 1 time) + base {0 (m/evidence (m/value #{}) nil) + 1 (m/evidence (m/value #{}) nil) + 2 (m/combine universe :exclusion truth ban)} + rules (into {} (for [head (range 3)] + [head (vec (for [target (range 3) + :when (bit-test mask (+ (* head 3) target))] + [truth target]))]))] + (m/fixed-point universe base rules 32))) + initial (build 90)] + (doseq [time [99 100 101]] + (let [later (build time)] + (is (and (:complete? initial) (:complete? later))) + (doseq [node (range 3)] + (let [e (get-in initial [:values node])] + (is (or (not (m/before? time (:end e))) + (= (:value e) (get-in later [:values node :value]))))))))))) + +(deftest decode-cache-lifecycle-and-writer-proof + (let [prior {:source 1 :basis 10 :qid 100 :format 1 :version 11 + :relation 12 :writer-certified? true :content [1 2 3]} + later (assoc prior :basis 20) + opaque-prior (dissoc prior :content) + opaque-later (dissoc later :content)] + (is (m/accept-decode? prior later)) + (doseq [field [:source :qid :format]] + (is (not (m/accept-decode? prior (update later field inc))))) + (doseq [field [:version :relation]] + (is (not (m/accept-decode? opaque-prior (update opaque-later field inc))))) + (is (m/accept-decode? prior (assoc later :writer-certified? false))) + (is (not (m/accept-decode? prior (assoc later :writer-certified? false :content [1 2 4])))) + (is (not (m/accept-decode? (dissoc opaque-prior :version) (dissoc opaque-later :version)))))) diff --git a/formal/qualified/mutation_test.clj b/formal/qualified/mutation_test.clj new file mode 100644 index 00000000..da5f9373 --- /dev/null +++ b/formal/qualified/mutation_test.clj @@ -0,0 +1,107 @@ +(ns eacl.formal.qualified.mutation-test + (:require [clojure.edn :as edn] + [clojure.test :refer [deftest is]] + [eacl.formal.qualified.model :as m] + [eacl.formal.qualified.model-test :as contract])) + +(defn changed-qualify [alter] + (let [original m/qualify] + (fn [u qs qid time] (alter u qs qid time (original u qs qid time))))) + +(defn run-controls [] + (let [u contract/universe + q {:valid? true :expiry 100 :caveat (m/value u)} + point #(m/qualify u {1 q} 1 %) + truth (m/evidence (m/value u) nil) + temporary (m/evidence (m/value u) 100) + ban-denial (m/combine u :exclusion truth (point 90)) + late (assoc contract/selected :time 100) + prior {:source 1 :basis 10 :qid 1 :format 1 :version 1 + :relation 1 :writer-certified? true} + cache-with-omission (fn [field] + (let [original m/accept-cache?] + (fn [universe entry selected] + (original universe + (update entry :scope dissoc field) + (update selected :scope dissoc field)))))] + {:omitted-qualifier-read + (with-redefs [m/qualify (fn [universe _ _ _] (m/evidence (m/value universe) nil))] + (not= :no (m/kind u (:value (point 100))))) + :missing-qualifier-is-plain + (with-redefs [m/qualify (changed-qualify + (fn [universe qs qid _ result] + (if (contains? qs qid) result (m/evidence (m/value universe) nil))))] + (not= :failure (m/kind u (:value (m/qualify u {} 1 0))))) + :inclusive-expiry + (with-redefs [m/before? (fn [time end] (or (nil? end) (<= time end)))] + (not= :no (m/kind u (:value (point 100))))) + :expired-caveat-still-runs + (with-redefs [m/qualify (changed-qualify + (fn [_ qs qid _ result] + (if-let [caveat (:caveat (get qs qid))] (assoc result :value caveat) result)))] + (not= :no (m/kind u (:value (m/qualify u {1 (assoc q :caveat (m/fault :evaluator))} 1 100))))) + :conditional-is-true + (let [original m/kind] + (with-redefs [m/kind (fn [universe x] + (let [k (original universe x)] (if (= k :conditional) :has k)))] + (not= :conditional (m/kind u (m/atom-value u 0))))) + :fault-is-absence + (let [original m/compose] + (with-redefs [m/compose (fn [op a b] + (original op (if (:fault a) (m/value #{}) a) + (if (:fault b) (m/value #{}) b)))] + (not= :failure (m/kind u (m/compose :exclusion (m/value u) (m/fault :invalid)))))) + :maximum-instead-of-minimum-horizon + (with-redefs [m/meet (fn [a b] (if (and a b) (max a b) (or a b)))] + (not (contract/certificate-contract? :intersection q (assoc q :expiry 110) 90 100 #{}))) + :emitted-only-cursor-horizon + (with-redefs [m/cursor-certificate (fn [examined _ complete?] + {:end (:end (last examined)) :complete? complete?})] + (not= 100 (:end (m/cursor-certificate [ban-denial truth] [] true)))) + :incomplete-certificate-publishes + (with-redefs [m/publishable? (fn [e time] (m/before? time (:end e)))] + (m/publishable? (assoc truth :complete? false) 100)) + :time-only-removes-permission + (let [original m/accept-cache?] + (with-redefs [m/accept-cache? (fn [universe entry selected] + (or (= :no (:kind entry)) (original universe entry selected)))] + (m/accept-cache? u (assoc contract/entry :evidence ban-denial :kind :no) late))) + :context-omitted-from-cache + (with-redefs [m/accept-cache? (cache-with-omission :context) + m/scope-valid? (constantly true)] + (m/accept-cache? u contract/entry (assoc-in contract/selected [:scope :context] [123]))) + :evaluator-omitted-from-cache + (with-redefs [m/accept-cache? (cache-with-omission :evaluator) + m/scope-valid? (constantly true)] + (m/accept-cache? u contract/entry (assoc-in contract/selected [:scope :evaluator] [123]))) + :conditional-cache-alias + (let [original m/accept-cache?] + (with-redefs [m/accept-cache? (fn [universe entry selected] + (original universe (assoc entry :kind (m/kind universe (get-in entry [:evidence :value]))) selected))] + (m/accept-cache? u (assoc contract/entry :evidence (m/evidence (m/atom-value u 0) 100)) contract/selected))) + :qid-only-decode-cache + (with-redefs [m/accept-decode? (fn [a b] (= (:qid a) (:qid b)))] + (m/accept-decode? prior (assoc prior :source 2 :basis 20))) + :raw-clock-regresses + (with-redefs [m/capture-time (fn [_ sample] sample)] + (< (m/capture-time 110 90) 110)) + :live-cursor-pins-old-time + (let [original m/cursor-decision] + (with-redefs [m/cursor-decision (fn [universe cursor selected] + (original universe cursor (assoc selected :time (get-in cursor [:entry :start]))))] + (= :continue (m/cursor-decision u contract/cursor late)))) + :recursion-stops-before-certificate-fixpoint + (let [base {0 (m/evidence (m/value #{}) nil) 1 ban-denial} + rules {0 [[truth 1]]} + initial (zipmap [0 1] (repeat (m/evidence (m/value #{}) nil))) + first-step (m/recursive-step u base rules initial) + full (m/fixed-point u base rules 10)] + (and (= (update-vals first-step :value) (update-vals initial :value)) + (not= (get-in first-step [0 :end]) (get-in full [:values 0 :end]))))})) + +(deftest all-registered-controls-are-executed-and-killed + (let [registered (:controls (edn/read-string (slurp "formal/qualified/mutations.edn"))) + results (run-controls)] + (is (= 17 (count registered))) + (is (= (set registered) (set (keys results)))) + (doseq [[id killed?] results] (is (true? killed?) (name id))))) diff --git a/formal/qualified/mutations.edn b/formal/qualified/mutations.edn new file mode 100644 index 00000000..279fefef --- /dev/null +++ b/formal/qualified/mutations.edn @@ -0,0 +1,11 @@ +{:version 1 + :controls + [:omitted-qualifier-read :missing-qualifier-is-plain + :inclusive-expiry :expired-caveat-still-runs + :conditional-is-true :fault-is-absence + :maximum-instead-of-minimum-horizon :emitted-only-cursor-horizon + :incomplete-certificate-publishes :time-only-removes-permission + :context-omitted-from-cache :evaluator-omitted-from-cache + :conditional-cache-alias :qid-only-decode-cache + :raw-clock-regresses :live-cursor-pins-old-time + :recursion-stops-before-certificate-fixpoint]} diff --git a/formal/qualified/operator_bridge.clj b/formal/qualified/operator_bridge.clj new file mode 100644 index 00000000..e42845c8 --- /dev/null +++ b/formal/qualified/operator_bridge.clj @@ -0,0 +1,88 @@ +(ns eacl.formal.qualified.operator-bridge + "Refines the existing scalar machine independently of the separately + certified storage/qualification seam. Injected leaf evidence stands for + all bounded completion sets, faults, and temporal certificates." + (:require [clojure.test :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.core :as eacl] + [eacl.formal.qualified.evidence-bridge :as bridge] + [eacl.formal.qualified.model :as model] + [eacl.formal.qualified.model-test :as contract] + [eacl.operator.evaluator :as scalar] + [eacl.operator.evaluator-test :as fixtures] + [eacl.operator.plan :as plan] + [eacl.operator.vector-evaluator :as vector-evaluator])) + +(defn demand [op a b] + (if (or (= :failure (model/kind contract/universe (:value a))) + (= (if (= op :union) :has :no) (model/kind contract/universe (:value a)))) + a + (model/combine contract/universe op a b))) + +(defn cases [] + (mapv (fn [i value] + (let [[end complete] (nth [[nil true] [100 true] [100 false]] (mod i 3))] + (evidence/with-certificate value end complete))) + (range) (take-nth 3 (bridge/inputs)))) + +(defn fixture [schema objects relationships] + ((ns-resolve 'eacl.operator.evaluator-test 'fixture) schema objects relationships)) + +(defn object [type name] (eacl/spice-object type [:eacl/id name])) +(defn relation [env resource name subject] + (ds/entid (:db env) [:eacl.relation/resource-type+relation-name+subject-type [resource name subject]])) + +(defn assert-refinement! [expected actual] + (let [observed (bridge/model-evidence actual)] + (is (= (:value expected) (:value observed))) + (is (= (:end expected) (:end observed))) + (is (= (:complete? expected) (:complete? observed))) + (is (= actual (evidence/decode (evidence/encode actual)))))) + +(defn vector-result [options] + (first + (vector-evaluator/check-many-eids + (assoc options :candidates [(assoc (select-keys options [:subject-type :subject-eid :resource-eid]) + :direction :forward :resource-type (first (:root (:plan options))))])))) + +(deftest scalar-intersection-and-exclusion-refinement + (let [user (object :user "model/user") doc (object :document "model/doc") + env (fixture fixtures/direct-schema [user doc] + (mapv #(eacl/->Relationship user % doc) [:reader :writer :banned])) + sealed (plan/seal-plan (:adapter env) [:document :view]) + ids (mapv #(relation env :document % :user) [:reader :writer :banned]) + options {:adapter (:adapter env) :plan sealed :subject-type :user + :subject-eid ((:eid env) user) :resource-eid ((:eid env) doc) + :qualification ::modeled-leaves}] + (doseq [a (cases) b (cases) c (cases)] + (let [leaf (zipmap ids [a b c]) + expected (demand :exclusion + (demand :intersection (bridge/model-evidence a) (bridge/model-evidence b)) + (bridge/model-evidence c))] + (with-redefs [qualification/qualify (fn [_ r e] (if e (get leaf r) false))] + (assert-refinement! expected (scalar/check-eids options)) + (assert-refinement! expected (vector-result options))))))) + +(deftest scalar-arrow-refinement + (let [user (object :user "model/user") group (object :group "model/group") doc (object :document "model/doc") + env (fixture fixtures/arrow-schema [user group doc] + [(eacl/->Relationship user :reader doc) + (eacl/->Relationship group :parent doc) + (eacl/->Relationship user :member group) + (eacl/->Relationship user :disabled group)]) + sealed (plan/seal-plan (:adapter env) [:document :view]) + ids [(relation env :document :parent :group) (relation env :group :member :user) + (relation env :group :disabled :user) (relation env :document :reader :user)] + options {:adapter (:adapter env) :plan sealed :subject-type :user + :subject-eid ((:eid env) user) :resource-eid ((:eid env) doc) + :qualification ::modeled-leaves}] + (doseq [via (cases) member (cases) disabled (cases)] + (let [leaf (zipmap ids [via member disabled true]) + target (demand :exclusion (bridge/model-evidence member) (bridge/model-evidence disabled)) + expected (demand :intersection (bridge/model-evidence true) + (demand :arrow (bridge/model-evidence via) target))] + (with-redefs [qualification/qualify (fn [_ r e] (if e (get leaf r) false))] + (assert-refinement! expected (scalar/check-eids options)) + (assert-refinement! expected (vector-result options))))))) diff --git a/formal/qualified/production_mutations.clj b/formal/qualified/production_mutations.clj new file mode 100644 index 00000000..97a595a8 --- /dev/null +++ b/formal/qualified/production_mutations.clj @@ -0,0 +1,552 @@ +(ns eacl.formal.qualified.production-mutations + "Mutation controls at actual implementation seams, mapped to independent + conformance tests. Every control first checks its unmodified gate." + (:require [clojure.test :as t :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.data :as data] + [eacl.authorization.context :as context] + [eacl.authorization.context-test :as context-test] + [eacl.authorization.result :as result] + [eacl.authorization.result-test :as result-test] + [eacl.core :as core] + [eacl.core-test :as core-test] + [eacl.engine.v8 :as engine] + [eacl.authorization.batch :as batch] + [eacl.authorization.data-test :as data-test] + [eacl.request.counters :as counters] + [eacl.authorization.clock :as clock] + [eacl.authorization.evidence-test :as evidence-test] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualifier-cache :as qualifier-cache] + [eacl.authorization.temporal :as temporal] + [eacl.authorization.temporal-test :as temporal-test] + [eacl.authorization.qualifier-cache-test :as qualifier-cache-test] + [eacl.datascript.qualifier-cache-test :as public-qualifier-cache-test] + [eacl.authorization.qualification-test :as qualification-test] + [eacl.engine.scan-cache :as scan-cache] + [eacl.engine.scan-cache-test :as scan-test] + [eacl.engine.stable-reducer :as reducer] + [eacl.engine.stable-reducer-evidence-test :as discovery-test] + [eacl.engine.stable-page :as stable-page] + [eacl.engine.stable-page-evidence-test :as page-test] + [eacl.engine.stable-route :as stable-route] + [eacl.engine.stable-route-evidence-test :as stable-route-test] + [eacl.engine.least-path :as least-path] + [eacl.engine.least-path-evidence-test :as legacy-lookup-test] + [eacl.engine.stable-route-native-evidence-test :as native-test] + [eacl.datascript.evaluation-clock-test :as clock-test] + [eacl.datascript.caveat-context-test :as public-context-test] + [eacl.datascript.qualified-check-test :as public-point-test] + [eacl.datascript.qualified-lookup-test :as public-lookup-test] + [eacl.datascript.qualified-cursor-test :as public-cursor-test] + [eacl.datascript.qualified-cache-trace-test :as cache-trace-test] + [eacl.client.range-reuse-test :as range-test] + [eacl.client.range-reuse :as range-reuse] + [eacl.datascript.qualified-write-test :as public-write-test] + [eacl.datascript.qualified-schema-test :as public-schema-test] + [eacl.datascript.qualified-inspection-test :as inspection-test] + [eacl.authorization.inspection-test :as inspection-seam-test] + [eacl.relationships.inspection :as inspection] + [eacl.relay :as relay] + [eacl.datascript.schema :as datascript-schema] + [eacl.schema.qualification-admission :as admission] + [eacl.schema.relation-allowance :as allowance] + [eacl.datomic.qualified-write-test :as datomic-write-test] + [eacl.datascript.qualifiers :as datascript-qualifiers] + [eacl.relationships.mutations :as mutations] + [eacl.relationships.mutations-test :as mutations-test] + [eacl.relationships.staged :as staged] + [eacl.relationships.storage :as storage] + [eacl.cache :as cache] + [eacl.client.orchestration :as orchestration] + [eacl.formal.qualified.recursive-bridge :as recursive-bridge] + [eacl.formal.qualified.seekable-bridge :as seekable-bridge] + [eacl.formal.qualified.arrow-bridge :as arrow-bridge] + [eacl.operator.seekable :as seekable] + [eacl.operator.lookup :as lookup] + [eacl.operator.lookup-evidence-test :as lookup-test] + [eacl.operator.evaluator :as scalar] + [eacl.operator.arrow-evidence-test :as arrow-test] + [eacl.operator.seekable-evidence-test :as seekable-test] + [eacl.operator.recursive :as recursive] + [eacl.operator.vector-evaluator :as vector] + [eacl.operator.vector-evaluator-test :as vector-test])) + +(defn failures [gate] + (let [events (atom [])] + (with-redefs [t/report (fn [event] (when (#{:fail :error} (:type event)) (swap! events conj event)))] + (try (gate) + (catch Throwable error (swap! events conj {:type :error :actual error})))) + (count @events))) + +(defn mutation-cases [] + (let [cursor-certificate temporal/cursor-certificate + certificate qualification/certificate + retain-certificate @#'range-reuse/retain-certificate + externalize-relationships relay/externalize-relationship-page + inspection-window inspection/window-options + plan-schema datascript-schema/plan-schema-replacement + qualify qualification/qualify identity qualification/exact-reuse-identity + fetch reducer/adapter-fetch-fn descriptor scan-cache/descriptor-key + snapshot-opts @#'orchestration/snapshot-opts + head-evidence @#'seekable/head-evidence + count-categories @#'lookup/count-categories + accepted-emission @#'least-path/accepted-emission + stream-next @#'least-path/stream-next + legacy-acceptor @#'least-path/legacy-node-acceptor + least-env @#'least-path/make-env + check-many vector/check-cached-many-eids + check-stable stable-route/check-eids + validate-stable @#'stable-route/validate-known-witness! + aggregate batch/aggregate-counters + reusable? temporal/reusable? + point-answer temporal/point-answer + lookup-answer @#'cache/lookup-answer + lookup-rendered cache/lookup-rendered-page! + content-key qualifier-cache/content-key + exact-key qualifier-cache/exact-key + values-for @#'staged/values-for + collect data/collect + can? core/can? + check-evidence engine/check-evidence + check-result result/check-result + discovery-options stable-route/discovery-options + buffer-id @#'reducer/buffer-id + checkpoint-put stable-page/checkpoint-put! + page-options @#'stable-page/qualified-page-options + page-binding stable-page/execution-binding + count-page-categories @#'engine/count-page-categories + count-result result/count-result + inclusive-candidates @#'engine/fetch-inclusive-candidates + evaluate-emissions @#'lookup/evaluate-emissions + request-schema @#'orchestration/request-schema + enqueue @#'recursive/enqueue-evidence! + normalize-relationship mutations/normalize-relationship + coalesce-updates mutations/coalesce-updates + plan-entry @#'staged/plan-entry + plan-batch staged/plan-batch + plan-retractions staged/plan-retraction-batch + write-relationship! core/write-relationship!] + {:temporal-collection-resident-lookup-ignores-interval + {:gate #'temporal-test/collection-certificates-guard-resident-answers-and-replacement + :redefs {#'cache/lookup-answer (fn [store key _] (lookup-answer store key nil))}} + :temporal-rendered-page-ignores-captured-time + {:gate #'public-cursor-test/live-pages-and-counts-reuse-only-their-certified-time-interval + :redefs {#'cache/lookup-rendered-page! (fn [store options key] + (lookup-rendered store (assoc options :evaluation-time-ms 99) key))}} + :qualified-object-delete-leaves-owned-qualifiers + {:gate #'public-write-test/qualified-object-deletion-is-atomic-and-bounded + :redefs {#'staged/plan-retraction-batch (fn [& args] (filterv #(not= :db/retractEntity (first %)) (apply plan-retractions args)))}} + :qualified-object-delete-splits-endpoint-pairs + {:gate #'public-write-test/qualified-object-deletion-is-atomic-and-bounded + :redefs {#'staged/plan-retraction-batch (fn [& args] (filterv #(not (and (= :db/retract (first %)) (= storage/reverse-attribute (nth % 2 nil)))) (apply plan-retractions args)))}} + :qualified-object-delete-loses-selected-basis-guard + {:gate #'public-write-test/qualified-object-deletion-is-atomic-and-bounded + :redefs {#'staged/plan-retraction-batch (fn [& args] (let [tx (apply plan-retractions args)] (if (seq tx) (subvec tx 1) tx)))}} + :qualified-shared-denotation-identity-omits-time + {:gate #'cache-trace-test/qualified-cache-traces-match-uncached-authorization + :redefs {#'qualification/exact-reuse-identity (fn [request] (assoc (identity request) 2 99))}} + :qualified-answer-identity-omits-request-context + {:gate #'cache-trace-test/qualified-cache-traces-match-uncached-authorization + :redefs {#'qualification/exact-reuse-identity (fn [request] (assoc (identity request) 3 :omitted))}} + :qualified-cursor-loses-skipped-ban-deadline + {:gate #'public-cursor-test/an-expiring-ban-before-the-boundary-requires-a-new-lookup + :redefs {#'qualification/certificate (fn [request] (assoc (certificate request) :valid-until-ms nil))}} + :qualified-cursor-does-not-enforce-live-time + {:gate #'public-cursor-test/live-cursors-stop-at-expiry-and-pinned-cursors-retain-their-time + :redefs {#'temporal/cursor-time-valid? (constantly true)}} + :qualified-cursor-incomplete-certificate-becomes-complete + {:gate #'public-cursor-test/incomplete-certificates-allow-only-the-original-time-without-proof-building + :redefs {#'temporal/cursor-certificate (fn [mode time prior current] (assoc (cursor-certificate mode time prior current) :complete? true))}} + :qualified-cursor-loses-context-scope + {:gate #'public-cursor-test/context-and-result-policy-mismatches-fail-before-qualification + :redefs {#'relay/qualification-scope (constantly nil)}} + :qualified-range-drops-retained-certificate + {:gate #'range-test/qualified-range-slices-and-composition-preserve-the-entire-retained-certificate + :redefs {#'range-reuse/retain-certificate (fn [value source] (dissoc (retain-certificate value source) :qualification-certificate))}} + :qualified-cursor-accepts-malformed-certificate + {:gate #'temporal-test/cursor-certificates-are-closed-and-never-extend-prior-deadlines + :redefs {#'temporal/cursor-certificate-valid? (constantly true)}} + :temporal-point-resident-lookup-ignores-interval + {:gate #'temporal-test/resident-expiration-is-a-miss-and-later-publication-replaces-the-interval + :redefs {#'cache/lookup-answer (fn [store key _] (lookup-answer store key nil))}} + :temporal-point-incomplete-certificate-becomes-complete + {:gate #'temporal-test/expired-bans-and-incomplete-certificates-never-reuse-a-stale-decision + :redefs {#'temporal/reusable? (fn [answer time exact?] (reusable? (assoc answer :complete? true) time exact?))}} + :temporal-point-certificate-detaches-from-evidence + {:gate #'temporal-test/certificate-boundaries-and-completeness-are-independent-of-retention + :redefs {#'temporal/point-answer-valid? (constantly true)}} + :temporal-point-older-publication-displaces-newer-interval + {:gate #'temporal-test/resident-expiration-is-a-miss-and-later-publication-replaces-the-interval + :redefs {#'temporal/supersedes? (constantly true)}} + :temporal-point-loses-expiring-ban-witness + {:gate #'public-point-test/temporal-point-cache-expires-denials-and-retains-permanent-witnesses + :redefs {#'temporal/point-answer (fn [time value] (point-answer time (evidence/with-certificate value nil true)))}} + :qualifier-cache-omits-native-content + {:gate #'qualifier-cache-test/complete-content-proofs-detect-unstamped-mutations-and-deletion + :redefs {#'qualifier-cache/content-key (fn [basis rid qid version _ named relation] + (content-key basis rid qid version nil named relation))}} + :qualifier-cache-omits-definition-content + {:gate #'qualifier-cache-test/complete-content-proofs-detect-unstamped-mutations-and-deletion + :redefs {#'qualifier-cache/content-key (fn [basis rid qid version entity _ relation] + (content-key basis rid qid version entity nil relation))}} + :qualifier-cache-omits-source-lifecycle + {:gate #'qualifier-cache-test/exact-and-content-reuse-retain-data-and-reevaluate-each-request + :redefs {#'qualifier-cache/content-key (fn [basis & args] + (apply content-key (dissoc basis :source-lifecycle) args))}} + :qualifier-cache-omits-owning-relation-proof + {:gate #'qualifier-cache-test/exact-and-content-reuse-retain-data-and-reevaluate-each-request + :redefs {#'qualifier-cache/content-key (fn [basis rid qid version entity named _] + (content-key basis rid qid version entity named nil))}} + :qualifier-cache-exact-scope-omits-native-basis + {:gate #'public-qualifier-cache-test/public-decode-reuse-rechecks-unknown-native-writers + :redefs {#'qualifier-cache/exact-key (fn [basis & args] + (apply exact-key (dissoc basis :revision :exact-locator :backend-snapshot-id) args))}} + :public-write-drops-qualifier-input + {:gate #'mutations-test/qualified-write-input-is-one-named-caveat-with-bounded-context-and-expiry + :redefs {#'mutations/normalize-relationship + (fn [relationship] (select-keys (normalize-relationship relationship) [:subject :relation :resource]))}} + :batch-conflict-identity-discards-qualifier-intent + {:gate #'mutations-test/batch-identity-excludes-qualifiers-but-update-intent-does-not + :redefs {#'mutations/coalesce-updates + (fn [updates] (coalesce-updates (mapv #(update % :relationship select-keys [:subject :relation :resource]) updates)))}} + :batch-publication-omits-relation-fence + {:gate #'public-write-test/qualified-batches-publish-atomically + :redefs {#'datascript-qualifiers/relation-fence (fn [_ _] [])}} + :prepared-publication-leaks-unresolved-qid + {:gate #'public-write-test/public-qualified-writes-preserve-identity-and-commit-atomically + :redefs {#'staged/values-for (fn [identity qid] (values-for identity (when qid -404)))}} + :batch-publication-shares-qualifiers + {:gate #'datomic-write-test/qualified-batches-publish-atomically + :redefs {#'staged/unique-qualifiers! (fn [_] nil)}} + :prepared-handle-ignores-requested-value + {:gate #'public-write-test/public-qualified-writes-preserve-identity-and-commit-atomically + :redefs {#'staged/plan-entry + (fn [writer db entry datoms fences?] + (plan-entry writer db (dissoc entry :expected-value) datoms fences?))}} + :qualified-publication-omits-endpoint-guards + {:gate #'public-write-test/public-qualified-writes-preserve-identity-and-commit-atomically + :redefs {#'staged/plan-batch + (fn [writer db entries datoms] + (plan-batch writer db (mapv #(dissoc % :identity-guards) entries) datoms))}} + :flat-public-write-drops-qualifier-metadata + {:gate #'public-write-test/public-qualified-writes-preserve-identity-and-commit-atomically + :redefs {#'core/write-relationship! + (fn [target update] (write-relationship! target (select-keys update [:operation :subject :relation :resource])))}} + :detailed-lookups-drop-emission-evidence + {:gate #'public-lookup-test/public-qualified-lookups-preserve-detailed-results-and-definite-defaults + :redefs {#'engine/with-emission-evidence (fn [page _] page)}} + :definite-lookup-omits-final-policy-filter + {:gate #'public-lookup-test/public-qualified-lookups-preserve-detailed-results-and-definite-defaults + :redefs {#'least-path/make-env + (fn [options ctx] (assoc (least-env options ctx) :result-policy :detailed))}} + :filtered-lookahead-drops-recovered-root-evidence + {:gate #'public-lookup-test/public-qualified-pages-retain-evidence-through-cursors-filters-and-ranges + :redefs {#'engine/fetch-inclusive-candidates + (fn [rtype fetch bound limit _] (inclusive-candidates rtype fetch bound limit nil))}} + :qualified-filter-evidence-is-treated-as-boolean + {:gate #'public-lookup-test/qualified-relationship-filters-compose-with-whole-permission-evidence + :redefs {#'lookup/evaluate-emissions + (fn [options cover emissions] + (evaluate-emissions (dissoc options :accept-result-evidence) cover emissions))}} + :rendered-lookup-cache-accepts-inconsistent-residuals + {:gate #'public-lookup-test/detailed-lookup-cache-ingress-is-policy-and-residual-aware + :redefs {#'cache/rendered-page-entry-valid? (constantly true)}} + :public-counts-ignore-requested-policy + {:gate #'public-lookup-test/public-qualified-counts-distinguish-conditional-results-and-expiring-bans + :redefs {#'result/result-policy (constantly :detailed)}} + :public-counts-drop-conditional-category + {:gate #'public-lookup-test/public-qualified-counts-distinguish-conditional-results-and-expiring-bans + :redefs {#'result/count-result + (fn [value limit policy] + (dissoc (count-result value limit policy) :conditional-count))}} + :recursive-cover-does-not-read-qualified-edges + {:gate #'public-lookup-test/public-qualified-counts-distinguish-conditional-results-and-expiring-bans + :redefs {#'engine/structural-cover-fetch clojure.core/identity}} + :recursive-count-includes-lookahead-category + {:gate #'public-lookup-test/public-qualified-counts-distinguish-conditional-results-and-expiring-bans + :redefs {#'engine/count-page-categories + (fn [categories page _] (count-page-categories categories page nil))}} + :detailed-count-cache-accepts-inconsistent-categories + {:gate #'public-lookup-test/detailed-count-cache-ingress-requires-consistent-closed-categories + :redefs {#'cache/count-answer? (constantly true)}} + :qualified-checkpoint-accepts-incomplete-evidence + {:gate #'page-test/incomplete-or-faulty-qualified-checkpoints-fall-back-to-replay + :redefs {#'stable-page/valid-qualified-checkpoint (fn [_ checkpoint] checkpoint)}} + :qualified-checkpoint-reuses-after-its-interval + {:gate #'page-test/qualified-checkpoints-reuse-only-inside-the-certified-interval + :redefs {#'temporal/reusable? (constantly true)}} + :qualified-lookahead-loses-its-evidence + {:gate #'page-test/pending-qualified-lookahead-needs-no-repeat-probe + :redefs {#'stable-page/checkpoint-put! + (fn [store key checkpoint] (checkpoint-put store key (dissoc checkpoint :pending-evidence)))}} + :qualified-checkpoint-key-omits-request-scope + {:gate #'page-test/checkpoint-retention-partitions-qualified-request-scope + :redefs {#'stable-page/qualified-page-options + (fn [options] (assoc (page-options options) :checkpoint-key (:checkpoint-key options)))}} + :qualified-standalone-token-omits-request-scope + {:gate #'page-test/standalone-qualified-tokens-bind-time-context-and-policy + :redefs {#'stable-page/execution-binding + (fn [options] (dissoc (page-binding options) :qualification :result-policy))}} + :discovery-reuses-a-buffer-from-an-earlier-prefix + {:gate #'discovery-test/temporal-revisions-replay-a-buffered-prefix + :redefs {#'reducer/buffer-id (fn [item] (buffer-id (dissoc item :revision)))}} + :discovery-discards-propagated-prefix-evidence + {:gate #'discovery-test/conditional-prefixes-revisit-cycles-without-duplicate-discoveries + :redefs {#'reducer/inherit-evidence (fn [_ successors] successors)}} + :discovery-partial-path-claims-the-whole-root + {:gate #'discovery-test/conditional-prefixes-revisit-cycles-without-duplicate-discoveries + :redefs {#'stable-route/discovery-options + (fn [options direction] + (assoc (discovery-options options direction) :candidate-evidence-fn + (fn [_ item] (:evidence item true))))}} + :discovery-omits-retained-evidence-bounds + {:gate #'discovery-test/weighted-admission-and-resume-keep-operational-bounds + :redefs {#'reducer/evidence-size (constantly 0)}} + :discovery-repeats-a-known-direct-tuple + {:gate #'discovery-test/direct-discovery-witnesses-avoid-repeating-the-known-tuple + :redefs {#'stable-route/discovery-options + (fn [options direction] + (let [configured (discovery-options options direction) + complete (:candidate-evidence-fn configured)] + (assoc configured :candidate-evidence-fn + (fn [eid item] (complete eid (dissoc item :direct-evidence))))))}} + :expired-public-request-compiles-all-caveats + {:gate #'public-point-test/expired-public-points-never-compile-undemanded-caveats + :redefs {#'orchestration/request-schema + (fn [& args] + (binding [engine/*qualification* nil] (apply request-schema args)))}} + :public-conditional-result-becomes-a-grant + {:gate #'result-test/detailed-results-preserve-membership-and-residuals + :redefs {#'result/check-result (fn [value] + (let [answer (check-result value)] + (if (= :conditional-permission (:permissionship answer)) + (assoc answer :allowed? true) answer)))}} + :boolean-compatibility-erases-operational-errors + {:gate #'core-test/boolean-compatibility-requires-definite-grants-and-preserves-operational-errors + :redefs {#'core/can? (fn [& args] (try (apply can? args) (catch Throwable _ false)))}} + :public-point-routing-omits-qualification + {:gate #'public-point-test/public-point-routes-preserve-conditional-evidence-and-expiring-bans + :redefs {#'engine/check-evidence (fn [& args] + (binding [engine/*qualification* nil] + (apply check-evidence args)))}} + :legacy-inactive-stream-path-becomes-active + {:gate #'legacy-lookup-test/qualified-unions-keep-native-order-and-complete-node-evidence + :redefs {#'least-path/stream-next + (fn [ctx state] + (let [[value next-state] (stream-next ctx state)] + [value (cond-> next-state + (and value (:qualification ctx) (evidence/no? (:evidence next-state))) + (assoc :evidence true))]))}} + :legacy-partial-path-claims-whole-node + {:gate #'legacy-lookup-test/qualified-unions-keep-native-order-and-complete-node-evidence + :redefs {#'least-path/legacy-node-acceptor + (fn [options] + (let [accept (legacy-acceptor options)] + (fn [candidate] + (if-let [witness (:evidence-witness candidate)] (:evidence witness) (accept candidate)))))}} + :legacy-nil-path-forces-qualified-context + {:gate #'legacy-lookup-test/nil-qualifier-unions-retain-existing-coordinates-without-context-work + :redefs {#'least-path/make-env + (fn [options ctx] + (when (:qualification options) (qualification/exact-reuse-identity (:qualification options))) + (least-env options ctx))}} + :stable-known-witness-scope-ignored + {:gate #'stable-route-test/known-witness-scope-is-validated-before-a-definite-shortcut + :redefs {#'stable-route/validate-known-witness! :known-witness}} + :stable-known-path-is-reprobed + {:gate #'stable-route-test/known-direct-and-child-witnesses-complete-only-remaining-alternatives + :redefs {#'stable-route/check-eids (fn [options] (check-stable (dissoc options :known-witness)))}} + :stable-known-arrow-binding-is-reprobed + {:gate #'stable-route-test/known-arrow-binding-is-not-requalified-or-reprobed + :redefs {#'stable-route/validate-known-witness! + (fn [options] (some-> (validate-stable options) (assoc :intermediate -1)))}} + :qualification-data-outside-aggregate-budget + {:gate #'qualification-test/qualifier-data-consumes-aggregate-command-and-fact-budgets + :redefs {#'batch/aggregate-counters + (fn [& args] (assoc (apply aggregate args) + :commands 0 :fetched-values 0 :allocation-proxy 1))}} + :qualification-data-drops-assertion-version + {:gate #'data-test/bounded-data-preserves-unknown-fields-and-same-read-version + :redefs {#'data/collect (fn [& args] (assoc (apply collect args) :version nil))}} + :qualification-data-hides-unknown-field + {:gate #'data-test/bounded-data-preserves-unknown-fields-and-same-read-version + :redefs {#'data/collect (fn [& args] (update (apply collect args) :entity dissoc :unexpected/field))}} + :qualification-data-unmetered + {:gate #'qualification-test/adapter-data-is-shared-and-metered-within-one-request + :redefs {#'counters/add-fetched-values! (fn ([] nil) ([_] nil))}} + :qualification-data-refetched-across-roles + {:gate #'qualification-test/adapter-data-faults-remain-visible-and-do-not-refetch + :redefs {#'qualification/entity-data (fn [request eid] ((:lookup request) eid))}} + :qualifier-reference-ignored + {:gate #'qualification-test/exclusive-expiry-precedes-program-work + :redefs {#'qualification/qualify (fn [_ _ value] (some? value))}} + :expiry-boundary-retains-permission + {:gate #'qualification-test/exclusive-expiry-precedes-program-work + :redefs {#'qualification/qualify (fn [request relation value] + (qualify (update request :time dec) relation value))}} + :authoritative-failure-becomes-plain + {:gate #'qualification-test/authoritative-errors-survive-expiry-and-exclusion + :redefs {#'qualification/qualify (fn [& args] + (let [result (apply qualify args)] + (if (evidence/fault? result) true result)))}} + :conditional-becomes-truthy + {:gate #'native-test/native-compact-scans-and-memos-preserve-qualified-point-evidence + :redefs {#'evidence/has? (fn [value] (boolean (evidence/value value)))}} + :fault-becomes-absence + {:gate #'qualification-test/authoritative-errors-survive-expiry-and-exclusion + :redefs {#'evidence/fault (fn [_ _] false)}} + :latest-expiry-replaces-earliest + {:gate #'evidence-test/temporal-certificate-uses-decisive-evidence + :redefs {#'evidence/meet (fn [a b] (if (and a b) (max a b) (or a b)))}} + :expired-evidence-reused + {:gate #'evidence-test/temporal-certificate-uses-decisive-evidence + :redefs {#'evidence/reusable? (fn [value start time] + (and (evidence/complete? value) (not (evidence/fault? value)) (<= start time)))}} + :scan-shape-omitted-from-cache + {:gate #'scan-test/compact-and-ordinary-responses-never-share-memo-or-resident-prefixes-test + :redefs {#'scan-cache/descriptor-key (fn [d] (descriptor (dissoc d :include-qualifier?)))}} + :compact-flag-dropped-before-native-scan + {:gate #'native-test/native-compact-scans-and-memos-preserve-qualified-point-evidence + :redefs {#'reducer/adapter-fetch-fn (fn [adapter] + (let [inner (fetch adapter)] + (fn [d] (inner (dissoc d :include-qualifier?)))))}} + :context-omitted-from-exact-point-scope + {:gate #'vector-test/qualified-vectors-retain-alignment-and-exact-cache-scope + :redefs {#'qualification/exact-reuse-identity (fn [request] (assoc (identity request) 3 nil))}} + :unprojected-request-reaches-each-caveat + {:gate #'qualification-test/each-caveat-projects-request-fields-without-weakening-bound-context + :redefs {#'context/project (fn [prepared _] (context/value prepared))}} + :unused-context-fields-omitted-from-identity + {:gate #'context-test/whole-context-is-canonical-and-independent-of-one-parameter-set + :redefs {#'context/identity (constantly "omitted")}} + :public-context-validation-bypassed + {:gate #'public-context-test/invalid-context-fails-before-selection-even-on-warm-or-empty-requests + :redefs {#'context/prepare (let [empty-context (context/prepare {})] (constantly empty-context))}} + :time-omitted-from-exact-point-scope + {:gate #'vector-test/qualified-vectors-retain-alignment-and-exact-cache-scope + :redefs {#'qualification/exact-reuse-identity (fn [request] (assoc (identity request) 2 nil))}} + :evidence-witness-validation-bypassed + {:gate #'vector-test/exact-evidence-witnesses-avoid-rechecking-proven-nodes + :redefs {#'vector/validate-evidence-witnesses! (fn [& _] nil)}} + :cached-grant-hides-encountered-witness-fault + {:gate #'vector-test/exact-evidence-witnesses-avoid-rechecking-proven-nodes + :redefs {#'vector/demanded-witness-fault (constantly nil)}} + :raw-clock-regresses + {:gate #'clock-test/client-samples-once-and-snapshots-pin-time + :redefs {#'clock/clock clojure.core/identity}} + :snapshot-resamples-time + {:gate #'clock-test/client-samples-once-and-snapshots-pin-time + :redefs {#'orchestration/snapshot-opts (fn [runtime basis] + (dissoc (snapshot-opts runtime basis) :evaluation-time-ms))}} + :conditional-seekable-head-becomes-definite + {:gate #'seekable-test/direct-specializations-carry-exact-qualified-evidence + :redefs {#'seekable/head-evidence (fn [cursor] + (let [value (head-evidence cursor)] + (evidence/with-certificate true (evidence/valid-until value) + (evidence/complete? value))))}} + :seekable-emission-loses-expiry-certificate + {:gate #'seekable-bridge/direct-page-algebra-and-exhaustive-temporal-certificates + :redefs {#'seekable/head-evidence (fn [cursor] + (evidence/with-certificate (head-evidence cursor) nil true))}} + :definite-lookup-includes-conditional-results + {:gate #'seekable-test/lookup-and-count-project-exact-generator-evidence-without-rechecking + :redefs {#'lookup/result-policy (constantly :detailed)}} + :count-categories-include-lookahead-sentinel + {:gate #'seekable-test/detailed-count-cap-excludes-the-sentinel-from-category-counts + :redefs {#'lookup/count-categories (fn [categories entries remaining] + (count-categories categories entries + (when remaining (inc remaining))))}} + :general-cover-conditional-child-becomes-definite + {:gate #'lookup-test/general-cover-completes-conditional-nodes-before-emission + :redefs {#'least-path/accepted-emission + (fn [env node rule subject resource value coords proof] + (accepted-emission env node rule subject resource value coords + (evidence/with-certificate true (evidence/valid-until proof) + (evidence/complete? proof))))}} + :general-cover-discards-proven-node-evidence + {:gate #'lookup-test/a-generated-direct-node-is-not-probed-again-by-its-parent + :redefs {#'vector/check-cached-many-eids + (fn [options] + (check-many (update options :candidates + #(mapv (fn [candidate] (dissoc candidate :evidence-witnesses)) %))))}} + :arrow-witness-scope-validation-bypassed + {:gate #'arrow-test/a-known-arrow-binding-is-completed-without-rechecking-its-target + :redefs {#'scalar/validate-arrow-witness! (fn [_ _ _ witness] witness)}} + :arrow-rechecks-already-proven-binding + {:gate #'arrow-test/a-known-arrow-binding-is-completed-without-rechecking-its-target + :redefs {#'scalar/known-arrow-binding? (constantly false)}} + :arrow-witness-ignores-joint-residual + {:gate #'arrow-test/ordered-arrows-compose-whole-child-evidence-and-resume-bindings + :redefs {#'least-path/path-hit? (fn [_ _ other] (and (some? other) (not (evidence/no? other))))}} + :arrow-resume-drops-binding-evidence + {:gate #'arrow-test/ordered-arrows-compose-whole-child-evidence-and-resume-bindings + :redefs {#'least-path/resume-evidence! (constantly true)}} + :witness-skips-expired-prefix-before-alternating + {:gate #'arrow-bridge/expired-prefix-witness-work-is-bounded-by-the-physical-shorter-side + :redefs {#'least-path/stream-next (fn [ctx state] + (loop [state state] + (let [[value next-state] (stream-next ctx state)] + (if (and value (evidence/no? (get next-state :evidence true))) + (recur next-state) [value next-state]))))}} + :inspection-expiry-is-inclusive + {:gate #'inspection-seam-test/expiry-inspection-is-independent-of-caveat-outcomes + :redefs {#'inspection/active? (fn [time relationship] + (or (nil? (:valid-until-ms relationship)) + (<= time (:valid-until-ms relationship))))}} + :inspection-requires-a-definite-caveat + {:gate #'inspection-seam-test/expiry-inspection-is-independent-of-caveat-outcomes + :redefs {#'inspection/active? (fn [time relationship] + (and (or (nil? (:valid-until-ms relationship)) + (< time (:valid-until-ms relationship))) + (or (nil? (:caveat relationship)) + (true? (get-in relationship [:caveat-context "flag"])))))}} + :inspection-loses-aligned-qualifier-reference + {:gate #'inspection-test/stored-and-expiry-active-inspection-retain-metadata-without-caveat-evaluation + :redefs {#'inspection/row (fn [relationship _ _] relationship)}} + :inspection-externalizer-discards-metadata + {:gate #'inspection-test/stored-and-expiry-active-inspection-retain-metadata-without-caveat-evaluation + :redefs {#'relay/externalize-relationship-page + (fn [& args] (update (apply externalize-relationships args) :data + #(mapv (fn [r] (apply dissoc r mutations/qualifier-keys)) %)))}} + :inspection-fills-past-candidate-window + {:gate #'inspection-test/expiry-filter-keeps-the-existing-candidate-work-bound + :redefs {#'inspection/window-options + (fn [request filters options] + (assoc (inspection-window request filters options) :candidate-window 1000))}} + :inspection-cache-admits-noncanonical-metadata + {:gate #'mutations-test/cached-qualifier-metadata-must-be-closed-bounded-and-canonical + :redefs {#'mutations/canonical-qualifier-metadata? (constantly true)}} + :schema-admission-omits-evaluator + {:gate #'public-schema-test/schema-admission-requires-evaluator-before-writes-or-empty-reads + :redefs {#'admission/schema! (fn [schema _ publication] (admission/require-publication! publication) schema)}} + :schema-admission-omits-publication-capability + {:gate #'public-schema-test/qualified-publication-capability-is-required-at-schema-boundaries + :redefs {#'admission/require-publication! clojure.core/identity}} + :warm-answer-bypasses-publication-capability + {:gate #'public-schema-test/unsupported-publication-is-rejected-before-a-warm-answer + :redefs {#'qualification/require-publication! (constantly true)}} + :schema-tightening-ignores-stored-qualifiers + {:gate #'public-write-test/schema-alternatives-preserve-relation-identities-and-retained-data + :redefs {#'allowance/validate-existing! (constantly true)}} + :schema-alternatives-retract-retained-relation + {:gate #'public-write-test/schema-alternatives-preserve-relation-identities-and-retained-data + :redefs {#'allowance/entity-deletions :retractions}} + :schema-tightening-omits-relation-commit-fence + {:gate #'public-write-test/schema-alternatives-preserve-relation-identities-and-retained-data + :redefs {#'datascript-schema/plan-schema-replacement + (fn [db source options] (assoc (plan-schema db source options) :relation-commit-guards []))}} + :recursive-membership-stops-before-certificate-convergence + {:gate #'recursive-bridge/qualified-positive-scc-refinement-and-temporal-stability + :redefs {#'recursive/enqueue-evidence! + (fn [state head value limits counters] + (if (= (evidence/value value) (evidence/value (get (:facts state) head false))) + state + (enqueue state head value limits counters)))}}})) + +(deftest production-mutations-are-killed-by-conformance-gates + (let [cases (mutation-cases)] + (is (= 108 (count cases))) + (doseq [[id {:keys [gate redefs]}] (sort-by key cases)] + (is (zero? (failures gate)) (str id " unmodified gate must pass")) + (is (pos? (with-redefs-fn redefs #(failures gate))) (str id " must be detected"))))) diff --git a/formal/qualified/qualification_bridge.clj b/formal/qualified/qualification_bridge.clj new file mode 100644 index 00000000..892f9fca --- /dev/null +++ b/formal/qualified/qualification_bridge.clj @@ -0,0 +1,55 @@ +(ns eacl.formal.qualified.qualification-bridge + (:require [clojure.test :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as fixtures] + [eacl.formal.qualified.model :as model] + [eacl.relationships.qualifier :as qualifier])) + +(def universe #{0 1}) +(def kinds {:has-permission :has :no-permission :no + :conditional-permission :conditional :evaluation-failure :failure}) + +(defn input-db [expiry bound damage] + (let [semantic (cond-> {:caveat 2} + expiry (assoc :valid-until-ms expiry) + (some? bound) (assoc :caveat-context {"flag" bound})) + db (assoc fixtures/fixture 3 (qualifier/entity-data 3 semantic fixtures/parameters))] + (case damage + :none db + :missing (dissoc db 3) + :format (assoc-in db [3 qualifier/marker-attribute] 0) + :context (assoc-in db [3 qualifier/context-attribute] "malformed") + :allowance (assoc-in db [1 :eacl.relation/caveats] #{77}) + :definition (dissoc db 2) + :version db))) + +(defn oracle-value [request bound] + (cond + (= request :wrong-type) (model/fault :evaluation) + (some? bound) (model/value (if bound universe #{})) + (nil? request) (model/atom-value universe 0) + :else (model/value (if request universe #{})))) + +(deftest exact-qualification-refines-temporal-leaf-model + (doseq [expiry [nil 100] + time [99 100 101] + bound [nil false true] + request [nil false true :wrong-type] + damage [:none :missing :format :context :allowance :definition :version]] + (let [reads (atom {}) calls (atom 0) + expected (model/qualify universe + {3 {:valid? (= damage :none) :expiry expiry + :caveat (oracle-value request bound)}} 3 time) + context (if (nil? request) {} {"flag" (if (= request :wrong-type) "wrong" request)}) + selected (fixtures/request {:db (input-db expiry bound damage) :time time + :context context :reads reads :calls calls + :version (constantly (if (= damage :version) 0 7))}) + actual (qualification/qualify selected 1 [10 3]) + again (qualification/qualify selected 1 [10 3])] + (is (= (model/kind universe (:value expected)) (kinds (evidence/permissionship actual)))) + (is (= (:end expected) (evidence/valid-until actual))) + (is (= (:complete? expected) (evidence/complete? actual))) + (is (= actual again)) + (is (= 1 (get @reads 3))) + (is (= (if (and (= damage :none) (model/before? time expiry)) 2 0) @calls))))) diff --git a/formal/qualified/recursive_bridge.clj b/formal/qualified/recursive_bridge.clj new file mode 100644 index 00000000..16aa591e --- /dev/null +++ b/formal/qualified/recursive_bridge.clj @@ -0,0 +1,91 @@ +(ns eacl.formal.qualified.recursive-bridge + "Independent finite graph refinement of the production component worklist. + Native compact edges identify injected leaf evidence; the qualification + bridge separately certifies decoding and evaluation of those leaves." + (:require [clojure.test :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as q-fixtures] + [eacl.datascript.backend :as backend] + [eacl.datascript.impl :as impl] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.datascript.schema :as schema] + [eacl.formal.qualified.evidence-bridge :as bridge] + [eacl.formal.qualified.model :as model] + [eacl.formal.qualified.model-test :as contract] + [eacl.operator.plan :as plan] + [eacl.operator.recursive :as recursive] + [eacl.operator.recursive-test :as fixtures] + [eacl.relationships.edge :as edge] + [eacl.relationships.staged :as staged])) + +(defn fixture [] + (let [conn (schema/create-conn {})] + (schema/write-schema! conn fixtures/recursive-schema) + (ds/transact! conn (mapv #(hash-map :eacl/id (str "model/" %)) (range 4))) + (let [writer (qualifiers/writer conn) + ids (mapv #(ds/entid (ds/db conn) [:eacl/id (str "model/" %)]) (range 4)) + user (first ids) folders (subvec ids 1) + relation (fn [name type] + (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type [:folder name type]])) + direct (relation :direct :user) parent (relation :parent :folder) eligible (relation :eligible :user)] + (doseq [folder folders] + (staged/write! writer :create [:user user direct :folder folder] {:valid-until-ms 1000}) + (staged/write! writer :create [:user user eligible :folder folder] nil)) + (doseq [from folders to folders] + (staged/write! writer :create [:folder from parent :folder to] {:valid-until-ms 1000})) + (let [db (ds/db conn) adapter (backend/basis-adapter db {}) + qids (into {} (concat + (map-indexed (fn [i folder] + [(edge/qualifier-id (impl/direct-edge db :user user direct :folder folder)) [:base i]]) folders) + (for [[i from] (map-indexed vector folders) [j to] (map-indexed vector folders)] + [(edge/qualifier-id (impl/direct-edge db :folder from parent :folder to)) [:via j i]])))] + {:adapter adapter :plan (plan/seal-plan adapter [:folder :view]) :qids qids + :candidates (mapv #(hash-map :direction :forward :subject-type :user :subject-eid user :resource-eid %) folders)})))) + +(defn via-value [graph time target source] + (if (bit-test graph (+ (* target 3) source)) + (let [end (nth [nil 100 110] (mod (+ target source) 3))] + (if (model/before? time end) (evidence/with-certificate true end true) false)) + false)) + +(defn semantics [graph scenario time] + (let [conditional (bridge/production-for-worlds #{1 3}) + base (if (= scenario :grant) conditional + (evidence/combine :exclusion conditional + (if (< time 100) (evidence/with-certificate true 100 true) false)))] + {:base {0 base 1 false 2 false} + :via (into {} (for [target (range 3) source (range 3)] + [[target source] (via-value graph time target source)]))})) + +(defn oracle [semantic] + (:values + (model/fixed-point contract/universe + (update-vals (:base semantic) bridge/model-evidence) + (into {} (for [target (range 3)] + [target (mapv (fn [source] + [(bridge/model-evidence (get-in semantic [:via [target source]])) source]) + (range 3))])) 64))) + +(deftest qualified-positive-scc-refinement-and-temporal-stability + (let [{:keys [qids] :as options} (fixture)] + (doseq [graph (range 512) scenario [:grant :expiring-ban]] + (let [semantic (semantics graph scenario 99) + expected (oracle semantic) + request (q-fixtures/request) + result (with-redefs [qualification/qualify + (fn [_ _ compact] + (if-let [[kind a b] (get qids (edge/qualifier-id compact))] + (if (= kind :base) (get-in semantic [:base a]) + (get-in semantic [:via [a b]])) + (some? compact)))] + (recursive/evaluate-many (assoc options :qualification request :checkpoint? false))) + actual (:decisions result)] + (doseq [node (range 3)] + (is (= (:value (get expected node)) (bridge/model-value (nth actual node))))) + (doseq [time [100 109 110] + :let [later (oracle (semantics graph scenario time))] + node (range 3)] + (is (or (not (evidence/reusable? (nth actual node) 99 time)) + (= (bridge/model-value (nth actual node)) (:value (get later node)))))))))) diff --git a/formal/qualified/representation.md b/formal/qualified/representation.md new file mode 100644 index 00000000..2b46d389 --- /dev/null +++ b/formal/qualified/representation.md @@ -0,0 +1,37 @@ +# Compact edge allocation decision + +An ordinary edge remains its native integer eid. A qualified edge is +`[opposite-eid qualifier-eid]`. Scans retain the original ordering and bounds +by opposite eid. No qualifier record is allocated for ordinary edges. + +Measured on Java 25.0.3, macOS, using the current thread's allocated-byte +counter, 20,000 warmups and eleven batches of 10,000 materialized 100-edge +chunks. The baseline and production `eacl.relationships.edge/from-datom` +were run in the same nREPL. All samples within each row agreed. + +| Qualified edges | Eid-only baseline | Production sparse pairs | Always pairs/maps | +| --- | ---: | ---: | ---: | +| 0% | 1,008 bytes/chunk | 1,008 bytes/chunk | 7,408 bytes/chunk | +| 5% | 1,008 bytes/chunk | 1,328 bytes/chunk | 7,408 bytes/chunk | +| 10% | 1,008 bytes/chunk | 1,648 bytes/chunk | 7,408 bytes/chunk | + +The sparse representation adds 64 bytes per qualified edge and zero bytes +per ordinary edge on this JVM. This is a representation allocation result, +not an end-to-end latency claim. Native scan wrappers, request capture, +evidence composition, caches, and concentrated expired prefixes remain in +the Phase 3 authorization performance gate. + +Reproduce the alternatives with `representation_probe.clj` through nREPL; +pass `eacl.relationships.edge/from-datom` to `measure` for the production +implementation. Native conformance separately checks forward/reverse/direct +results and every ascending/descending inclusive/exclusive bound on all four +backends, including both Datahike attribute modes. + +The production nil-qid qualification seam was measured with the same probe: +project each ordinary datom through `edge/from-datom`, then call +`qualification/qualify` with nil request state. Both it and the eid-only +baseline used 1,008.0024 bytes per 100-edge chunk in all eleven samples. The +small fractional amount is fixed probe overhead; their allocation difference +is zero. The portable test also verifies that ordinary visits never realize +the request's qualifier memo. This measurement does not cover request setup +or end-to-end authorization latency. diff --git a/formal/qualified/representation_probe.clj b/formal/qualified/representation_probe.clj new file mode 100644 index 00000000..6c297598 --- /dev/null +++ b/formal/qualified/representation_probe.clj @@ -0,0 +1,45 @@ +(ns eacl.formal.qualified.representation-probe + "Development-only allocation probe for the compact scan representation. + Whole authorization latency gates run separately on an idle host.") + +(defn endpoint [datom] (nth (:v datom) 3)) +(defn compact-edge [datom] + (let [v (:v datom) eid (nth v 3) qid (nth v 4)] + (if (nil? qid) eid [eid qid]))) +(defn mapped-edge [datom] + (let [v (:v datom)] {:eid (nth v 3) :qualifier-eid (nth v 4)})) +(defn paired-edge [datom] + (let [v (:v datom)] [(nth v 3) (nth v 4)])) + +(defn fixture [qualified-count] + (mapv (fn [i] {:v [:user 1 :document (+ 100 i) (when (< i qualified-count) (+ 1000 i))]}) + (range 100))) + +(defn measure [project datoms] + (let [bean (java.lang.management.ManagementFactory/getThreadMXBean) + thread-id (.threadId (Thread/currentThread)) + bytes #(.getThreadAllocatedBytes ^com.sun.management.ThreadMXBean bean thread-id) + run #(into [] (map project) datoms) + consume (volatile! nil)] + (dotimes [_ 20000] (vreset! consume (run))) + (let [samples (vec (for [_ (range 11)] + (let [start (bytes)] + (dotimes [_ 10000] (vreset! consume (run))) + (/ (- (bytes) start) 10000.0))))] + {:median-bytes-per-100-edges (nth (vec (sort samples)) 5) + :samples samples :result-count (count @consume)}))) + +(defn run-probe! [output] + (let [results + (into (sorted-map) + (for [percent [0 5 10]] + [percent (into {} + (for [[name project] [[:eid-only endpoint] [:sparse-pair compact-edge] + [:always-pair paired-edge] [:always-map mapped-edge]]] + [name (measure project (fixture percent))]))])) + report {:java (System/getProperty "java.version") + :os (System/getProperty "os.name") + :scope :thread-allocation-only + :edges-per-chunk 100 :results results}] + (spit output (pr-str report)) + report)) diff --git a/formal/qualified/seekable_bridge.clj b/formal/qualified/seekable_bridge.clj new file mode 100644 index 00000000..46a23da8 --- /dev/null +++ b/formal/qualified/seekable_bridge.clj @@ -0,0 +1,86 @@ +(ns eacl.formal.qualified.seekable-bridge + "Direct native page generators versus independent completion-set algebra. + Exhaustive walks also check the aggregate examined certificate; partial + continuation frontiers are deliberately not certified by this bridge." + (:require [clojure.test :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.datascript.backend :as backend] + [eacl.datascript.impl :as impl] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.formal.qualified.evidence-bridge :as bridge] + [eacl.formal.qualified.model :as model] + [eacl.operator.seekable :as seekable] + [eacl.operator.seekable-evidence-test :as fixtures] + [eacl.relationships.edge :as edge] + [eacl.relationships.staged :as staged])) + +(defn fixture [] + (let [{:keys [conn user docs] :as env} (fixtures/fixture) + writer (qualifiers/writer conn) + relations (into {} (for [name [:reader :writer :banned]] + [name (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type [:doc name :user]])]))] + (doseq [doc docs relation (vals relations) [s r] [[user doc] [doc user]]] + (staged/write! writer (if (impl/direct-edge (ds/db conn) :user s relation :doc r) :replace :create) + [:user s relation :doc r] {:valid-until-ms 1000})) + (let [db (ds/db conn) + qids (into {} (for [[i doc] (map-indexed vector docs) [name relation] relations + [s r] [[user doc] [doc user]]] + [(edge/qualifier-id (impl/direct-edge db :user s relation :doc r)) [name i]]))] + (assoc env :db db :adapter (backend/basis-adapter db {}) :qids qids)))) + +(defn leaves [a b time] + (into {} (for [[role values end] [[:reader [a b true a] nil] + [:writer [b a a false] 110] + [:banned [b a a false] 100]] + [i value] (map-indexed vector values)] + [[role i] (if (model/before? time end) (evidence/with-certificate value end true) false)]))) + +(defn oracle [permission semantic i] + (model/compose (if (= permission :both) :intersection :exclusion) + (bridge/model-value (get semantic [:reader i])) + (bridge/model-value (get semantic [(if (= permission :both) :writer :banned) i])))) + +(defn drain [options] + (loop [boundary nil rows [] certificate true] + (let [page (seekable/page (assoc options :boundary boundary)) + emissions (:emissions page) + rows (into rows emissions) + certificate (evidence/combine :intersection certificate (:examined-certificate page))] + (if (:exhausted? page) + {:rows rows :certificate certificate} + (recur (:coords (peek emissions)) rows certificate))))) + +(defn check-case! [{:keys [docs qids] :as env} permission direction traversal width a b] + (let [positions (zipmap docs (range)) + semantic (leaves a b 99) + options (assoc (fixtures/options env permission 99 {} direction) :traversal traversal :width width) + resolve-edge (fn [_ _ compact] + (if compact (get semantic (get qids (edge/qualifier-id compact))) false)) + result (with-redefs-fn {#'qualification/qualify resolve-edge} #(drain options)) + rows (:rows result) + certificate (:certificate result) + ordered (if (= direction :asc) docs (rseq docs)) + expected (vec (for [doc ordered + :let [value (oracle permission semantic (positions doc))] + :when (seq (:worlds value))] + [doc value]))] + (is (= expected (mapv (fn [row] [(:value row) (bridge/model-value (:evidence row))]) rows))) + (doseq [time [100 109 110]] + (let [later (leaves a b time) + unchanged-row? (fn [row] + (or (not (evidence/reusable? (:evidence row) 99 time)) + (= (bridge/model-value (:evidence row)) + (oracle permission later (positions (:value row))))))] + (is (every? unchanged-row? rows)) + (is (or (not (evidence/reusable? certificate 99 time)) + (= (mapv #(oracle permission semantic %) (range 4)) + (mapv #(oracle permission later %) (range 4))))))))) + +(deftest direct-page-algebra-and-exhaustive-temporal-certificates + (let [env (fixture) + inputs (remove evidence/fault? (take-nth 3 (bridge/inputs)))] + (doseq [permission [:both :allowed] direction [:asc :desc] + traversal [:forward :reverse] width [1 2] a inputs b inputs] + (check-case! env permission direction traversal width a b)))) diff --git a/formal/qualified/stable_route_bridge.clj b/formal/qualified/stable_route_bridge.clj new file mode 100644 index 00000000..1dcb303b --- /dev/null +++ b/formal/qualified/stable_route_bridge.clj @@ -0,0 +1,108 @@ +(ns eacl.formal.qualified.stable-route-bridge + "Finite completion-set refinement of the existing bidirectional and DFS + routes. The oracle exhausts a small graph; production keeps its demand, + chunking, early exits, and shorter-side probing." + (:require [clojure.test :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.engine.stable-route-evidence-test :as fixture] + [eacl.relationships.edge :as edge] + [eacl.formal.qualified.evidence-bridge :as bridge] + [eacl.formal.qualified.model :as model] + [eacl.formal.qualified.model-test :as contract])) + +(defn active [value end time] + (if (model/before? time end) (evidence/with-certificate value end true) false)) + +(defn denotation [a b c time] + (model/compose :union + (model/compose :arrow (bridge/model-value (active a 100 time)) + (bridge/model-value b)) + (model/compose :arrow (bridge/model-value (active c 110 time)) + (bridge/model-value b)))) + +(deftest bidirectional-completion-sets-and-exclusive-certificates + (let [inputs (remove evidence/fault? (take-nth 3 (bridge/inputs))) + rows [[2 20 100 201] [3 20 100 202] [1 30 2 301] [1 30 3 302]]] + (doseq [rule [(fixture/arrow fixture/root 20 fixture/target) + {:rule :arrow-relation :resource-type :doc :via-relation-eid 20 + :intermediate-type :group :target-relation-eid 30 :target-subject-type :user}] + chunk [1 2] + a inputs b inputs c inputs] + (let [plan (fixture/sealed {fixture/root [rule] fixture/target [(fixture/direct fixture/target 30)]}) + answer (:answer (fixture/run plan rows {201 (active a 100 99) 202 (active c 110 99) + 301 b 302 b} + {:physical-chunk-size chunk}))] + (is (= (denotation a b c 99) (bridge/model-value answer))) + (is (= answer (evidence/decode (evidence/encode answer)))) + (doseq [time [100 109 110]] + (is (or (not (evidence/reusable? answer 99 time)) + (= (denotation a b c time) (bridge/model-value answer))))))))) + +(defn graph-semantics [graph time] + {:base {0 (bridge/production-for-worlds #{1 3}) 1 false + 2 (active (bridge/production-for-worlds #{2 3}) 100 time)} + :via (into {} (for [target (range 3) source (range 3)] + [[target source] + (if (bit-test graph (+ (* target 3) source)) + (active (bridge/production-for-worlds (if (= source 1) #{2 3} contract/universe)) + (nth [nil 100 110] (mod (+ target source) 3)) time) + false)]))}) + +(deftest known-path-evidence-refines-remaining-alternatives + (let [inputs (remove evidence/fault? (take-nth 3 (bridge/inputs)))] + (doseq [kind [:relation :self-permission :arrow-relation :arrow-permission] + chunk [1 2] a inputs b inputs] + (let [arrow? (contains? #{:arrow-relation :arrow-permission} kind) + rule (case kind + :relation (fixture/direct fixture/root 10) + :self-permission {:rule kind :node fixture/root :target-node fixture/target} + :arrow-relation {:rule kind :node fixture/root :resource-type :doc :via-relation-eid 20 + :intermediate-type :group :target-relation-eid 30 :target-subject-type :user} + :arrow-permission (fixture/arrow fixture/root 20 fixture/target)) + plan (fixture/sealed {fixture/root (if arrow? [rule] [rule (fixture/direct fixture/root 11)]) + fixture/target [(fixture/direct fixture/target (if arrow? 30 10))]}) + rows (if arrow? [[2 20 100 201] [3 20 100 202] [1 30 2 301] [1 30 3 302]] + [[1 10 100 101] [1 11 100 102]]) + a (active a 110 99) b (active b 120 99) + leaves (if arrow? {201 a 202 b 301 true 302 true} {101 a 102 b}) + options (assoc (fixture/known-options rule a (when arrow? 2)) :physical-chunk-size chunk) + result (fixture/run plan rows leaves options) + answer (:answer result) + oracle (fn [time] (model/compose :union (bridge/model-value (active a 110 time)) + (bridge/model-value (active b 120 time))))] + (is (= (oracle 99) (bridge/model-value answer))) + (is (not-any? #(contains? (if arrow? #{201 301} #{101}) + (edge/qualifier-id (second %))) + (:qualification-reads result))) + (doseq [time [109 110 119 120]] + (is (or (not (evidence/reusable? answer 99 time)) + (= (oracle time) (bridge/model-value answer))))))))) + +(defn graph-oracle [semantic] + (:values + (model/fixed-point contract/universe + (update-vals (:base semantic) bridge/model-evidence) + (into {} (for [target (range 3)] + [target (mapv (fn [source] + [(bridge/model-evidence (get-in semantic [:via [target source]])) source]) + (range 3))])) 64))) + +(deftest cyclic-path-prefixes-refine-positive-fixed-point + (let [plan (fixture/sealed {fixture/root [(fixture/direct fixture/root 10) + (fixture/arrow fixture/root 20 fixture/root)]}) + ids [100 2 3] + rows (into (mapv (fn [i] [1 10 (nth ids i) (+ 1000 i)]) (range 3)) + (for [target (range 3) source (range 3)] + [(nth ids source) 20 (nth ids target) (+ 2000 (* target 3) source)]))] + (doseq [graph (range 512)] + (let [semantic (graph-semantics graph 99) + leaves (into (into {} (map (fn [[i value]] [(+ 1000 i) value]) (:base semantic))) + (map (fn [[[target source] value]] [(+ 2000 (* target 3) source) value]) (:via semantic))) + expected (graph-oracle semantic) + later (mapv #(graph-oracle (graph-semantics graph %)) [100 109 110])] + (doseq [node (range 3)] + (let [answer (:answer (fixture/run plan rows leaves {:resource-eid (nth ids node)}))] + (is (= (:value (get expected node)) (bridge/model-value answer))) + (doseq [[time at-time] (map vector [100 109 110] later)] + (is (or (not (evidence/reusable? answer 99 time)) + (= (:value (get at-time node)) (bridge/model-value answer))))))))))) diff --git a/formal/qualified/temporal_bridge.clj b/formal/qualified/temporal_bridge.clj new file mode 100644 index 00000000..23b11fa8 --- /dev/null +++ b/formal/qualified/temporal_bridge.clj @@ -0,0 +1,37 @@ +(ns eacl.formal.qualified.temporal-bridge + (:require [clojure.test :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.temporal :as temporal] + [eacl.formal.qualified.model :as model])) + +(deftest point-answer-time-acceptance-refines-the-independent-reuse-model + (let [universe #{0 1} + scope (zipmap model/scope-fields (repeat 1))] + (doseq [[production oracle] [[true (model/value universe)] + [false (model/value #{})] + [(evidence/conditional [:field] ["field"]) (model/value #{1})]] + start [98 99 100] + end [101 nil] + complete? [true false] + time [97 98 99 100 101 102] + basis [1 2]] + (let [proof (evidence/with-certificate production end complete?) + answer (temporal/point-answer start proof) + entry {:authenticated? true :scope scope :basis 1 :start start + :evidence (assoc (model/evidence oracle end) :complete? complete?) + :kind (model/kind universe oracle)} + selected {:scope scope :basis basis :ancestors #{1} :time time}] + (is (= (model/accept-cache? universe entry selected) + (temporal/reusable? answer time (= 1 basis)))))))) + +(deftest cursor-time-acceptance-refines-the-independent-reuse-model + (let [universe #{0 1} scope (zipmap model/scope-fields (repeat 1))] + (doseq [mode [:live :pinned] start [98 99 100] end [101 nil] + complete? [true false] time [97 98 99 100 101 102]] + (let [proof (assoc (model/evidence (model/value universe) end) :complete? complete?) + cursor {:entry {:authenticated? true :scope scope :basis 1 :start start :evidence proof :kind :has} + :mode mode :token-expiry 1000 :retained-complete? complete?} + selected {:scope scope :basis 1 :ancestors #{} :time time :wall-time 100 :key-available? true} + certificate (temporal/cursor-certificate mode start nil (temporal/interval start end complete?))] + (is (= (= :continue (model/cursor-decision universe cursor selected)) + (temporal/cursor-time-valid? certificate time))))))) diff --git a/formal/qualified/verify-fast.sh b/formal/qualified/verify-fast.sh new file mode 100644 index 00000000..0c581007 --- /dev/null +++ b/formal/qualified/verify-fast.sh @@ -0,0 +1,135 @@ +#!/bin/sh +set -eu +repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd) +cd "$repo_root" +: "${EACL_NREPL_PORT:?EACL_NREPL_PORT must name the project dev nREPL}" +lock_value() { + node -e 'const fs=require("fs"); console.log(process.argv[2].split(".").reduce((v,k)=>v[k],JSON.parse(fs.readFileSync(process.argv[1],"utf8"))))' "$1" "$2" +} +dafny_version=$(lock_value formal/toolchain.lock.json tools.dafny.version) +dafny="${EACL_FORMAL_CACHE:-$repo_root/target/formal-tools}/dafny-$dafny_version/dafny/dafny" +output="${EACL_FORMAL_OUTPUT:-$repo_root/target/formal}/qualified" +mkdir -p "$output" +node formal/qualified/check-boundary.mjs +time_limit=$(lock_value formal/qualified/gate.lock.json verificationTimeLimitSeconds) +resource_limit=$(lock_value formal/qualified/gate.lock.json proofResourceLimit) +"$dafny" verify --verification-time-limit "$time_limit" --resource-limit "$resource_limit" \ + formal/dafny/QualifiedEvidence.dfy formal/dafny/QualifiedTemporal.dfy formal/dafny/QualifiedReuse.dfy \ + > "$output/proofs.log" 2>&1 || { cat "$output/proofs.log"; exit 1; } +cat "$output/proofs.log" +expected=$(lock_value formal/qualified/gate.lock.json verifiedObligations) +actual=$(awk '/Dafny program verifier finished with/ {total += $6} END {print total+0}' "$output/proofs.log") +if [ "$actual" -ne "$expected" ]; then + printf 'Qualified proof count changed: expected %s, observed %s\n' "$expected" "$actual" >&2 + exit 1 +fi +assertions=$(lock_value formal/qualified/gate.lock.json assertions) +sh bin/ci-nrepl-eval "$EACL_NREPL_PORT" \ + "(do + (load-file \"formal/caveats/model.clj\") + (load-file \"formal/qualified/model.clj\") + (load-file \"formal/qualified/model_test.clj\") + (load-file \"formal/qualified/mutation_test.clj\") + (load-file \"formal/qualified/discovery_model.clj\") + (load-file \"formal/qualified/discovery_model_test.clj\") + (require 'eacl.authorization.evidence :reload) + (require 'eacl.authorization.evidence-test :reload) + (load-file \"formal/qualified/evidence_bridge.clj\") + (require 'eacl.caveats.definition :reload) + (require 'eacl.authorization.data :reload) + (require 'eacl.authorization.data-test :reload) + (require 'eacl.authorization.context :reload) + (require 'eacl.authorization.context-test :reload) + (require 'eacl.authorization.qualification :reload) + (require 'eacl.authorization.qualification-test :reload) + (require 'eacl.authorization.qualifier-cache-test :reload) + (require 'eacl.authorization.temporal-test :reload) + (load-file \"formal/qualified/temporal_bridge.clj\") + (require 'eacl.datascript.qualifier-cache-test :reload) + (load-file \"formal/qualified/qualification_bridge.clj\") + (require 'eacl.backend.v8 :reload) + (require 'eacl.operator.evaluator :reload) + (require 'eacl.operator.evaluator-test :reload) + (require 'eacl.operator.arrow-evidence-test :reload) + (load-file \"formal/qualified/arrow_bridge.clj\") + (require 'eacl.backend.direct-membership :reload) + (require 'eacl.operator.vector-evaluator :reload) + (require 'eacl.operator.vector-evaluator-test :reload) + (require 'eacl.authorization.evidence-index :reload) + (require 'eacl.authorization.evidence-index-test :reload) + (require 'eacl.operator.recursive :reload) + (require 'eacl.operator.recursive-test :reload) + (load-file \"formal/qualified/operator_bridge.clj\") + (load-file \"formal/qualified/recursive_bridge.clj\") + (require 'eacl.engine.stable-reducer :reload) + (require 'eacl.engine.stable-route :reload) + (require 'eacl.engine.stable-reducer-evidence-test :reload) + (require 'eacl.engine.stable-page :reload) + (require 'eacl.engine.stable-page-evidence-test :reload) + (load-file \"formal/qualified/discovery_bridge.clj\") + (require 'eacl.engine.stable-route-evidence-test :reload) + (load-file \"formal/qualified/stable_route_bridge.clj\") + (require 'eacl.engine.least-path-evidence-test :reload) + (load-file \"formal/qualified/legacy_lookup_bridge.clj\") + (require 'eacl.operator.seekable :reload) + (require 'eacl.engine.least-path :reload) + (require 'eacl.operator.lookup :reload) + (require 'eacl.operator.seekable-evidence-test :reload) + (require 'eacl.operator.lookup-evidence-test :reload) + (load-file \"formal/qualified/seekable_bridge.clj\") + (load-file \"formal/qualified/lookup_bridge.clj\") + (require 'eacl.engine.scan-cache :reload) + (require 'eacl.engine.scan-cache-test :reload) + (require 'eacl.engine.stable-route-native-evidence-test :reload) + (require 'eacl.datascript.evaluation-clock-test :reload) + (require 'eacl.datascript.caveat-context-test :reload) + (require 'eacl.authorization.result :reload) + (require 'eacl.authorization.result-test :reload) + (require 'eacl.core-test :reload) + (require 'eacl.datascript.qualified-check-test :reload) + (require 'eacl.datascript.qualified-lookup-test :reload) + (require 'eacl.datascript.qualified-cursor-test :reload) + (require 'eacl.caveats.cache-trace-contract :reload) + (require 'eacl.datascript.qualified-cache-trace-test :reload) + (require 'eacl.client.range-reuse-test :reload) + (require 'eacl.relationships.mutations-test :reload) + (require 'eacl.caveats.deletion-contract :reload) + (require 'eacl.datascript.qualified-write-test :reload) + (require 'eacl.caveats.schema-admission-test :reload) + (require 'eacl.schema.relation-allowance-test :reload) + (require 'eacl.datascript.qualified-schema-test :reload) + (require 'eacl.authorization.inspection-test :reload) + (require 'eacl.datascript.qualified-inspection-test :reload) + (require 'eacl.datomic.qualified-write-test :reload) + (require 'eacl.datahike.qualified-write-test :reload) + (load-file \"formal/qualified/production_mutations.clj\") + (let [r (clojure.test/run-tests 'eacl.formal.qualified.model-test 'eacl.formal.qualified.mutation-test + 'eacl.formal.qualified.discovery-model-test 'eacl.formal.qualified.discovery-bridge + 'eacl.engine.stable-reducer-evidence-test 'eacl.engine.stable-page-evidence-test + 'eacl.authorization.evidence-test + 'eacl.formal.qualified.evidence-bridge 'eacl.formal.qualified.qualification-bridge + 'eacl.formal.qualified.operator-bridge 'eacl.authorization.evidence-index-test + 'eacl.formal.qualified.recursive-bridge 'eacl.formal.qualified.stable-route-bridge + 'eacl.formal.qualified.seekable-bridge + 'eacl.formal.qualified.lookup-bridge + 'eacl.formal.qualified.arrow-bridge + 'eacl.formal.qualified.legacy-lookup-bridge + 'eacl.authorization.data-test 'eacl.authorization.qualification-test + 'eacl.authorization.qualifier-cache-test 'eacl.datascript.qualifier-cache-test + 'eacl.authorization.temporal-test 'eacl.formal.qualified.temporal-bridge + 'eacl.authorization.context-test 'eacl.datascript.caveat-context-test + 'eacl.authorization.result-test 'eacl.core-test 'eacl.datascript.qualified-check-test + 'eacl.datascript.qualified-lookup-test 'eacl.datascript.qualified-cursor-test 'eacl.datascript.qualified-cache-trace-test 'eacl.client.range-reuse-test + 'eacl.relationships.mutations-test + 'eacl.datascript.qualified-write-test + 'eacl.caveats.schema-admission-test + 'eacl.schema.relation-allowance-test + 'eacl.datascript.qualified-schema-test + 'eacl.authorization.inspection-test + 'eacl.datascript.qualified-inspection-test 'eacl.datomic.qualified-write-test + 'eacl.datahike.qualified-write-test + 'eacl.formal.qualified.production-mutations)] + (when (or (pos? (+ (:fail r) (:error r))) (not= $assertions (:pass r))) + (throw (ex-info \"Qualified finite gate failed or assertion inventory changed\" r))) + r))" > "$output/finite.log" 2>&1 || { cat "$output/finite.log"; exit 1; } +cat "$output/finite.log" diff --git a/formal/smoke/clj/eacl/formal/state_trace_differential_test.clj b/formal/smoke/clj/eacl/formal/state_trace_differential_test.clj index 8a7a1bcb..fc7b35e3 100644 --- a/formal/smoke/clj/eacl/formal/state_trace_differential_test.clj +++ b/formal/smoke/clj/eacl/formal/state_trace_differential_test.clj @@ -10,6 +10,7 @@ [datomic.api :as d] [datascript.core :as ds] [eacl.cache :as shared-cache] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl] [eacl.datahike.core :as datahike] [eacl.datascript.backend :as datascript-backend] @@ -405,10 +406,10 @@ ["document-1" "document-2"]] [:public-cache-enabled (ids selected)] [:public-cache-disabled (ids fresh)]]})))) - (is (true? (:cached? selected)) - "a completed demand answer lifts across an unrelated basis under the scalar proof") + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? selected)) + "qualified answers require an exact basis; legacy answers may lift under scalar proof") (is (= traversal-before-selected traversal-after-selected) - "the managed demand hit performs no indexed traversal")) + "retained structural data avoids another indexed traversal")) (assert-public-authorization! label cached diff --git a/modules/eacl-caveats-jvm/README.md b/modules/eacl-caveats-jvm/README.md index b118b65c..e987dd1e 100644 --- a/modules/eacl-caveats-jvm/README.md +++ b/modules/eacl-caveats-jvm/README.md @@ -36,12 +36,14 @@ candidate library's string-unescaping divergences. Caller parameter names are also lowered. Operand errors are preserved by a fixed overload adapter, and unary `!` lowers to a reserved internal call because the native unary visitor otherwise changes missing-map-key errors into overload errors. Bindings and timestamp wrappers are constructed per invocation; -only successful parsed programs are shared. Native error values are detected +only successful portable plans and parsed programs are shared. Native error values are detected before Boolean extraction. Error messages and library objects never appear in portable outcomes. -The default retains at most 256 programs and builds at most four distinct -programs concurrently. Same-key misses share construction, failures wake all +The default retains at most 256 compiled artifacts and builds at most four +distinct artifacts concurrently. Portable plans and native programs share that +capacity; a fully compiled definition occupies two entries. Partial inputs +retain only the portable plan and never construct a native program. Same-key misses share construction, failures wake all waiters and are not retained, and distinct misses wait for capacity. Cache entries include canonical name, typed parameters, source, and implementation fingerprint; database entity IDs and request values are excluded. Schema edits diff --git a/modules/eacl-caveats-jvm/src/eacl/caveats/jvm.clj b/modules/eacl-caveats-jvm/src/eacl/caveats/jvm.clj index b9c3d8e6..0e93140e 100644 --- a/modules/eacl-caveats-jvm/src/eacl/caveats/jvm.clj +++ b/modules/eacl-caveats-jvm/src/eacl/caveats/jvm.clj @@ -11,7 +11,7 @@ [exoscale.cel.parser :as cel])) (def implementation - {:adapter "eacl.caveats.jvm/2" :literal-lowering "bindings/1" :error-propagation "overloads-and-not/1" + {:adapter "eacl.caveats.jvm/3" :literal-lowering "bindings/1" :error-propagation "overloads-and-not/1" :artifacts [["com.exoscale/cel-parser" "0.1.8" "2554b657e335524115c29f45f9f2b45d1a868a495ddb24d5ac8758acf2aa982d"] ["com.exoscale/antlr-cel" "0.1.1" "d8f3012b5f24d89d87dea9e1de826dd578d7b3a47e2b9038256af6e821929d99"] ["org.antlr/antlr4-runtime" "4.9.2" "120053628dd598d43cb7ac6b9ecc72529dfa5a5fd3292d37cf638a81cc0075f6"]]}) @@ -83,7 +83,13 @@ (defn- evaluate-definition [program-cache entity request bound] (try - (let [{:keys [parameters plan] :as compiled} (definition/decode-entity entity) + (let [content (definition/content-identity entity) + ;; Validate the complete current entity before using content identity. + ;; Portable plans and native programs share one bounded capacity; + ;; partial requests build only the portable plan. + {:keys [parameters plan] :as compiled} + (cache/get-or-build! program-cache [::portable-plan (:fingerprint capability) content] + #(definition/decode-entity entity)) prepared (partial/prepare-evaluation parameters plan (if (nil? request) {} request) (if (nil? bound) {} bound))] diff --git a/modules/eacl-caveats-jvm/test/eacl/caveats/jvm/evaluator_test.clj b/modules/eacl-caveats-jvm/test/eacl/caveats/jvm/evaluator_test.clj index 8f614916..fa6e1ac4 100644 --- a/modules/eacl-caveats-jvm/test/eacl/caveats/jvm/evaluator_test.clj +++ b/modules/eacl-caveats-jvm/test/eacl/caveats/jvm/evaluator_test.clj @@ -2,16 +2,45 @@ (:require [clojure.edn :as edn] [clojure.java.io :as io] [clojure.test :refer [deftest is testing]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] [eacl.caveats.definition :as definition] [eacl.caveats.evaluator :as evaluator] [eacl.caveats.jvm :as jvm] + [eacl.schema.qualification-admission :as admission] [eacl.caveats.partial :as partial] + [eacl.caveats.values :as values] + [eacl.relationships.qualifier :as qualifier] [exoscale.cel.expr :as expr] [exoscale.cel.parser :as cel])) (defn check [engine source parameters context bound] (evaluator/evaluate engine (definition/entity "check" parameters source) context bound)) +(deftest qualified-edge-uses-native-or-partial-evaluation-before-expiry + (let [parameters [["a" :bool] ["b" :bool]] + entities {1 {:eacl.relation/caveats #{2} :eacl.relation/allows-unqualified? false} + 2 (definition/entity "check" parameters "a && b") + 3 (qualifier/entity-data 3 {:caveat 2 :caveat-context {"a" true} + :valid-until-ms 100} parameters)} + engine (jvm/evaluator) + evaluate (fn [time context] + (qualification/qualify + (qualification/request {:time time :context context :evaluator engine + :entity #(get entities %) :version (constantly 7) + :basis {:source "jvm-test" :revision 1}}) + 1 [10 3]))] + (is (= :has-permission (evidence/permissionship (evaluate 99 {"a" false "b" true})))) + (is (= :no-permission (evidence/permissionship (evaluate 99 {"b" false})))) + (is (= :conditional-permission (evidence/permissionship (evaluate 99 {})))) + (is (= ["b"] (evidence/missing-fields (evaluate 99 {})))) + (is (= :evaluation-failure (evidence/permissionship (evaluate 99 {"a" "wrong" "b" true})))) + (with-redefs [cel/make-program (fn [& _] (throw (AssertionError. "expired native compile"))) + cel/eval-for (fn [& _] (throw (AssertionError. "expired native evaluation"))) + definition/decode-entity (fn [& _] (throw (AssertionError. "expired portable compile")))] + (is (false? (evaluate 100 {}))) + (is (false? (evaluate 101 {})))))) + (deftest qualified-corpus (let [engine (jvm/evaluator) corpus (edn/read-string (slurp (io/resource "eacl/caveats/corpus.edn")))] @@ -93,7 +122,7 @@ (is (= {:outcome :true} (evaluator/evaluate engine a {"x" true} nil))) (is (= {:outcome :false} (evaluator/evaluate engine b {"x" true} nil))) (is (= {:outcome :true} (evaluator/evaluate engine (assoc a :db/id 456) {"x" true} nil))) - (is (= 2 (:builds (jvm/cache-stats engine)))))) + (is (= 4 (:builds (jvm/cache-stats engine)))))) (deftest concurrent-native-program-is-binding-local (let [engine (jvm/evaluator) @@ -101,7 +130,37 @@ jobs (mapv (fn [n] (future (evaluator/evaluate engine entity {"a" n "b" 12} {}))) (range 24))] (is (= (mapv #(hash-map :outcome (if (< % 12) :true :false)) (range 24)) (mapv #(deref % 10000 :timeout) jobs))) - (is (= 1 (:builds (jvm/cache-stats engine)))))) + (is (= 2 (:builds (jvm/cache-stats engine)))))) + +(deftest compiled-plan-reuse-validates-current-content-and-shares-the-bound + (let [engine (jvm/evaluator {:max-entries 2}) + entity (definition/entity "cached" {"flag" :bool} "flag")] + (is (= {:outcome :true} (evaluator/evaluate engine entity {"flag" true} {}))) + (is (= {:outcome :error :reason :definition-shape} + (evaluator/evaluate engine (assoc entity :unexpected true) {"flag" true} {}))) + (is (= {:outcome :false} + (evaluator/evaluate engine (assoc entity :eacl.caveat/expression-source "!flag") + {"flag" true} {}))) + (is (<= (:entries (jvm/cache-stats engine)) 2)) + (is (= {:outcome :true} (evaluator/evaluate engine entity {"flag" true} {}))))) + +(deftest retained-plans-reuse-only-complete-validated-definition-content + (let [engine (jvm/evaluator) + entity (definition/entity "cached" {"flag" :bool} "flag")] + (is (= {:outcome :true} (evaluator/evaluate engine entity {"flag" true} {}))) + (with-redefs [values/decode-parameters (fn [& _] (throw (AssertionError. "redecoded retained parameters")))] + (is (= {:outcome :false} (evaluator/evaluate engine (assoc entity :db/id 456) {"flag" false} {})))) + (doseq [changed [(dissoc entity :eacl.caveat/parameters-payload) + (assoc entity :eacl.caveat/parameters-payload "[:eacl.caveat/parameters 1 []]") + (assoc entity :eacl.caveat/parameters-payload "malformed") + (assoc entity :eacl.caveat/parameters-payload (apply str (repeat 16385 "x"))) + (assoc entity :eacl.caveat/profile-version "unknown") + (assoc entity :eacl.caveat/name "not a name") + (assoc entity :eacl.caveat/expression-source "missing")]] + (is (= :error (:outcome (evaluator/evaluate engine changed {"flag" true} {}))))) + (is (= {:outcome :true} + (evaluator/evaluate engine (definition/entity "cached" {"flag" :int} "flag >= 0") + {"flag" 1} {}))))) (deftest composed-map-errors-retain-their-category (let [engine (jvm/evaluator)] @@ -113,3 +172,10 @@ ["\"x\".contains(m.k)" {"m" [:map :string :string]}]]] (is (= {:outcome :error :reason :missing-map-key} (check engine source parameters {"m" {}} {}))) (is (= {:outcome :true} (check engine (str "(" source ") || true") parameters {"m" {}} {})))))) + +(deftest registered-jvm-evaluator-satisfies-caveated-schema-admission + (let [schema {:relations [{:eacl.relation/caveats [[:eacl.caveat/name "enabled"]] + :eacl.relation/allows-unqualified? false}]}] + (is (identical? schema + (admission/schema! schema (evaluator/default-evaluator) + (admission/publication-descriptor :prepared)))))) diff --git a/modules/eacl-datahike/README.md b/modules/eacl-datahike/README.md index 71656d09..7f05e0ab 100644 --- a/modules/eacl-datahike/README.md +++ b/modules/eacl-datahike/README.md @@ -158,7 +158,7 @@ native child/subject order is not semantic. Configure structural ceilings with client-level `:permission-tree-limits`. ```clojure -{:deps {dev.eacl/eacl-datahike {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datahike {:mvn/version "9.0.0-SNAPSHOT"}}} ``` Its POM depends on `dev.eacl/eacl` at the exact same version, so consumers do @@ -185,10 +185,12 @@ statistics can be captured. ## Relationship storage 9 -This EACL v8 adapter uses five-slot endpoint pairs with a trailing nullable -`qualifier-eid`. This phase writes `nil` and raises `:eacl/unsupported-qualifier` -when serving encounters a qualifier. Upgrades are explicit and restartable; -ordinary client construction requires a completed target store. Follow the -[7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before starting clients. +This adapter uses five-slot endpoint pairs with a trailing nullable +`qualifier-eid`. V8 writes only `nil` and rejects non-nil qualifiers. V9 adds +[Caveats and expiring Relationships](../../docs/caveats.md) through coordinated +qualified activation; older readers must be drained first. Upgrades are explicit +and restartable, and client construction requires a completed target store. +Follow the [7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before +starting clients, then the v9 serving rollout guide before qualified writes. The adapter's `create-conn` helper explicitly bootstraps fresh stores. diff --git a/modules/eacl-datahike/src/eacl/datahike/backend.clj b/modules/eacl-datahike/src/eacl/datahike/backend.clj index 53416aeb..1bd7ef20 100644 --- a/modules/eacl-datahike/src/eacl/datahike/backend.clj +++ b/modules/eacl-datahike/src/eacl/datahike/backend.clj @@ -1,6 +1,7 @@ (ns eacl.datahike.backend "Datahike storage operations for the shared v8 authorization engine." (:require [datahike.api :as d] + [eacl.authorization.data :as qualification-data] [eacl.backend.source :as source] [eacl.backend.v8 :as backend] [eacl.datahike.db :as ddb] @@ -12,7 +13,9 @@ [java.util UUID])) (def adapter-capabilities - {:cursor #{:forward :reverse :opaque :authenticated :encrypted} + {:qualification #{qualification-data/capability} + :qualified-publication #{:atomic-prepared-v1} + :cursor #{:forward :reverse :opaque :authenticated :encrypted} :cache-proofs #{:ordered-generations :snapshot-bound :database-visible} :direct-membership-batch #{backend/direct-membership-batch-capability} :runtime #{:clj}}) @@ -248,7 +251,8 @@ :identity-contract (:identity-contract opts :selected-internal/current-external-injective-v2) - :capabilities adapter-capabilities + :capabilities (cond-> adapter-capabilities + (not (ddb/direct-writer? db)) (dissoc :qualified-publication)) :state {:db db :commit-id (commit-locator db) :parent-commit-ids (parent-locators db)} @@ -323,6 +327,15 @@ (fn [request] (direct-membership/direct-match-many? db request)) + :direct-edge + (fn [subject-type subject-id relation-id resource-type resource-id] + (impl/direct-edge db subject-type subject-id relation-id resource-type resource-id)) + + :qualification-data + (fn [eid] + (qualification-data/collect eid (d/datoms db {:index :eavt :components [eid]}) + #(if (keyword? %) % (get (d/entity db %) :db/ident)) true)) + :all-permission-nodes (fn [] (->> (ddb/avet-datoms db schema/permission-key-attr) @@ -333,6 +346,18 @@ (fn [relation-ids] (ordered-generation-frame db relation-ids))}}))) +(defn connection-source-scope + "The native source identity of a live connection, including externally + created memory connections whose DB values have no standalone identity." + [conn] + (let [database @conn + {:keys [backend id]} (store-identity database)] + {:source-id {:store-backend backend + :store-id (if (= :memory backend) + (some-> (connection-live-source-id conn database) str) + id)} + :branch (:branch (db-config database))})) + (defn source "Builds the borrowed immutable-basis source for one Datahike conn." [conn opts] @@ -340,13 +365,8 @@ ;; immutable value without a branch-head store operation. Construction ;; consumes only configuration needed for the source's static profile. static-db @conn - {:keys [backend id]} (store-identity static-db) - id (if (= :memory backend) - (some-> (connection-live-source-id conn static-db) str) - id) source-scope - {:source-id {:store-backend backend :store-id id} - :branch (:branch (db-config static-db))} + (connection-source-scope conn) source-lifecycle (fn [] (or (some-> (:runtime-lifecycle-state opts) diff --git a/modules/eacl-datahike/src/eacl/datahike/core.clj b/modules/eacl-datahike/src/eacl/datahike/core.clj index ef8a8f50..3e19a547 100644 --- a/modules/eacl-datahike/src/eacl/datahike/core.clj +++ b/modules/eacl-datahike/src/eacl/datahike/core.clj @@ -12,11 +12,13 @@ [eacl.datahike.backend :as datahike-backend] [eacl.datahike.db :as ddb] [eacl.datahike.impl :as impl] + [eacl.datahike.qualifiers :as qualifiers] [eacl.datahike.migrations.v7-to-v8 :as v7-to-v8] [eacl.datahike.schema :as schema] [eacl.datahike.storage :as target-storage] [eacl.relationships.upgrade :as storage-upgrade] [eacl.relationships.storage :as relationship-storage] + [eacl.relationships.staged :as staged] [eacl.schema.expression-policy :as expression-policy])) (def cursor->token cursor/cursor->token) @@ -114,6 +116,11 @@ (def ^:private api {:backend-id :datahike + :writer-max-attempts 8 + :writer-contention? staged/prepared-contention? + :qualified-writer #'qualifiers/writer + :qualified-publication-capability #'qualifiers/publication-capability + :qualified-plan #'qualifiers/plan :db d/db :entid ddb/entid :default-entid->object-id (fn [db eid] (:eacl/id (d/entity db eid))) @@ -138,11 +145,13 @@ ;; the impl suites) and REPL redefinition visible through the shared ;; orchestration. :schema {:read-schema #'schema/read-schema + :read-authorization-schema #'schema/read-authorization-schema :generation #'schema/current-schema-generation :plan-replacement #'schema/plan-schema-replacement :write-schema! #'schema/write-schema!} :impl {:validate-relationship-operation! #'impl/validate-relationship-operation! + :relationship-publication-input #'impl/relationship-publication-input :relationship-relation-id #'impl/relationship-relation-id :relation-coordinate relation-coordinate :tx-update-relationship #'impl/tx-update-relationship diff --git a/modules/eacl-datahike/src/eacl/datahike/db.clj b/modules/eacl-datahike/src/eacl/datahike/db.clj index b0670f65..e1ed4b6a 100644 --- a/modules/eacl-datahike/src/eacl/datahike/db.clj +++ b/modules/eacl-datahike/src/eacl/datahike/db.clj @@ -244,11 +244,30 @@ (endpoint-pair/identity-prefix value))) (defn checked-relationship-datoms - [db entity attr prefix cursor-eid direction] + ([db entity attr prefix cursor-eid direction] + (checked-relationship-datoms db entity attr prefix cursor-eid direction false)) + ([db entity attr prefix cursor-eid direction include-qualifier?] (endpoint-pair/checked-datoms - (eavt-tuple-prefix db entity attr storage/value-arity prefix cursor-eid direction))) + (eavt-tuple-prefix db entity attr storage/value-arity prefix cursor-eid direction) + include-qualifier?))) (defn checked-global-relationship-datoms - [db attr prefix cursor-eid direction] - (endpoint-pair/checked-datoms - (avet-tuple-prefix db attr storage/value-arity prefix cursor-eid direction))) + ([db attr prefix cursor-eid direction] + (checked-global-relationship-datoms db attr prefix cursor-eid direction false)) + ([db attr prefix cursor-eid direction include-qualifier?] + (endpoint-pair/checked-datoms + (avet-tuple-prefix db attr storage/value-arity prefix cursor-eid direction) + include-qualifier?))) + +(defn entity-facts [database eid] + (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) + (d/datoms database {:index :eavt :components [eid]}))) + +(defn entity-data [database eid] + (let [rows (entity-facts database eid)] + (when (seq rows) + (reduce (fn [result [a v]] + (let [attribute (if (keyword? a) a (:db/ident (d/entity database a)))] + (if (= :eacl.relation/caveats attribute) + (update result attribute (fnil conj #{}) v) (assoc result attribute v)))) + {:db/id eid} rows)))) diff --git a/modules/eacl-datahike/src/eacl/datahike/impl.clj b/modules/eacl-datahike/src/eacl/datahike/impl.clj index 7a9a9b9d..2741ed32 100644 --- a/modules/eacl-datahike/src/eacl/datahike/impl.clj +++ b/modules/eacl-datahike/src/eacl/datahike/impl.clj @@ -4,6 +4,8 @@ [eacl.datahike.db :as ddb] [eacl.datahike.schema :as schema] [eacl.engine.relationships :as relationship-engine] + [eacl.relationships.edge :as edge] + [eacl.relationships.inspection :as inspection] [eacl.relationships.endpoint-pair :as endpoint-pair] [eacl.relationships.filters :as relationship-filters] [eacl.relationships.mutations :as relationship-mutations] @@ -75,8 +77,10 @@ (relationship-datoms-on-entity db entity attr prefix cursor-id :asc)) ([db entity attr prefix cursor-id direction] + (relationship-datoms-on-entity db entity attr prefix cursor-id direction false)) + ([db entity attr prefix cursor-id direction include-qualifier?] (ddb/checked-relationship-datoms - db entity attr prefix cursor-id direction))) + db entity attr prefix cursor-id direction include-qualifier?))) (defn- apply-scan-bound "Avoid a predicate on every row when a direct native seek has already @@ -92,29 +96,29 @@ values ;; EAVT contains at most one datom for one [entity attribute value] ;; tuple, so the inclusive native seek has at most one boundary row. - (if (= bound-eid (first values)) (rest values) values)) + (if (= bound-eid (some-> (first values) edge/endpoint)) (rest values) values)) :else (filter (case direction :asc (if inclusive-bound? - #(<= bound-eid %) - #(< bound-eid %)) + #(<= bound-eid (edge/endpoint %)) + #(< bound-eid (edge/endpoint %))) :desc (if inclusive-bound? - #(>= bound-eid %) - #(> bound-eid %))) + #(>= bound-eid (edge/endpoint %)) + #(> bound-eid (edge/endpoint %)))) values))) (defn- scan-value [datom] (nth (:v datom) 3)) (defn- endpoint-scan [db endpoint-id attribute prefix cursor-or-options] - (let [{:keys [direction bound-eid inclusive-bound?]} + (let [{:keys [direction bound-eid inclusive-bound? include-qualifier?]} (relationship-storage/normalize-scan-options cursor-or-options) datoms (relationship-datoms-on-entity - db endpoint-id attribute prefix bound-eid direction)] + db endpoint-id attribute prefix bound-eid direction include-qualifier?)] (apply-scan-bound - db (map scan-value datoms) + db (map (if include-qualifier? edge/from-datom scan-value) datoms) direction bound-eid inclusive-bound?))) (defn subject->resources @@ -256,6 +260,14 @@ :resource-identity-guard (endpoint-identity-guard db :resource resource-id resource)})) +(defn relationship-publication-input + "Resolves the same endpoint identities and commit guards as ordinary writes." + [db relationship] + (let [resolved (resolve-relationship db relationship)] + {:relationship (mapv resolved [:subject-type :subject-id :relation-id :resource-type :resource-id]) + :identity-guards [(:subject-identity-guard resolved) + (:resource-identity-guard resolved)]})) + (defn- stored-halves [db resolved] [(vec (ddb/relationship-identity-datoms @@ -325,6 +337,16 @@ db subject-id relationship-storage/forward-attribute (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)))))) +(defn direct-edge + "Stored compact edge or nil, prior to request qualification." + [db subject-type subject-id relation-id resource-type resource-id] + (some-> (first (endpoint-pair/checked-datoms + (ddb/relationship-identity-datoms + db subject-id relationship-storage/forward-attribute + (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)) + true)) + edge/from-datom)) + (defn- reverse-match? [db resource-type resource-id relation-id subject-type subject-id] (boolean @@ -409,7 +431,8 @@ ;; no filter validation at all. (when-not relationship-filters/*validated-request?* (relationship-filters/validate! filters)) - (let [subject-id' (when (contains? filters :subject/id) + (let [include-qualifier? (true? (:include-qualifier? window-options)) + subject-id' (when (contains? filters :subject/id) (internal-id db (:subject/id filters))) resource-id' (when (contains? filters :resource/id) (internal-id db (:resource/id filters))) @@ -419,15 +442,17 @@ (if (or (and (contains? filters :subject/id) (nil? subject-id')) (and (contains? filters :resource/id) (nil? resource-id'))) {:data [] :cursor nil} - (letfn [(relationship-row [spec subject-id resource-id] + (letfn [(relationship-row [spec subject-id resource-id qualifier-id] {:spec-idx (:idx spec) :subject-id subject-id :resource-id resource-id :relationship - (eacl/->Relationship - (spice-object (:subject-type spec) subject-id) - (:relation-name spec) - (spice-object (:resource-type spec) resource-id))}) + (inspection/row + (eacl/->Relationship + (spice-object (:subject-type spec) subject-id) + (:relation-name spec) + (spice-object (:resource-type spec) resource-id)) + (:relation-id spec) qualifier-id)}) (normalized-cursor [cursor] (when cursor (cond-> @@ -445,18 +470,19 @@ scan-kind direction cursor %)) rows))) (exact-match-row [spec cursor direction] - (let [row (when (and (:subject-id spec) (:resource-id spec)) - (when (direct-match? - db - (:subject-type spec) - (:subject-id spec) - (:relation-id spec) - (:resource-type spec) - (:resource-id spec)) - (relationship-row spec (:subject-id spec) (:resource-id spec))))] + (let [row + (when (and (:subject-id spec) (:resource-id spec)) + (when-let [compact + (if include-qualifier? + (direct-edge db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (when (direct-match? db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (:resource-id spec)))] + (relationship-row spec (:subject-id spec) (:resource-id spec) + (edge/qualifier-id compact))))] (if row - (drop-until-beyond-cursor - spec cursor direction [row]) + (drop-until-beyond-cursor spec cursor direction [row]) []))) (scan-forward-anchored [spec cursor direction] (if (:resource-id spec) @@ -470,10 +496,10 @@ (:resource-type spec)] (or (:resource-id cursor) (:resource cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [v]}] (relationship-row - spec (:subject-id spec) (nth v 3)))) + spec (:subject-id spec) (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-reverse-anchored [spec cursor direction] @@ -488,10 +514,10 @@ (:subject-type spec)] (or (:subject-id cursor) (:subject cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [v]}] (relationship-row - spec (nth v 3) (:resource-id spec)))) + spec (nth v 3) (:resource-id spec) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-forward-partial [spec cursor direction] @@ -503,9 +529,9 @@ (:resource-type spec)] (or (:resource-id cursor) (:resource cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [e v]}] - (relationship-row spec e (nth v 3)))) + (relationship-row spec e (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))) (scan-reverse-partial [spec cursor direction] @@ -517,9 +543,9 @@ (:subject-type spec)] (or (:subject-id cursor) (:subject cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [e v]}] - (relationship-row spec (nth v 3) e))) + (relationship-row spec (nth v 3) e (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))) (scan-spec @@ -541,7 +567,7 @@ (let [scan-specs (relationship-engine/plan-scans (all-relation-defs db) filters')] - (if window-options + (if (:accept? window-options) (relationship-engine/execute-filtered-window scan-specs filters' decision-kernel scan-spec window-options) (if-not (or (contains? filters' :limit) diff --git a/modules/eacl-datahike/src/eacl/datahike/migrations/v7_to_v9.clj b/modules/eacl-datahike/src/eacl/datahike/migrations/v7_to_v9.clj index 58b74b3a..47141813 100644 --- a/modules/eacl-datahike/src/eacl/datahike/migrations/v7_to_v9.clj +++ b/modules/eacl-datahike/src/eacl/datahike/migrations/v7_to_v9.clj @@ -9,8 +9,8 @@ (defn- install! [conn] (let [database (d/db conn) - definitions (concat upgrade/metadata-schema - (filter #(contains? storage/attributes (:db/ident %)) schema/datahike-schema))] + definitions (vals (into {} (map (juxt :db/ident identity)) + (concat upgrade/metadata-schema schema/datahike-schema)))] (doseq [attr storage/attributes :when (and (db/entid database attr) (not (admission/schema-compatible? database attr)))] (upgrade/fail! :incompatible-schema {:attribute attr})) diff --git a/modules/eacl-datahike/src/eacl/datahike/qualifiers.clj b/modules/eacl-datahike/src/eacl/datahike/qualifiers.clj index 64399756..ad633ae1 100644 --- a/modules/eacl-datahike/src/eacl/datahike/qualifiers.clj +++ b/modules/eacl-datahike/src/eacl/datahike/qualifiers.clj @@ -1,39 +1,36 @@ (ns eacl.datahike.qualifiers "Prepared concrete refs; direct native writers only." - (:require [datahike.api :as d] + (:require [eacl.datahike.db :as native-db] + [datahike.api :as d] [eacl.datahike.db :as db] [eacl.datahike.backend :as backend] [eacl.datahike.schema :as schema] - [eacl.relationships.staged :as staged])) + [eacl.relationships.staged :as staged] + [eacl.schema.qualification-admission :as admission])) -(defn- facts [database eid] - (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) - (d/datoms database {:index :eavt :components [eid]}))) +(defn- facts [database eid] (native-db/entity-facts database eid)) -(defn- entity [database eid] - (let [rows (facts database eid)] - (when (seq rows) - (reduce (fn [result [a v]] - (let [attribute (if (keyword? a) a (:db/ident (d/entity database a)))] - (if (= :eacl.relation/caveats attribute) - (update result attribute (fnil conj #{}) v) (assoc result attribute v)))) - {:db/id eid} rows)))) +(defn- entity [database eid] (native-db/entity-data database eid)) (defn- assert-entity [database {:keys [eid expected]}] (when-not (= expected (facts database eid)) (staged/error! :qualifier-changed-at-commit)) []) -(defn- fence [database relation-id reference-added?] +(defn- schema-fence [database reference-added?] (let [schema-id (db/entid database [:eacl/id "schema-string"]) - expected (:eacl/schema-write-fence (entity database schema-id)) - attribute #(db/attr-repr database %)] + expected (:eacl/schema-write-fence (entity database schema-id))] (when-not expected (staged/error! :schema-unprepared)) - (cond-> [[:db.fn/cas schema-id (attribute :eacl/schema-write-fence) expected expected]] - reference-added? (conj [:db/add schema-id :eacl/schema-write-fence :db/current-tx]) - relation-id (into [[:db.fn/cas relation-id (attribute :eacl/relation-version) - (:eacl/relation-version (entity database relation-id)) - (:eacl/relation-version (entity database relation-id))] - [:db/add relation-id :eacl/relation-version :db/current-tx]])))) + (cond-> [[:db.fn/cas schema-id (db/attr-repr database :eacl/schema-write-fence) expected expected]] + reference-added? (conj [:db/add schema-id :eacl/schema-write-fence :db/current-tx])))) + +(defn- relation-fence [database relation-id] + (when relation-id + (let [expected (:eacl/relation-version (entity database relation-id))] + [[:db.fn/cas relation-id (db/attr-repr database :eacl/relation-version) expected expected] + [:db/add relation-id :eacl/relation-version :db/current-tx]]))) + +(defn- fence [database relation-id reference-added?] + (into (schema-fence database reference-added?) (relation-fence database relation-id))) (defn read-api "Read-only native inputs; constructing this map never prepares or writes a store." @@ -51,14 +48,34 @@ :qualifier-cache-scope :assertion-version :qualifier-version (fn [database eid] (some-> (d/datoms database {:index :eavt :components [eid :eacl.relationship-qualifier/format-version]}) first :tx))}) +(defn planner-api + "Pure native reads and transaction-data construction, safe for snapshots." + [] + (let [tempids (atom -1000000000)] + (merge (read-api) + {:strategy :prepared :fence fence + :schema-fence schema-fence :relation-fence relation-fence + ;; Keep the eid inside a map: Datahike treats call slot three as an attribute. + :assert-entity (fn [eid expected] [:db.fn/call assert-entity {:eid eid :expected expected}]) + :tempid #(swap! tempids dec)}))) + +(defn plan + "Builds qualified transaction data from one immutable basis without writing." + ([database entries app-datoms] + (plan database entries app-datoms (backend/database-source-scope database))) + ([database entries app-datoms source-scope] + (let [native (assoc (planner-api) :source #(or (backend/database-source-scope %) source-scope))] + (staged/plan-batch (staged/planner native database) database entries app-datoms)))) + (defn writer [conn] (when-not (db/direct-writer? (d/db conn)) (staged/error! :unsupported-backend)) (schema/prepare-cache-coherence! conn) - (let [tempids (atom -1000000000)] - (staged/native-writer - (merge (read-api) - {:strategy :prepared :snapshot #(d/db conn) :fence fence - ;; Datahike examines tuple attributes before dispatching :db.fn/call; - ;; an eid in argument slot three is mistaken for an attribute ref. - :assert-entity (fn [eid expected] [:db.fn/call assert-entity {:eid eid :expected expected}]) - :tempid #(swap! tempids dec) :transact! #(d/transact conn %)})))) + (staged/native-writer + (merge (planner-api) + (let [scope (backend/connection-source-scope conn)] + {:source #(or (backend/database-source-scope %) scope)}) + {:snapshot #(d/db conn) :transact! #(d/transact conn %)}))) + +(defn publication-capability [database] + (when (db/direct-writer? database) + (admission/publication-descriptor :prepared))) diff --git a/modules/eacl-datahike/src/eacl/datahike/schema.clj b/modules/eacl-datahike/src/eacl/datahike/schema.clj index 1cff8246..129dc283 100644 --- a/modules/eacl-datahike/src/eacl/datahike/schema.clj +++ b/modules/eacl-datahike/src/eacl/datahike/schema.clj @@ -11,6 +11,7 @@ [eacl.schema.expression-policy :as expression-policy] [eacl.schema.expression-resolver :as expression-resolver] [eacl.schema.model :as model] + [eacl.schema.relation-allowance :as relation-allowance] [eacl.schema.replacement-plan :as replacement-plan])) (def relation-key-attr @@ -224,12 +225,14 @@ rows))) (defn read-relations - "Every relation definition. Enumerated from the relation key index rather than - by query: the index is the engine's own view of the schema, so a relation - that is invisible here is invisible to permission evaluation too." + "Canonical Relation definitions, including named Caveat alternatives." [db] - (mapv #(d/pull db relation-pull (:e %)) - (ddb/avet-datoms db relation-key-attr))) + (let [pattern (cond-> relation-pull + (ddb/entid db :eacl.relation/caveats) + (into [:eacl.relation/allows-unqualified? + {:eacl.relation/caveats [:eacl.caveat/name]}]))] + (mapv #(relation-allowance/canonicalize (d/pull db pattern (:e %))) + (ddb/avet-datoms db relation-key-attr)))) (defn read-permissions [db] @@ -296,13 +299,18 @@ :where [?q :eacl.relationship-qualifier/caveat ?c]] db eid) [])) -(defn read-schema - [db & [_format]] +(defn read-authorization-schema + "Reads permission structure without compiling undemanded Caveat programs." + [db] (let [permissions (read-permissions db)] (expression-persistence/validate-entities permissions) - (let [caveats (read-caveats db)] - (cond-> {:relations (read-relations db) :permissions permissions} - (seq caveats) (assoc :caveats caveats))))) + {:relations (read-relations db) :permissions permissions})) + +(defn read-schema + [db & [_format]] + (let [schema (read-authorization-schema db) + caveats (read-caveats db)] + (cond-> schema (seq caveats) (assoc :caveats caveats)))) (defn prepare-cache-coherence! "Initializes missing native schema/relation generations and the schema @@ -471,16 +479,22 @@ throwable)) (throw throwable))))) +(defn- stored-relation-caveats [db relation] + (relation-allowance/stored-caveats + {:entid #(ddb/entid db %) :entity #(ddb/entity-data db %) + :rows #(ddb/relationship-identity-datoms db %1 %2 %3) + :scan #(ddb/avet-tuple-prefix db %1 relationship-storage/value-arity %2)} relation)) + (defn plan-schema-replacement "Pure schema replacement planner shared by committed and speculative paths." [db schema-string - {:keys [allow-empty-schema? expression-limits orphan-policy] + {:keys [allow-empty-schema? expression-limits orphan-policy allow-caveats?] :or {orphan-policy :error}}] (let [expression-limits (expression-policy/normalize-client-limits expression-limits) new-schema-map (expression-persistence/candidate-schema - (expression-resolver/validate-schema schema-string expression-limits)) + (expression-resolver/validate-schema schema-string expression-limits {:allow-caveats? allow-caveats?})) existing-schema (binding [expression-persistence/*expression-limits* expression-limits] (read-schema db)) @@ -499,6 +513,7 @@ :existing {:relations (count (:relations existing-schema)) :permissions (count (:permissions existing-schema))}}))) deltas (compare-schema existing-schema new-schema-map) + _ (relation-allowance/validate-existing! (:relations deltas) #(stored-relation-caveats db %)) semantic (replacement-plan/plan {:deltas deltas @@ -508,7 +523,7 @@ #(relationship-present-for-relation? db %)}) {:keys [relations permissions caveats]} deltas _ (caveat-definition/validate-replacements! caveats #(caveat-references db %)) - relation-retractions (:retractions relations) + relation-retractions (relation-allowance/entity-deletions relations) permission-retractions (expression-persistence/entity-deletions permissions) relation-additions @@ -535,13 +550,14 @@ [:db.fn/cas relation-eid (ddb/attr-repr db :eacl/relation-version) relation-generation relation-generation])) - relation-retractions) + (:retractions relations)) tx-data (vec (concat + (:additions caveats) + (relation-allowance/attribute-retractions relations) relation-additions (:additions permissions) - (:additions caveats) (for [caveat (caveat-definition/entity-deletions caveats) :let [eid (ddb/entid db [:eacl.caveat/name (:eacl.caveat/name caveat)])] :when eid] diff --git a/modules/eacl-datahike/test/eacl/datahike/cache_model_test.clj b/modules/eacl-datahike/test/eacl/datahike/cache_model_test.clj index ceb00f45..739719c7 100644 --- a/modules/eacl-datahike/test/eacl/datahike/cache_model_test.clj +++ b/modules/eacl-datahike/test/eacl/datahike/cache_model_test.clj @@ -42,7 +42,10 @@ (defn- without-cache-provenance [value] (if (map? value) - (dissoc value :cached? :cache-basis) + ;; Separate live requests may mint different time-scoped cursor bytes. + ;; Compare authorization and page bounds; cursor validity has its own suite. + (cond-> (dissoc value :cached? :cache-basis) + (:page-info value) (update :page-info dissoc :start-cursor :end-cursor)) value)) (defn- assert-same-answers! diff --git a/modules/eacl-datahike/test/eacl/datahike/caveat_schema_test.clj b/modules/eacl-datahike/test/eacl/datahike/caveat_schema_test.clj index f35f7248..a90832fc 100644 --- a/modules/eacl-datahike/test/eacl/datahike/caveat_schema_test.clj +++ b/modules/eacl-datahike/test/eacl/datahike/caveat_schema_test.clj @@ -1,5 +1,6 @@ (ns eacl.datahike.caveat-schema-test (:require [eacl.datahike.core :as api] + [eacl.datahike.backend :as backend] [eacl.cache :as cache] [eacl.caveats.hot-path-contract :as hot-path] [clojure.test :refer [deftest]] @@ -8,6 +9,8 @@ [eacl.datahike.schema :as schema] [eacl.caveats.persistence-contract :as contract] [eacl.caveats.publication-contract :as publication] + [eacl.relationships.edge-contract :as edge-contract] + [eacl.datahike.impl :as scan-impl] [eacl.datahike.qualifiers :as qualifiers])) (defn interleave! [competitor outer] @@ -36,3 +39,14 @@ {:write-schema! #(schema/write-schema! conn %) :writer #(qualifiers/writer conn) :entid db/entid :strategy :prepared :interleave! interleave!}) (finally (d/release conn) (d/delete-database config)))))) + +(deftest compact-qualified-scans + (doseq [options [{} {:attribute-refs? true}]] + (let [conn (schema/create-conn [] options) config (:config (d/db conn))] + (try + (edge-contract/check! + {:write-schema! #(schema/write-schema! conn %) :writer #(qualifiers/writer conn) + :entid db/entid :forward scan-impl/subject->resources + :reverse scan-impl/resource->subjects :direct scan-impl/direct-edge + :adapter #(backend/basis-adapter % {})}) + (finally (d/release conn) (d/delete-database config)))))) diff --git a/modules/eacl-datahike/test/eacl/datahike/qualified_write_test.clj b/modules/eacl-datahike/test/eacl/datahike/qualified_write_test.clj new file mode 100644 index 00000000..4d7b7755 --- /dev/null +++ b/modules/eacl-datahike/test/eacl/datahike/qualified_write_test.clj @@ -0,0 +1,114 @@ +(ns eacl.datahike.qualified-write-test + (:require [clojure.test :refer [deftest is]] + [datahike.api :as d] + [eacl.caveats.publication-batch-contract :as batch] + [eacl.caveats.public-write-contract :as public] + [eacl.caveats.write-contention-contract :as contention] + [eacl.caveats.schema-allowance-contract :as allowance] + [eacl.caveats.inspection-contract :as inspection] + [eacl.caveats.deletion-contract :as deletion] + [eacl.caveats.cache-trace-contract :as cache-trace] + [eacl.authorization.qualification-test :as fixtures] + [eacl.client.orchestration :as orchestration] + [eacl.caveats.definition-test :as errors] + [eacl.caveats.schema-admission-test :as schemas] + [eacl.core :as eacl] + [eacl.datahike.core :as api] + [eacl.datahike.caveat-schema-test :as schema-races] + [eacl.datahike.db :as db] + [eacl.datahike.schema :as schema] + [eacl.datahike.storage :as admission] + [eacl.datahike.qualifiers :as qualifiers])) + +(deftest qualified-batches-publish-atomically + (doseq [options [{} {:attribute-refs? true}]] + (let [conn (schema/create-conn [{:db/ident :app/flag :db/valueType :db.type/long :db/cardinality :db.cardinality/one}] options) + config (:config (d/db conn))] + (try + (batch/check! {:write-schema! #(schema/write-schema! conn %) + :cas-attribute (when (:attribute-refs? options) db/entid) + :writer #(qualifiers/writer conn) :entid db/entid :strategy :prepared}) + (finally (d/release conn) (d/delete-database config)))))) + +(deftest public-qualified-writes-preserve-identity-and-commit-atomically + (doseq [options [{} {:attribute-refs? true}]] + (let [conn (schema/create-conn [{:db/ident :app/flag :db/valueType :db.type/long :db/cardinality :db.cardinality/one}] options) + config (:config (d/db conn)) + now (atom 99)] + (try + (public/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) :entid db/entid :now now + :cas-attribute (when (:attribute-refs? options) db/entid)}) + (finally (d/release conn) (d/delete-database config)))))) + +(deftest schema-alternatives-preserve-relation-identities-and-retained-data + (doseq [options [{} {:attribute-refs? true}]] + (let [conn (schema/create-conn [] options) config (:config (d/db conn))] + (try + (allowance/check! {:client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) + :read-schema schema/read-schema :interleave! schema-races/interleave! :entid db/entid}) + (finally (d/release conn) (d/delete-database config)))))) + +(deftest externally-created-connection-supports-qualified-preparation-and-snapshot-publication + (let [config (update schema/default-config :store assoc :id (random-uuid)) + _ (d/create-database config) + conn (d/connect config) + now (atom 99)] + (try + (d/transact conn (schema/merge-schema [{:db/ident :app/flag :db/valueType :db.type/long + :db/cardinality :db.cardinality/one}])) + (admission/bootstrap! conn) + (is (nil? (get (:config (d/db conn)) schema/live-source-id-key))) + (public/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) :entid db/entid :now now}) + (finally (d/release conn) (d/delete-database config))))) + +(deftest remote-writer-cannot-admit-a-qualified-schema + (let [conn (schema/create-conn) config (:config (d/db conn))] + (try + (let [client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [db/direct-writer? (constantly false)] + (is (nil? (qualifiers/publication-capability (d/db conn)))) + (is (= :eacl/unsupported-capability + (errors/error-type #(eacl/write-schema! client {:schema (schemas/source "user with enabled")}))))))) + (finally (d/release conn) (d/delete-database config))))) + +(deftest stored-and-active-inspection-preserve-aligned-native-qualifiers + (doseq [options [{} {:attribute-refs? true}]] + (let [conn (schema/create-conn [{:db/ident :app/flag :db/valueType :db.type/long :db/cardinality :db.cardinality/one}] options) + config (:config (d/db conn)) + now (atom 99)] + (try + (inspection/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) :entid db/entid :now now + :cas-attribute (when (:attribute-refs? options) db/entid)}) + (finally (d/release conn) (d/delete-database config)))))) + +(deftest qualified-cache-traces-match-uncached-authorization + (doseq [options [{} {:attribute-refs? true}]] + (let [conn (schema/create-conn [] options) config (:config (d/db conn)) now (atom 99)] + (try + (cache-trace/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) :now now :expire-cache! api/expire-cache!}) + (finally (d/release conn) (d/delete-database config)))))) + +(deftest qualified-object-deletion-is-atomic-and-bounded + (doseq [options [{} {:attribute-refs? true}]] + (let [conn (schema/create-conn [] options) config (:config (d/db conn))] + (try + (deletion/check! {:client (api/make-client conn {:clock (constantly 200) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn)}) + (finally (d/release conn) (d/delete-database config)))))) + +(deftest qualified-native-cas-contention-replans-from-a-new-basis + (let [conn (schema/create-conn) + client (api/make-client conn {})] + (contention/check! client #(qualifiers/writer conn)) + (contention/terminal-validation-check! client))) diff --git a/modules/eacl-datalevin/README.md b/modules/eacl-datalevin/README.md index 12d852b9..46bc605d 100644 --- a/modules/eacl-datalevin/README.md +++ b/modules/eacl-datalevin/README.md @@ -35,7 +35,7 @@ For a source checkout with the expected sibling layout, use: ``` The reserved release coordinate is -`dev.eacl/eacl-datalevin:8.0.0-SNAPSHOT`, depending on +`dev.eacl/eacl-datalevin:9.0.0-SNAPSHOT`, depending on `dev.eacl/datalevin-embedded-eacl:1.0.2-eacl.2`. Neither is a usable published dependency until the release and clean remote-consumer gates pass. @@ -157,10 +157,12 @@ mutation, or opening the directory with upstream Datalevin are outside it. ## Relationship storage 9 -This EACL v8 adapter uses five-slot endpoint pairs with a trailing nullable -`qualifier-eid`. This phase writes `nil` and raises `:eacl/unsupported-qualifier` -when serving encounters a qualifier. Upgrades are explicit and restartable; -ordinary client construction requires a completed target store. Follow the -[7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before starting clients. +This adapter uses five-slot endpoint pairs with a trailing nullable +`qualifier-eid`. V8 writes only `nil` and rejects non-nil qualifiers. V9 adds +[Caveats and expiring Relationships](../../docs/caveats.md) through coordinated +qualified activation; older readers must be drained first. Upgrades are explicit +and restartable, and client construction requires a completed target store. +Follow the [7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before +starting clients, then the v9 serving rollout guide before qualified writes. The adapter's `create-conn` helper explicitly bootstraps fresh stores. diff --git a/modules/eacl-datalevin/src/eacl/datalevin/backend.cljc b/modules/eacl-datalevin/src/eacl/datalevin/backend.cljc index 2b101baf..5501a32a 100644 --- a/modules/eacl-datalevin/src/eacl/datalevin/backend.cljc +++ b/modules/eacl-datalevin/src/eacl/datalevin/backend.cljc @@ -2,6 +2,7 @@ "Owned explicit-snapshot storage operations for the EACL v8 engine." (:require [clojure.set :as set] [datalevin.core :as d] + [eacl.authorization.data :as qualification-data] [eacl.backend.source :as source] [eacl.backend.v8 :as backend] [eacl.datalevin.db :as ddb] @@ -10,7 +11,9 @@ [eacl.schema.expression-persistence :as expression-persistence])) (def adapter-capabilities - {:cursor #{:forward :reverse :opaque :authenticated :encrypted} + {:qualification #{qualification-data/capability} + :qualified-publication #{:atomic-inline-v1} + :cursor #{:forward :reverse :opaque :authenticated :encrypted} :cache-proofs #{:ordered-generations :snapshot-bound :database-visible} :runtime #{:clj}}) @@ -275,6 +278,22 @@ % subject-type subject-id relation-id resource-type resource-id))) + :direct-edge + (fn [subject-type subject-id relation-id resource-type resource-id] + (exact-natural! :subject-id subject-id) + (exact-natural! :relation-id relation-id) + (exact-natural! :resource-id resource-id) + (ddb/with-db snapshot + #(impl/direct-edge % subject-type subject-id relation-id resource-type resource-id))) + + :qualification-data + (fn [eid] + (exact-natural! :qualification-entity-id eid) + (ddb/with-db snapshot + #(qualification-data/collect eid + (d/datoms % :eav eid nil nil (inc qualification-data/maximum-entity-facts)) + identity false))) + :all-permission-nodes (fn [] (ddb/with-db diff --git a/modules/eacl-datalevin/src/eacl/datalevin/core.cljc b/modules/eacl-datalevin/src/eacl/datalevin/core.cljc index 0a79cd6d..1b994a2e 100644 --- a/modules/eacl-datalevin/src/eacl/datalevin/core.cljc +++ b/modules/eacl-datalevin/src/eacl/datalevin/core.cljc @@ -12,6 +12,7 @@ [eacl.datalevin.db :as ddb] [eacl.datalevin.fork :as fork] [eacl.datalevin.impl :as impl] + [eacl.datalevin.qualifiers :as qualifiers] [eacl.datalevin.schema :as schema] [eacl.datalevin.storage :as target-storage] [eacl.relationships.storage :as relationship-storage])) @@ -63,8 +64,10 @@ (defn- stale-connection-contention? [throwable] - (= :eacl.datalevin/stale-connection-generation - (:type (ex-data throwable)))) + (or (= :eacl.datalevin/stale-connection-generation + (:type (ex-data throwable))) + (= :cleanup-source-changed + (:reason (datalevin-failure-data throwable :eacl.qualifier/staged-write))))) (defn- transact-native! [write-token conn {:keys [tx-data]}] @@ -173,6 +176,9 @@ [snapshot-or-db object-eid] (ddb/with-db snapshot-or-db #(impl/tx-delete-object % object-eid))) +(defn- snapshot-object-relationship-retractions [snapshot-or-db object-eid] + (ddb/with-db snapshot-or-db #(impl/selected-object-relationship-retractions % object-eid))) + (defn- snapshot-read-relationships ([snapshot-or-db query kernel] (snapshot-read-relationships snapshot-or-db query kernel nil)) @@ -183,6 +189,9 @@ (def ^:private base-api {:backend-id :datalevin + :qualified-writer #'qualifiers/writer + :qualified-publication-capability #'qualifiers/publication-capability + :qualified-plan #'qualifiers/plan :writer-max-attempts 8 :writer-contention? stale-connection-contention? :db ds/db @@ -208,14 +217,17 @@ ;; the impl suites) and REPL redefinition visible through the shared ;; orchestration. :schema {:read-schema #'schema/read-schema + :read-authorization-schema #'schema/read-authorization-schema :generation snapshot-schema-generation :write-schema! nil} :impl {:validate-relationship-operation! #'impl/validate-relationship-operation! + :relationship-publication-input (fn [db relationship] (ddb/with-db db #(impl/relationship-publication-input % relationship))) :relationship-relation-id snapshot-relationship-relation-id :relation-coordinate snapshot-relation-coordinate :tx-update-relationship snapshot-tx-update-relationship :tx-delete-object snapshot-tx-delete-object + :object-relationship-retractions snapshot-object-relationship-retractions :affected-relation-ids #'impl/affected-relation-ids :read-relationships snapshot-read-relationships} :extra-client-opt-keys diff --git a/modules/eacl-datalevin/src/eacl/datalevin/db.cljc b/modules/eacl-datalevin/src/eacl/datalevin/db.cljc index 00980f61..3d95a28d 100644 --- a/modules/eacl-datalevin/src/eacl/datalevin/db.cljc +++ b/modules/eacl-datalevin/src/eacl/datalevin/db.cljc @@ -99,7 +99,7 @@ the list crossed the threshold and the caller must use the bounded native seek. The threshold+1 sample makes that choice without ever mistaking a truncated sample for a complete adjacency list." - [db entity attr prefix cursor-eid direction native-limit] + [db entity attr prefix cursor-eid direction native-limit include-qualifier?] (let [sample (into [] (take (inc small-endpoint-scan-threshold)) @@ -114,7 +114,7 @@ ordered (if (= :desc direction) (rseq matching) matching)] - (into [] (take native-limit) (endpoint-pair/checked-datoms ordered)))))) + (into [] (take native-limit) (endpoint-pair/checked-datoms ordered include-qualifier?)))))) (defn eavt-endpoint-prefix "Endpoint datoms for an exact three-component value prefix. @@ -128,6 +128,8 @@ (eavt-endpoint-prefix db entity attr prefix cursor-eid direction maximum-unpaged-scan-results)) ([db entity attr prefix cursor-eid direction native-limit] + (eavt-endpoint-prefix db entity attr prefix cursor-eid direction native-limit false)) + ([db entity attr prefix cursor-eid direction native-limit include-qualifier?] (if-not (and (nat-int? entity) (endpoint-pair/valid-prefix? prefix) (#{:asc :desc} direction) @@ -135,7 +137,7 @@ [] (or (small-endpoint-prefix - db entity attr prefix cursor-eid direction native-limit) + db entity attr prefix cursor-eid direction native-limit include-qualifier?) (let [tail (or cursor-eid (if (= :desc direction) max-eid min-eid)) bound (endpoint-pair/seek-bound prefix tail direction max-eid) @@ -148,7 +150,8 @@ (fn [{:keys [e a] :as datom}] (and (= entity e) (= attr a) (endpoint-pair/value-prefix? (:v datom) prefix))) - scan)))))))) + scan) + include-qualifier?))))))) (defn avet-endpoint-prefix "Endpoint datoms across entities for an exact three-component value prefix, @@ -162,6 +165,8 @@ ([db attr prefix cursor-eid direction native-limit] (avet-endpoint-prefix db attr prefix cursor-eid nil direction native-limit)) ([db attr prefix cursor-eid cursor-entity direction native-limit] + (avet-endpoint-prefix db attr prefix cursor-eid cursor-entity direction native-limit false)) + ([db attr prefix cursor-eid cursor-entity direction native-limit include-qualifier?] (if-not (and (endpoint-pair/valid-prefix? prefix) (#{:asc :desc} direction) (or (nil? cursor-entity) (nat-int? cursor-entity)) @@ -180,4 +185,34 @@ (take-while (fn [{:keys [a] :as datom}] (and (= attr a) (endpoint-pair/value-prefix? (:v datom) prefix))) - scan))))))) + scan) include-qualifier?)))))) + +(defn qualified-relation-datoms + "Complete qualified Relation stream in bounded native batches. Callers must + consume the stream inside the selected snapshot's ownership scope." + [db attr prefix] + (letfn [(step [boundary] + (lazy-seq + (let [rows (ds/seek-datoms db :ave attr + (if boundary (:v boundary) (into prefix [0 nil])) + (:e boundary) 1025) + rows (if (and boundary (= [(:e boundary) (:v boundary)] + [(:e (first rows)) (:v (first rows))])) + (rest rows) rows) + chunk (vec (take-while #(and (= attr (:a %)) + (endpoint-pair/value-prefix? (:v %) prefix)) rows))] + (when (seq chunk) + (concat (endpoint-pair/checked-datoms chunk true) + (when (= (count chunk) (count rows)) + (step (peek chunk))))))))] + (step nil))) + +(defn entity-facts [database eid] + (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) (ds/datoms database :eav eid))) + +(defn entity-data [database eid] + (let [rows (entity-facts database eid)] + (when (seq rows) + (reduce (fn [result [a v]] + (if (= :eacl.relation/caveats a) (update result a (fnil conj #{}) v) (assoc result a v))) + {:db/id eid} rows)))) diff --git a/modules/eacl-datalevin/src/eacl/datalevin/impl.cljc b/modules/eacl-datalevin/src/eacl/datalevin/impl.cljc index a157f8a9..6bc3ef79 100644 --- a/modules/eacl-datalevin/src/eacl/datalevin/impl.cljc +++ b/modules/eacl-datalevin/src/eacl/datalevin/impl.cljc @@ -3,6 +3,8 @@ [eacl.core :as eacl :refer [spice-object]] [eacl.datalevin.db :as ddb] [eacl.engine.relationships :as relationship-engine] + [eacl.relationships.edge :as edge] + [eacl.relationships.inspection :as inspection] [eacl.relationships.endpoint-pair :as endpoint-pair] [eacl.relationships.filters :as relationship-filters] [eacl.relationships.mutations :as relationship-mutations] @@ -119,12 +121,12 @@ inclusive bound and yields one EAV datom per distinct value, so the only remaining work is dropping an exclusive boundary row and bounding the page; nothing is re-filtered or de-duplicated per value." - [datoms bound-eid inclusive-bound? limit] - (let [values (into [] (map scan-value) datoms) + [datoms bound-eid inclusive-bound? limit include-qualifier?] + (let [values (into [] (map (if include-qualifier? edge/from-datom scan-value)) datoms) values (if (and (some? bound-eid) (not inclusive-bound?) (pos? (count values)) - (= bound-eid (nth values 0))) + (= bound-eid (edge/endpoint (nth values 0)))) (subvec values 1) values)] (if limit @@ -141,13 +143,13 @@ (defn- endpoint-scan [db endpoint-id attribute prefix cursor-or-options] - (let [{:keys [direction bound-eid inclusive-bound? limit]} + (let [{:keys [direction bound-eid inclusive-bound? limit include-qualifier?]} (relationship-storage/normalize-scan-options cursor-or-options) native-limit (inc (or limit ddb/maximum-unpaged-scan-results))] (eager-scan-values (ddb/eavt-endpoint-prefix - db endpoint-id attribute prefix bound-eid direction native-limit) - bound-eid inclusive-bound? limit))) + db endpoint-id attribute prefix bound-eid direction native-limit include-qualifier?) + bound-eid inclusive-bound? limit include-qualifier?))) (defn subject->resources [db subject-type subject-id relation-id resource-type cursor-or-options] @@ -287,6 +289,14 @@ :resource-identity-guard (endpoint-identity-guard db :resource resource-id resource)})) +(defn relationship-publication-input + "Resolves the same endpoint identities and commit guards as ordinary writes." + [db relationship] + (let [resolved (resolve-relationship db relationship)] + {:relationship (mapv resolved [:subject-type :subject-id :relation-id :resource-type :resource-id]) + :identity-guards [(:subject-identity-guard resolved) + (:resource-identity-guard resolved)]})) + (defn relationship-relation-id [db relationship] (:relation-id (resolve-relationship db relationship))) @@ -345,6 +355,16 @@ (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)))))) +(defn direct-edge + "Stored compact edge or nil, prior to request qualification." + [db subject-type subject-id relation-id resource-type resource-id] + (some-> (first (endpoint-pair/checked-datoms + (ddb/relationship-identity-datoms + db subject-id relationship-storage/forward-attribute + (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)) + true)) + edge/from-datom)) + (defn- reverse-match? [db resource-type resource-id relation-id subject-type subject-id] (boolean @@ -420,7 +440,8 @@ ;; (backend-unification 9.1). (when-not relationship-filters/*validated-request?* (relationship-filters/validate! filters)) - (let [subject-id' (when (contains? filters :subject/id) + (let [include-qualifier? (true? (:include-qualifier? window-options)) + subject-id' (when (contains? filters :subject/id) (internal-id db (:subject/id filters))) resource-id' (when (contains? filters :resource/id) (internal-id db (:resource/id filters))) @@ -430,15 +451,17 @@ (if (or (and (contains? filters :subject/id) (nil? subject-id')) (and (contains? filters :resource/id) (nil? resource-id'))) {:data [] :cursor nil} - (letfn [(relationship-row [spec subject-id resource-id] + (letfn [(relationship-row [spec subject-id resource-id qualifier-id] {:spec-idx (:idx spec) :subject-id subject-id :resource-id resource-id :relationship - (eacl/->Relationship - (spice-object (:subject-type spec) subject-id) - (:relation-name spec) - (spice-object (:resource-type spec) resource-id))}) + (inspection/row + (eacl/->Relationship + (spice-object (:subject-type spec) subject-id) + (:relation-name spec) + (spice-object (:resource-type spec) resource-id)) + (:relation-id spec) qualifier-id)}) (normalized-cursor [cursor] (when cursor (cond-> @@ -467,19 +490,17 @@ (exact-match-row [spec cursor direction] (let [row (when (and (:subject-id spec) (:resource-id spec)) - (when - (direct-match? - db - (:subject-type spec) - (:subject-id spec) - (:relation-id spec) - (:resource-type spec) - (:resource-id spec)) - (relationship-row - spec (:subject-id spec) (:resource-id spec))))] + (when-let [compact + (if include-qualifier? + (direct-edge db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (when (direct-match? db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (:resource-id spec)))] + (relationship-row spec (:subject-id spec) (:resource-id spec) + (edge/qualifier-id compact))))] (if row - (drop-until-beyond-cursor - spec cursor direction [row]) + (drop-until-beyond-cursor spec cursor direction [row]) []))) (scan-forward-anchored [spec cursor direction] (if (:resource-id spec) @@ -495,15 +516,14 @@ (:resource cursor)) direction] rows - (if-let [limit (native-page-limit spec cursor)] - (apply ddb/eavt-endpoint-prefix - (conj args limit)) - (apply ddb/eavt-endpoint-prefix args))] + (apply ddb/eavt-endpoint-prefix + (cond-> (conj args (or (native-page-limit spec cursor) + ddb/maximum-unpaged-scan-results)) include-qualifier? (conj true)))] (->> rows (map (fn [{:keys [v]}] (relationship-row - spec (:subject-id spec) (nth v 3)))) + spec (:subject-id spec) (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))))) (scan-reverse-anchored [spec cursor direction] @@ -520,15 +540,14 @@ (:subject cursor)) direction] rows - (if-let [limit (native-page-limit spec cursor)] - (apply ddb/eavt-endpoint-prefix - (conj args limit)) - (apply ddb/eavt-endpoint-prefix args))] + (apply ddb/eavt-endpoint-prefix + (cond-> (conj args (or (native-page-limit spec cursor) + ddb/maximum-unpaged-scan-results)) include-qualifier? (conj true)))] (->> rows (map (fn [{:keys [v]}] (relationship-row - spec (nth v 3) (:resource-id spec)))) + spec (nth v 3) (:resource-id spec) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))))) (scan-forward-partial [spec cursor direction] @@ -544,21 +563,13 @@ (:subject cursor)) direction] rows - (if-let [limit (native-page-limit spec cursor)] - (apply ddb/avet-endpoint-prefix (conj args limit)) - (ddb/avet-endpoint-prefix - db - relationship-storage/forward-attribute - [(:subject-type spec) - (:relation-id spec) - (:resource-type spec)] - (or (:resource-id cursor) - (:resource cursor)) - direction))] + (apply ddb/avet-endpoint-prefix + (cond-> (conj args (or (native-page-limit spec cursor) + ddb/maximum-unpaged-scan-results)) include-qualifier? (conj true)))] (->> rows (map (fn [{:keys [e v]}] - (relationship-row spec e (nth v 3)))) + (relationship-row spec e (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-reverse-partial [spec cursor direction] @@ -574,21 +585,13 @@ (:resource cursor)) direction] rows - (if-let [limit (native-page-limit spec cursor)] - (apply ddb/avet-endpoint-prefix (conj args limit)) - (ddb/avet-endpoint-prefix - db - relationship-storage/reverse-attribute - [(:resource-type spec) - (:relation-id spec) - (:subject-type spec)] - (or (:subject-id cursor) - (:subject cursor)) - direction))] + (apply ddb/avet-endpoint-prefix + (cond-> (conj args (or (native-page-limit spec cursor) + ddb/maximum-unpaged-scan-results)) include-qualifier? (conj true)))] (->> rows (map (fn [{:keys [e v]}] - (relationship-row spec (nth v 3) e))) + (relationship-row spec (nth v 3) e (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-spec @@ -610,7 +613,7 @@ (let [scan-specs (relationship-engine/plan-scans (matching-relation-defs db filters') filters')] - (if window-options + (if (:accept? window-options) (relationship-engine/execute-filtered-window scan-specs filters' decision-kernel scan-spec window-options) (if-not (or (contains? filters' :limit) @@ -661,6 +664,13 @@ [db object-eid] (object-relationship-retractions db object-eid)) +(defn selected-object-relationship-retractions + "Materializes exact halves on an owned selected snapshot. Qualified deletion + fits whole pairs and qualifier cleanup into bounded transactions, each with + a native basis assertion; no lazy native rows escape the selected read." + [db object-id] + (object-relationship-retractions db (internal-id db object-id))) + (defn tx-delete-object "Returns a commit-time transaction function removing both physical halves of every relationship touching `object-id`. The object entity itself is diff --git a/modules/eacl-datalevin/src/eacl/datalevin/qualifiers.cljc b/modules/eacl-datalevin/src/eacl/datalevin/qualifiers.cljc index 66ead2d3..95879b9a 100644 --- a/modules/eacl-datalevin/src/eacl/datalevin/qualifiers.cljc +++ b/modules/eacl-datalevin/src/eacl/datalevin/qualifiers.cljc @@ -1,34 +1,35 @@ (ns eacl.datalevin.qualifiers "Certified inline native refs under the embedded store's write policy." - (:require [datalevin.core :as d] + (:require [eacl.datalevin.db :as native-db] + [datalevin.core :as d] [eacl.datalevin.db :as db] [eacl.datalevin.schema :as schema] - [eacl.relationships.staged :as staged])) + [eacl.relationships.staged :as staged] + [eacl.schema.qualification-admission :as admission])) -(defn- facts [database eid] - (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) (d/datoms database :eav eid))) +(defn- facts [database eid] (native-db/entity-facts database eid)) -(defn- entity [database eid] - (let [rows (facts database eid)] - (when (seq rows) - (reduce (fn [result [a v]] - (if (= :eacl.relation/caveats a) (update result a (fnil conj #{}) v) (assoc result a v))) - {:db/id eid} rows)))) +(defn- entity [database eid] (native-db/entity-data database eid)) (defn- assert-entity [database eid expected] (when-not (= expected (facts database eid)) (staged/error! :qualifier-changed-at-commit)) []) -(defn- fence [database relation-id reference-added?] +(defn- schema-fence [database reference-added?] (let [schema-id (d/entid database [:eacl/id "schema-string"]) expected (:eacl.datalevin/schema-write-fence (entity database schema-id))] (when-not expected (staged/error! :schema-unprepared)) (cond-> [[:db.fn/cas schema-id :eacl.datalevin/schema-write-fence expected expected]] - reference-added? (conj [:db/add schema-id :eacl.datalevin/schema-write-fence :db/current-tx]) - relation-id (into [[:db.fn/cas relation-id :eacl.datalevin/relation-generation - (:eacl.datalevin/relation-generation (entity database relation-id)) - (:eacl.datalevin/relation-generation (entity database relation-id))] - [:db/add relation-id :eacl.datalevin/relation-generation :db/current-tx]])))) + reference-added? (conj [:db/add schema-id :eacl.datalevin/schema-write-fence :db/current-tx])))) + +(defn- relation-fence [database relation-id] + (when relation-id + (let [expected (:eacl.datalevin/relation-generation (entity database relation-id))] + [[:db.fn/cas relation-id :eacl.datalevin/relation-generation expected expected] + [:db/add relation-id :eacl.datalevin/relation-generation :db/current-tx]]))) + +(defn- fence [database relation-id reference-added?] + (into (schema-fence database reference-added?) (relation-fence database relation-id))) (defn read-api "Read-only native inputs; constructing this map never prepares or writes a store." @@ -46,17 +47,35 @@ :qualifier-cache-scope :exact-only :qualifier-version (fn [_database _eid] nil)}) +(defn planner-api + "Pure native reads and transaction-data construction, safe for snapshots." + [] + (merge (read-api) + {:strategy :inline :fence fence + :schema-fence schema-fence :relation-fence relation-fence + :assert-entity (fn [eid expected] [:db.fn/call assert-entity eid expected]) + :tempid #(d/tempid :db.part/user)})) + +(defn plan + "Builds qualified transaction data from one immutable basis without writing." + ([database entries app-datoms] + (db/with-db database + (fn [db] + (staged/plan-batch (staged/planner (planner-api) db) db entries app-datoms)))) + ([database entries app-datoms _source-scope] + (plan database entries app-datoms))) + (defn writer [conn] - (let [token (:write-token (schema/ensure-physical-schema! conn)) - tempids (atom -1000000000)] + (let [token (:write-token (schema/ensure-physical-schema! conn))] (staged/native-writer - (merge (read-api) - {:strategy :inline :snapshot #(d/db conn) - :with-snapshot (fn [f] - (let [snapshot (d/open-read-snapshot conn)] - (try (d/with-read-snapshot snapshot f) - (finally (d/close-read-snapshot! snapshot))))) - :fence fence - :assert-entity (fn [eid expected] [:db.fn/call assert-entity eid expected]) - :tempid #(swap! tempids dec) - :transact! #(d/transact! conn % {:datalevin/write-token token})})))) + (merge (planner-api) + {:snapshot #(d/db conn) + :with-selected-snapshot db/with-db + :with-snapshot (fn [f] + (let [snapshot (d/open-read-snapshot conn)] + (try (d/with-read-snapshot snapshot f) + (finally (d/close-read-snapshot! snapshot))))) + :transact! #(d/transact! conn % {:datalevin/write-token token})})))) + +(defn publication-capability [database] + (admission/publication-descriptor :inline)) diff --git a/modules/eacl-datalevin/src/eacl/datalevin/schema.cljc b/modules/eacl-datalevin/src/eacl/datalevin/schema.cljc index bda41dca..f7f0582d 100644 --- a/modules/eacl-datalevin/src/eacl/datalevin/schema.cljc +++ b/modules/eacl-datalevin/src/eacl/datalevin/schema.cljc @@ -10,7 +10,8 @@ [eacl.schema.expression-persistence :as expression-persistence] [eacl.schema.expression-policy :as expression-policy] [eacl.schema.expression-resolver :as expression-resolver] - [eacl.schema.model :as model])) + [eacl.schema.model :as model] + [eacl.schema.relation-allowance :as relation-allowance])) (def datalevin-schema (merge target-storage/metadata-schema caveat-schema/datalevin-schema @@ -306,10 +307,18 @@ attributes))) (defn read-relations + "Canonical Relation definitions, including named Caveat alternatives." [db] - (mapv #(eager-entity db (:e %) relation-pull) - (ddb/avet-datoms - db :eacl.relation/resource-type+relation-name+subject-type))) + (mapv (fn [row] + (let [relation (eager-entity db (:e row) + (into relation-pull relation-allowance/attributes)) + relation (cond-> relation + (contains? relation :eacl.relation/caveats) + (update :eacl.relation/caveats + (fn [refs] + (mapv #(select-keys % [:eacl.caveat/name]) refs))))] + (relation-allowance/canonicalize relation))) + (ddb/avet-datoms db :eacl.relation/resource-type+relation-name+subject-type))) (defn read-permissions [db] @@ -330,15 +339,24 @@ :where [?q :eacl.relationship-qualifier/caveat ?c]] db eid) [])) +(defn read-authorization-schema + "Reads permission structure without compiling undemanded Caveat programs." + [snapshot-or-db] + (ddb/with-db + snapshot-or-db + (fn [db] + (let [permissions (read-permissions db)] + (expression-persistence/validate-entities permissions) + {:relations (read-relations db) :permissions permissions})))) + (defn read-schema [snapshot-or-db & [_format]] (ddb/with-db snapshot-or-db (fn [db] - (let [permissions (read-permissions db) + (let [schema (read-authorization-schema db) caveats (read-caveats db)] - (expression-persistence/validate-entities permissions) - (cond-> {:relations (read-relations db) :permissions permissions} + (cond-> schema (seq caveats) (assoc :caveats caveats)))))) (defn prepare-cache-coherence! @@ -518,6 +536,141 @@ :else (throw throwable))))) +(defn- stored-relation-caveats [db relation] + (relation-allowance/stored-caveats + {:entid #(ds/entid db %) :entity #(ddb/entity-data db %) + :rows #(ddb/relationship-identity-datoms db %1 %2 %3) + :scan #(ddb/qualified-relation-datoms db %1 %2)} relation)) + +(defn- plan-schema-write + "Pure schema validation and commit guards from one owned read snapshot." + [db schema-string + {:keys [allow-empty-schema? expression-limits allow-caveats?]} + known-schema-generation policy-installed?] + (let [expression-limits + (expression-policy/normalize-client-limits expression-limits) + new-schema-map (expression-persistence/candidate-schema + (expression-resolver/validate-schema + schema-string expression-limits {:allow-caveats? allow-caveats?})) + current-generation (current-schema-generation db) + schema-write-fence (current-schema-write-fence db) + _ (when-not (and current-generation + schema-write-fence) + (throw + (ex-info + "Datalevin schema writes require prepared generation evidence." + {:type :eacl.cache/generation-unprepared + :eacl/error :eacl.cache/generation-unprepared + :backend :datalevin + :policy-installed? policy-installed? + :missing + (cond-> [] + (nil? current-generation) + (conj :eacl.datalevin/schema-generation) + + (nil? schema-write-fence) + (conj :eacl.datalevin/schema-write-fence))}))) + existing-schema (binding [expression-persistence/*expression-limits* + expression-limits] + (read-schema db)) + _ (when (and (empty? (:definitions new-schema-map)) + (not allow-empty-schema?) + (or (seq (:relations existing-schema)) + (seq (:permissions existing-schema)) + (seq (:caveats existing-schema)))) + (throw (ex-info (str "Refusing to replace a non-empty schema with zero definitions." + " Pass {:allow-empty-schema? true} to write-schema! if this is intentional.") + {:type :eacl.schema/empty-schema-guard :eacl/error :eacl.schema/empty-schema-guard + :existing {:relations (count (:relations existing-schema)) + :permissions (count (:permissions existing-schema))}}))) + deltas (compare-schema existing-schema new-schema-map) + _ (relation-allowance/validate-existing! (:relations deltas) #(stored-relation-caveats db %)) + {:keys [relations permissions caveats]} deltas + _ (caveat-definition/validate-replacements! caveats #(caveat-references db %)) + relation-retractions (relation-allowance/entity-deletions relations) + permission-retractions + (expression-persistence/entity-deletions permissions)] + (doseq [rel relation-retractions] + (let [cnt (count-relationships-using-relation db rel)] + (when (pos? cnt) + (throw (ex-info (str "Cannot delete relation " (:eacl.relation/relation-name rel) + " because it is used by " cnt " relationships.") + {:type :eacl.schema/relation-in-use + :eacl/error :eacl.schema/relation-in-use + :relation rel + :count cnt}))))) + (let [relation-additions + (mapv #(assoc % :eacl.datalevin/relation-generation :db/current-tx) + (:additions relations)) + schema-eid (ds/entid db [:eacl/id "schema-string"]) + schema-generation + (if (= ::read-current-generation known-schema-generation) + current-generation + known-schema-generation) + relation-commit-guards + (mapv + (fn [relation] + (let [relation-eid + (ds/entid db [:eacl/id (:eacl/id relation)]) + relation-generation + (some-> (ds/datoms db :eav relation-eid + :eacl.datalevin/relation-generation) + first + :v)] + (when-not relation-generation + (throw + (ex-info + "Relation removal requires prepared native generations." + {:type :eacl.cache/generation-unprepared :eacl/error :eacl.cache/generation-unprepared + :backend :datalevin + :relation-id (:eacl/id relation)}))) + [:db.fn/cas relation-eid :eacl.datalevin/relation-generation + relation-generation relation-generation])) + (:retractions relations)) + tx-data + (vec + (concat + [[:db.fn/cas schema-eid :eacl.datalevin/schema-write-fence + schema-write-fence schema-write-fence]] + relation-commit-guards + (:additions caveats) + (relation-allowance/attribute-retractions relations) + relation-additions + (:additions permissions) + (for [caveat (caveat-definition/entity-deletions caveats) + :let [eid (ds/entid db [:eacl.caveat/name (:eacl.caveat/name caveat)])] + :when eid] + [:db/retractEntity eid]) + (for [rel relation-retractions + :let [eid (ds/entid db [:eacl/id (:eacl/id rel)])] + :when eid] + [:db/retractEntity eid]) + (for [perm permission-retractions + :let [eid (ds/entid db [:eacl/id (:eacl/id perm)])] + :when eid] + [:db/retractEntity eid]) + [{:db/id schema-eid + :eacl/id "schema-string" + :eacl/schema-string schema-string} + [:db/add schema-eid :eacl.datalevin/schema-generation + :db/current-tx] + [:db/add schema-eid :eacl.datalevin/schema-write-fence + :db/current-tx]])) + stored-string + (some-> (ds/entity db [:eacl/id "schema-string"]) + :eacl/schema-string) + changed? + (or (not= stored-string schema-string) + (some seq + [(:additions relations) + (:retractions relations) + (:additions permissions) + (:retractions permissions) + (:additions caveats) + (:retractions caveats)]))] + {:deltas deltas :tx-data tx-data :schema-generation schema-generation + :changed? (boolean changed?)}))) + (defn write-schema! "Parses, validates, diffs and transacts a SpiceDB schema string. Throws :eacl.schema/parse-error on unparseable input (a failed parse must @@ -529,143 +682,27 @@ ([conn schema-string options] (write-schema! conn schema-string options ::read-current-generation)) ([conn schema-string - {:keys [allow-empty-schema? expression-limits]} + {:keys [allow-empty-schema? expression-limits allow-caveats?]} known-schema-generation] (write-schema! conn schema-string {:allow-empty-schema? allow-empty-schema? - :expression-limits expression-limits} + :expression-limits expression-limits :allow-caveats? allow-caveats?} known-schema-generation nil)) ([conn schema-string - {:keys [allow-empty-schema? expression-limits]} + {:keys [allow-empty-schema? expression-limits allow-caveats?]} known-schema-generation write-token] - (let [expression-limits - (expression-policy/normalize-client-limits expression-limits) - new-schema-map (expression-persistence/candidate-schema - (expression-resolver/validate-schema - schema-string expression-limits)) - db (ds/db conn) - current-generation (current-schema-generation db) - schema-write-fence (current-schema-write-fence db) - _ (when-not (and current-generation - schema-write-fence) - (throw - (ex-info - "Datalevin schema writes require prepared generation evidence." - {:type :eacl.cache/generation-unprepared - :eacl/error :eacl.cache/generation-unprepared - :backend :datalevin - :policy-installed? (boolean (fork/write-policy conn)) - :missing - (cond-> [] - (nil? current-generation) - (conj :eacl.datalevin/schema-generation) - - (nil? schema-write-fence) - (conj :eacl.datalevin/schema-write-fence))}))) - existing-schema (binding [expression-persistence/*expression-limits* - expression-limits] - (read-schema db)) - _ (when (and (empty? (:definitions new-schema-map)) - (not allow-empty-schema?) - (or (seq (:relations existing-schema)) - (seq (:permissions existing-schema)) - (seq (:caveats existing-schema)))) - (throw (ex-info (str "Refusing to replace a non-empty schema with zero definitions." - " Pass {:allow-empty-schema? true} to write-schema! if this is intentional.") - {:type :eacl.schema/empty-schema-guard :eacl/error :eacl.schema/empty-schema-guard - :existing {:relations (count (:relations existing-schema)) - :permissions (count (:permissions existing-schema))}}))) - deltas (compare-schema existing-schema new-schema-map) - {:keys [relations permissions caveats]} deltas - _ (caveat-definition/validate-replacements! caveats #(caveat-references db %)) - relation-retractions (:retractions relations) - permission-retractions - (expression-persistence/entity-deletions permissions)] - (doseq [rel relation-retractions] - (let [cnt (count-relationships-using-relation db rel)] - (when (pos? cnt) - (throw (ex-info (str "Cannot delete relation " (:eacl.relation/relation-name rel) - " because it is used by " cnt " relationships.") - {:type :eacl.schema/relation-in-use - :eacl/error :eacl.schema/relation-in-use - :relation rel - :count cnt}))))) - (let [relation-additions - (mapv #(assoc % :eacl.datalevin/relation-generation :db/current-tx) - (:additions relations)) - schema-eid (ds/entid db [:eacl/id "schema-string"]) - schema-generation - (if (= ::read-current-generation known-schema-generation) - current-generation - known-schema-generation) - relation-commit-guards - (mapv - (fn [relation] - (let [relation-eid - (ds/entid db [:eacl/id (:eacl/id relation)]) - relation-generation - (some-> (ds/datoms db :eav relation-eid - :eacl.datalevin/relation-generation) - first - :v)] - (when-not relation-generation - (throw - (ex-info - "Relation removal requires prepared native generations." - {:type :eacl.cache/generation-unprepared :eacl/error :eacl.cache/generation-unprepared - :backend :datalevin - :relation-id (:eacl/id relation)}))) - [:db.fn/cas relation-eid :eacl.datalevin/relation-generation - relation-generation relation-generation])) - relation-retractions) - tx-data - (vec - (concat - [[:db.fn/cas schema-eid :eacl.datalevin/schema-write-fence - schema-write-fence schema-write-fence]] - relation-commit-guards - relation-additions - (:additions permissions) - (:additions caveats) - (for [caveat (caveat-definition/entity-deletions caveats) - :let [eid (ds/entid db [:eacl.caveat/name (:eacl.caveat/name caveat)])] - :when eid] - [:db/retractEntity eid]) - (for [rel relation-retractions - :let [eid (ds/entid db [:eacl/id (:eacl/id rel)])] - :when eid] - [:db/retractEntity eid]) - (for [perm permission-retractions - :let [eid (ds/entid db [:eacl/id (:eacl/id perm)])] - :when eid] - [:db/retractEntity eid]) - [{:db/id schema-eid - :eacl/id "schema-string" - :eacl/schema-string schema-string} - [:db/add schema-eid :eacl.datalevin/schema-generation - :db/current-tx] - [:db/add schema-eid :eacl.datalevin/schema-write-fence - :db/current-tx]])) - stored-string - (some-> (ds/entity db [:eacl/id "schema-string"]) - :eacl/schema-string) - changed? - (or (not= stored-string schema-string) - (some seq - [(:additions relations) - (:retractions relations) - (:additions permissions) - (:retractions permissions) - (:additions caveats) - (:retractions caveats)])) - report - (if changed? - (transact-schema! conn tx-data schema-generation write-token) - {:db-before db - :db-after db - :tx-data [] - :no-op? true})] - (assoc deltas - :eacl.schema/db-after (:db-after report) - :eacl.schema/no-op? (boolean (:no-op? report))))))) + (let [snapshot (ds/open-read-snapshot conn) + {:keys [deltas tx-data schema-generation changed?]} + (try + (ds/with-read-snapshot + snapshot + #(plan-schema-write % schema-string + {:allow-empty-schema? allow-empty-schema? + :expression-limits expression-limits :allow-caveats? allow-caveats?} + known-schema-generation (boolean (fork/write-policy conn)))) + (finally (ds/close-read-snapshot! snapshot))) + report (when changed? (transact-schema! conn tx-data schema-generation write-token))] + (assoc deltas + :eacl.schema/db-after (if report (:db-after report) (ds/db conn)) + :eacl.schema/no-op? (not changed?))))) diff --git a/modules/eacl-datalevin/test/eacl/bench/qualified_authorization_test.clj b/modules/eacl-datalevin/test/eacl/bench/qualified_authorization_test.clj new file mode 100644 index 00000000..e832259f --- /dev/null +++ b/modules/eacl-datalevin/test/eacl/bench/qualified_authorization_test.clj @@ -0,0 +1,256 @@ +(ns eacl.bench.qualified-authorization-test + "Explicit nREPL-only release benchmark; no automatic benchmark test vars." + (:require [clojure.edn :as edn] + [clojure.java.io :as io] + [eacl.authorization.qualification :as qualification] + [eacl.bench.qualifier-storage-test :as systems] + [eacl.caveats.jvm :as jvm] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl])) + +(def budget-path "docs/benchmarks/qualified-authorization-budgets.edn") +(def schema + "caveat enabled(flag bool) { flag } + definition user {} + definition doc { + relation member: user | user with enabled + relation banned: user | user with enabled + relation parent: doc | doc with enabled + permission direct = member + permission view = member - banned + permission via = parent->direct + permission walk = member + parent->walk + }") +(def ordinary-schema + "definition user {} + definition doc { + relation member: user + relation banned: user + relation parent: doc + permission direct = member + permission view = member - banned + permission via = parent->direct + permission walk = member + parent->walk + }") + +(defn percentile [values p] + (nth (vec (sort values)) (min (dec (count values)) (long (* p (count values)))))) + +(defn measure [operation iterations samples] + (let [started (System/nanoTime) + _ (operation) + first-call-ns (- (System/nanoTime) started)] + (let [deadline (+ (System/nanoTime) 1000000000)] + (loop [n 0] + (when (or (< n (max 30 (* 10 iterations))) (< (System/nanoTime) deadline)) + (operation) + (recur (inc n))))) + (let [bean (java.lang.management.ManagementFactory/getThreadMXBean) + tid (.threadId (Thread/currentThread)) + allocated #(.getThreadAllocatedBytes ^com.sun.management.ThreadMXBean bean tid) + _ (assert (.isThreadAllocatedMemorySupported ^com.sun.management.ThreadMXBean bean)) + _ (.setThreadAllocatedMemoryEnabled ^com.sun.management.ThreadMXBean bean true) + batches (vec (for [_ (range samples)] + (let [bytes (allocated) started (System/nanoTime)] + (dotimes [_ iterations] (operation)) + {:ns (/ (- (System/nanoTime) started) (double iterations)) + :bytes (/ (- (allocated) bytes) (double iterations))})))] + {:first-call-ns first-call-ns + :median-ns (percentile (map :ns batches) 0.5) + :p95-ns (percentile (map :ns batches) 0.95) + :allocated-bytes (percentile (map :bytes batches) 0.5) + :iterations-per-batch iterations :samples batches}))) + +(defn- qualify [scenario relationships] + (let [density (case scenario :sparse-5 5 :ordinary 0 10) + target (quot (* density (count relationships)) 100) + indices (if (#{:sparse-5 :sparse-10} scenario) + (set (map #(quot (* % (count relationships)) target) (range target))) + (set (range target)))] + (mapv (fn [i relationship] + (if (indices i) + (cond-> (assoc relationship :valid-until-ms + (if (= :expired-prefix-10 scenario) 100 200)) + (even? i) (assoc :caveat "enabled" :caveat-context {"flag" true})) + relationship)) + (range) relationships))) + +(defn fixture! [system scenario n] + (let [client (:client system) + alice (eacl/spice-object :user "alice") + bob (eacl/spice-object :user "bob") + root (eacl/spice-object :doc "root") + cycle (eacl/spice-object :doc "cycle") + documents (mapv #(eacl/spice-object :doc (format "document-%06d" %)) (range n)) + relationships (qualify scenario + (into [(eacl/->Relationship alice :member root) + (eacl/->Relationship cycle :parent root) + (eacl/->Relationship root :parent cycle)] + (mapcat (fn [i doc] + (cond-> [(eacl/->Relationship alice :member doc) + (eacl/->Relationship root :parent doc)] + (zero? (mod i 4)) + (conj (eacl/->Relationship alice :banned doc)))) + (range) documents))) + active? #(or (nil? (:valid-until-ms %)) (< 100 (:valid-until-ms %))) + active (into #{} (comp (filter active?) + (map #(vector (:subject %) (:relation %) (:resource %)))) relationships)] + (eacl/write-schema! client (if (= :ordinary scenario) ordinary-schema schema)) + ((:transact! system) (mapv #(hash-map :eacl/id (:id %)) (into [alice bob root cycle] documents))) + (doseq [batch (partition-all 200 relationships)] + (eacl/create-relationships! client (vec batch))) + {:alice alice :bob bob :documents documents :root root :cycle cycle :scenario scenario + :relationships relationships + :direct? #(contains? active [alice :member %]) + :view? #(and (contains? active [alice :member %]) + (not (contains? active [alice :banned %]))) + :via? #(and (contains? active [root :parent %]) + (contains? active [alice :member root]))})) + +(defn- read-profile [operation] + (let [calls (atom {}) requests (atom 0) original qualification/request-from-adapter + result (with-redefs [qualification/request-from-adapter + (fn [adapter options] + (let [request (original adapter options) + id (swap! requests inc) + lookup (:lookup request)] + (assoc request :lookup + (fn [eid] + (swap! calls update [id eid] (fnil inc 0)) + (lookup eid)))))] + (operation))] + {:reads (reduce + 0 (vals @calls)) :maximum-reads-per-entity (reduce max 0 (vals @calls)) + :entities-per-request (count @calls) :requests @requests :cached? (:cached? result) + :result-type (str (type result))})) + +(defn- operations [client {:keys [alice bob documents root]} mode] + (let [options (fn [] (cond-> {:caveat-context {"flag" true}} + (= :cold mode) (assoc :cache? false) + (= :warm mode) (assoc :populate-cache? false))) + index (volatile! -1) + next-doc #(nth documents (mod (vswap! index + 37) (count documents))) + point (fn [permission subject] + #(eacl/check-permission client + (merge (options) {:subject subject :resource (next-doc) + :permission permission}))) + query {:subject alice :resource/type :doc :permission :view} + fixed {:subject alice :resource root :permission :direct + :caveat-context {"flag" true "fixed" true}}] + [[:direct :point 20 (point :direct alice)] + [:negative :point 20 (point :direct bob)] + [:exclusion :point 20 (point :view alice)] + [:arrow :point 20 (point :via alice)] + [:recursive :point 10 (point :walk bob)] + [:page :page 3 #(eacl/lookup-resources client (merge query (options) {:first 50}))] + [:continuation-roundtrip :page 3 + #(let [q (merge query (options) {:first 50}) + first-page (eacl/lookup-resources client q)] + (eacl/lookup-resources client (assoc q :after (get-in first-page [:page-info :end-cursor]))))] + [:count :count 5 #(eacl/count-resources client (merge query (options)))] + [:cache-hit :cache-hit 50 #(eacl/check-permission client fixed)]])) + +(defn- validate-fixture! [client {:keys [alice bob documents root direct? view? via? scenario]}] + (when (#{:qualified-prefix-10 :expired-prefix-10} scenario) + (let [prefix (:data (eacl/read-relationships client + {:subject/type :user :subject/id "alice" + :resource/type :doc :resource/relation :member + :first 20 :cache? false})) + deadline (if (= :expired-prefix-10 scenario) 100 200)] + (assert (= 20 (count prefix))) + (assert (every? #(= deadline (:valid-until-ms %)) prefix) + (pr-str [:native-prefix scenario (mapv :valid-until-ms prefix)])))) + (doseq [doc (concat (take 20 documents) [(nth documents (quot (count documents) 2)) (peek documents)]) + [permission expected] [[:direct (direct? doc)] [:view (view? doc)] [:via (via? doc)]]] + (assert (= expected (true? + (:allowed? (eacl/check-permission client + {:subject alice :resource doc :permission permission + :caveat-context {"flag" true} :cache? false})))) + (pr-str [:point permission doc expected]))) + (assert (= false (:allowed? (eacl/check-permission client + {:subject bob :resource root :permission :walk + :caveat-context {"flag" true} :cache? false})))) + (let [expected (+ (count (filter view? documents)) (if (direct? root) 1 0)) + actual (:count (eacl/count-resources client {:subject alice :resource/type :doc :permission :view + :caveat-context {"flag" true} :cache? false}))] + (assert (= expected actual) (pr-str [:count expected actual])))) + +(defn- warm-data! [client {:keys [documents root cycle]}] + ;; Physical reads populate decoded data without publishing authorization + ;; answers. Read-only authorization can then measure fresh work on both epochs. + (doseq [resource (into [root cycle] documents)] + (eacl/read-relationships client {:resource/type :doc :resource/id (:id resource) :first 10}))) + +(defn run-case! [backend scenario enabled? output] + (binding [orchestration/*qualified-authorization-enabled?* enabled?] + (let [budgets (edn/read-string (slurp budget-path)) + system (systems/open-system backend {:clock (constantly 100) :caveat-evaluator (jvm/evaluator)})] + (try + (let [fixture (fixture! system scenario (:documents budgets)) + client (:client system) + _ (validate-fixture! client fixture) + results (into {} + (for [mode [:cold :warm] + :let [_ (when (= :warm mode) (warm-data! client fixture))] + [name kind iterations operation] (operations client fixture mode)] + (let [metric (measure operation iterations (:samples budgets)) + reads (read-profile operation)] + (assert (<= (:maximum-reads-per-entity reads) 1) + (pr-str [:duplicate-data-read scenario mode name reads])) + (when (and (= :warm mode) (not= :cache-hit name)) + (assert (not (:cached? reads)) (pr-str [:warm-answer-contamination scenario name]))) + (when (= :cache-hit name) + (assert (true? (:cached? reads)) (pr-str [:completed-answer-miss scenario mode]))) + (when (or (= :ordinary scenario) (= :cache-hit name)) + (assert (zero? (:reads reads)) (pr-str [:unexpected-data-read scenario name reads]))) + (println :measured backend scenario mode name (long (:median-ns metric))) + (flush) + [[mode name] (assoc metric :kind kind :data-reads reads)]))) + relationships (:relationships fixture) + result {:backend backend :scenario scenario :enabled? enabled? :measurement-version 5 + :java (System/getProperty "java.version") :os (System/getProperty "os.name") + :heap-max-bytes (.maxMemory (Runtime/getRuntime)) + :validated-prefix-size (if (#{:qualified-prefix-10 :expired-prefix-10} scenario) 20 0) + :arch (System/getProperty "os.arch") :processors (.availableProcessors (Runtime/getRuntime)) + :documents (:documents budgets) :relationships (count relationships) + :qualifiers (count (filter :valid-until-ms relationships)) + :caveats (count (filter :caveat relationships)) + :budgets budgets :metrics results}] + (io/make-parents output) + (spit output (pr-str result)) + (dissoc result :metrics :budgets)) + (finally ((:close! system))))))) + +(defn check-results! [directory] + (let [budgets (edn/read-string (slurp budget-path)) + reports (mapv #(edn/read-string (slurp %)) + (filter #(and (.isFile %) (.endsWith (.getName %) ".edn")) (file-seq (io/file directory)))) + index (into {} (map (juxt (juxt :backend :scenario :enabled?) identity)) reports) + _ (assert (= 24 (count reports) (count index)) "Expected one report for each of the 24 cases") + _ (doseq [report reports] + (assert (= 5 (:measurement-version report)) "Mixed measurement versions") + (assert (= budgets (:budgets report)) "Budget provenance changed") + (assert (= 18 (count (:metrics report))) "Incomplete operation matrix") + (doseq [[_ metric] (:metrics report)] + (assert (= (:samples budgets) (count (:samples metric))) "Incomplete sample set"))) + failures + (vec (for [backend (:backends budgets) + scenario (:cases budgets) + :let [baseline (get index [backend :ordinary false]) + candidate (get index [backend scenario true]) + _ (assert (and baseline candidate) (pr-str [:missing-report backend scenario])) + _ (assert (= budgets (:budgets baseline) (:budgets candidate)) "Budget provenance changed")] + [operation metric] (:metrics candidate) + :let [original (get-in baseline [:metrics operation]) + {:keys [latency-factor latency-slack-ns allocation-factor allocation-slack-bytes]} + (get-in budgets [:relative-budgets scenario])] + [quantity actual limit] + [[:median-ns (:median-ns metric) (+ latency-slack-ns (* latency-factor (:median-ns original)))] + [:p95-ns (:p95-ns metric) (+ latency-slack-ns (* latency-factor (:p95-ns original)))] + [:absolute-p95-ns (:p95-ns metric) (get-in budgets [:absolute-p95-ns (:kind metric)])] + [:allocated-bytes (:allocated-bytes metric) + (+ allocation-slack-bytes (* allocation-factor (:allocated-bytes original)))]] + :when (> actual limit)] + {:backend backend :scenario scenario :operation operation :quantity quantity + :actual actual :limit limit}))] + {:reports (count reports) :comparisons (* (count (:backends budgets)) (count (:cases budgets)) 18 4) + :passed? (empty? failures) :failures failures})) diff --git a/modules/eacl-datalevin/test/eacl/bench/qualifier_storage_test.clj b/modules/eacl-datalevin/test/eacl/bench/qualifier_storage_test.clj index 9f80436d..77bbcfef 100644 --- a/modules/eacl-datalevin/test/eacl/bench/qualifier_storage_test.clj +++ b/modules/eacl-datalevin/test/eacl/bench/qualifier_storage_test.clj @@ -31,43 +31,45 @@ (when directory (reduce + 0 (map #(.length %) (filter #(.isFile %) (file-seq (io/file directory))))))) -(defn open-system [backend] - (let [directory (u/tmp-dir (str "eacl-qualifier-bench-" (random-uuid))) - uri (str "datomic:mem://qualifier-bench-" (random-uuid)) - watermark (atom 0) - config {:security-key "01234567890123456789012345678901" - :source-lifecycle "qualifier-benchmark"} - [conn api db transact! rows close!] - (case backend - :datomic - (do (dt/create-database uri) - (let [conn (dt/connect uri)] - (if-let [install! (ns-resolve 'eacl.datomic.schema 'install!)] - (install! conn) - @(dt/transact conn dt-schema/v8-schema)) - [conn dt-api/make-client dt/db #(deref (dt/transact %1 %2)) - #(dt/datoms %1 :aevt %2) - #(do (dt/release conn) (dt/delete-database uri))])) - :datahike - (let [conn (dh-api/create-conn nil {:store {:backend :file :path (str directory "/db")}}) - config (:config (dh/db conn))] - [conn dh-api/make-client dh/db dh/transact - #(dh/datoms %1 {:index :aevt :components [%2]}) - #(do (dh/release conn) (dh/delete-database config))]) - :datascript - (let [conn (ds-api/create-conn)] - [conn ds-api/make-client ds/db ds/transact! #(ds/datoms %1 :aevt %2) (fn [])]) - :datalevin - (let [conn (dl-api/create-conn directory)] - [conn dl-api/make-client dl/db dl/transact! #(dl/datoms %1 :ave %2) #(dl/close conn)])) - config (cond-> config - (= :datalevin backend) - (assoc :revision-watermark watermark - :advance-revision-watermark! #(swap! watermark max %)))] - {:client (api conn config) :snapshot #(db conn) - :transact! #(transact! conn %) :rows rows - :durable-bytes #(when (#{:datahike :datalevin} backend) (directory-bytes directory)) - :close! #(do (close!) (when (.exists (io/file directory)) (u/delete-files directory)))})) +(defn open-system + ([backend] (open-system backend {})) + ([backend options] + (let [directory (u/tmp-dir (str "eacl-qualifier-bench-" (random-uuid))) + uri (str "datomic:mem://qualifier-bench-" (random-uuid)) + watermark (atom 0) + config {:security-key "01234567890123456789012345678901" + :source-lifecycle "qualifier-benchmark"} + [conn api db transact! rows close!] + (case backend + :datomic + (do (dt/create-database uri) + (let [conn (dt/connect uri)] + (if-let [install! (ns-resolve 'eacl.datomic.schema 'install!)] + (install! conn) + @(dt/transact conn dt-schema/v8-schema)) + [conn dt-api/make-client dt/db #(deref (dt/transact %1 %2)) + #(dt/datoms %1 :aevt %2) + #(do (dt/release conn) (dt/delete-database uri))])) + :datahike + (let [conn (dh-api/create-conn nil {:store {:backend :file :path (str directory "/db")}}) + config (:config (dh/db conn))] + [conn dh-api/make-client dh/db dh/transact + #(dh/datoms %1 {:index :aevt :components [%2]}) + #(do (dh/release conn) (dh/delete-database config))]) + :datascript + (let [conn (ds-api/create-conn)] + [conn ds-api/make-client ds/db ds/transact! #(ds/datoms %1 :aevt %2) (fn [])]) + :datalevin + (let [conn (dl-api/create-conn directory)] + [conn dl-api/make-client dl/db dl/transact! #(dl/datoms %1 :ave %2) #(dl/close conn)])) + config (cond-> config + (= :datalevin backend) + (assoc :revision-watermark watermark + :advance-revision-watermark! #(swap! watermark max %)))] + {:client (api conn (merge config options)) :snapshot #(db conn) + :transact! #(transact! conn %) :rows rows + :durable-bytes #(when (#{:datahike :datalevin} backend) (directory-bytes directory)) + :close! #(do (close!) (when (.exists (io/file directory)) (u/delete-files directory)))}))) (defn- percentile [values p] (nth (vec (sort values)) (min (dec (count values)) (long (* p (count values)))))) diff --git a/modules/eacl-datalevin/test/eacl/datalevin/caveat_schema_test.clj b/modules/eacl-datalevin/test/eacl/datalevin/caveat_schema_test.clj index 6dffa9bd..ec5ca60c 100644 --- a/modules/eacl-datalevin/test/eacl/datalevin/caveat_schema_test.clj +++ b/modules/eacl-datalevin/test/eacl/datalevin/caveat_schema_test.clj @@ -1,5 +1,6 @@ (ns eacl.datalevin.caveat-schema-test (:require [eacl.datalevin.core :as api] + [eacl.datalevin.backend :as backend] [eacl.cache :as cache] [eacl.caveats.hot-path-contract :as hot-path] [clojure.test :refer [deftest]] @@ -8,6 +9,8 @@ [eacl.datalevin.schema :as schema] [eacl.caveats.persistence-contract :as contract] [eacl.caveats.publication-contract :as publication] + [eacl.relationships.edge-contract :as edge-contract] + [eacl.datalevin.impl :as scan-impl] [eacl.datalevin.qualifiers :as qualifiers])) (defn interleave! [competitor outer] @@ -42,3 +45,25 @@ [[:db/add eid :eacl.datalevin/schema-generation :db/current-tx] [:db/add eid :eacl.datalevin/schema-write-fence :db/current-tx]]))})) (finally (d/close conn) (util/delete-files dir))))) + +(deftest compact-qualified-scans + (let [dir (util/tmp-dir (str "edge-scan-" (random-uuid))) + conn (schema/create-conn dir {}) + token (:write-token (schema/ensure-physical-schema! conn))] + (try + (edge-contract/check! + {:write-schema! #(schema/write-schema! conn % {} (schema/current-schema-generation (d/db conn)) token) :writer #(qualifiers/writer conn) + :entid d/entid :forward scan-impl/subject->resources + :reverse scan-impl/resource->subjects :direct scan-impl/direct-edge + :with-basis (fn [f] + (let [snapshot (d/open-read-snapshot conn)] + (try + (d/with-read-snapshot + snapshot + (fn [database] + (f database + (backend/basis-adapter + snapshot {backend/prepared-schema-eid-key + (d/entid database [:eacl/id "schema-string"])})))) + (finally (d/close-read-snapshot! snapshot)))))}) + (finally (d/close conn) (util/delete-files dir))))) diff --git a/modules/eacl-datalevin/test/eacl/datalevin/contract_test.clj b/modules/eacl-datalevin/test/eacl/datalevin/contract_test.clj index c2561bd5..3bd126dc 100644 --- a/modules/eacl-datalevin/test/eacl/datalevin/contract_test.clj +++ b/modules/eacl-datalevin/test/eacl/datalevin/contract_test.clj @@ -853,8 +853,9 @@ scan ddb/avet-endpoint-prefix] (with-redefs [ddb/avet-endpoint-prefix (fn [& args] - (when (number? (last args)) - (swap! native-limits conj (last args))) + (let [limit (if (boolean? (last args)) (nth args (- (count args) 2)) (last args))] + (when (number? limit) + (swap! native-limits conj limit))) (apply scan args))] (let [relationships (loop [after nil @@ -927,47 +928,47 @@ schema-reads (atom 0) ledger (request-counters/make-ledger) open-read-snapshot d/open-read-snapshot - read-schema datalevin-schema/read-schema + read-schema datalevin-schema/read-authorization-schema escaped (atom nil)] (with-redefs [d/open-read-snapshot (fn [connection] (swap! opens inc) (open-read-snapshot connection)) - datalevin-schema/read-schema + datalevin-schema/read-authorization-schema (fn [db] (swap! schema-reads inc) (read-schema db))] (binding [request-counters/*ledger* ledger] (eacl/with-snapshot [snapshot (eacl/snapshot client)] - (reset! escaped snapshot) - (is (= 1 (count (:relations (eacl/read-schema snapshot))))) - (is (= 1 - (count - (:data - (eacl/read-relationships - snapshot {:subject/type :user - :subject/id "alice" - :resource/type :document - :resource/relation :viewer - :first 10 - :cache? false}))))) - (dotimes [_ 4] - (is (:allowed? - (eacl/check-permission - snapshot {:subject alice - :permission :view - :resource document - :cache? false})))) - (is (= :eacl/unsupported-capability - (:type - (error-data - #(eacl/delete-relationship! - snapshot alice :viewer document))))) - (is (= :eacl/snapshot-thread-violation - (:type - @(future - (error-data - #(eacl/can? snapshot alice :view document)))))))) + (reset! escaped snapshot) + (is (= 1 (count (:relations (eacl/read-schema snapshot))))) + (is (= 1 + (count + (:data + (eacl/read-relationships + snapshot {:subject/type :user + :subject/id "alice" + :resource/type :document + :resource/relation :viewer + :first 10 + :cache? false}))))) + (dotimes [_ 4] + (is (:allowed? + (eacl/check-permission + snapshot {:subject alice + :permission :view + :resource document + :cache? false})))) + (is (= :eacl/unsupported-capability + (:type + (error-data + #(eacl/delete-relationship! + snapshot alice :viewer document))))) + (is (= :eacl/snapshot-thread-violation + (:type + @(future + (error-data + #(eacl/can? snapshot alice :view document)))))))) (is (= 1 @opens)) (is (= 2 @schema-reads) "one public schema read plus one shared request-local parse") diff --git a/modules/eacl-datalevin/test/eacl/datalevin/mutation_race_test.clj b/modules/eacl-datalevin/test/eacl/datalevin/mutation_race_test.clj index 9fb29c33..92ba22c2 100644 --- a/modules/eacl-datalevin/test/eacl/datalevin/mutation_race_test.clj +++ b/modules/eacl-datalevin/test/eacl/datalevin/mutation_race_test.clj @@ -3,6 +3,8 @@ [datalevin.core :as d] [datalevin.util :as u] [eacl.core :as eacl] + [eacl.client.orchestration :as orchestration] + [eacl.relationships.staged :as staged] [eacl.datalevin.backend :as backend] [eacl.datalevin.core :as datalevin] [eacl.datalevin.schema :as physical-schema] @@ -132,20 +134,30 @@ (defn- run-at-same-commit-boundary [intercept? left right] (let [ready (CountDownLatch. 2) - intercepted (atom 0) + intercepted (atom #{}) original d/transact! - transact - (fn [& args] - (let [tx-data (second args) - slot (when (intercept? tx-data) - (swap! intercepted inc))] - (when (and slot (<= slot 2)) - (.countDown ready) - (when-not (.await ready 5 TimeUnit/SECONDS) - (throw (ex-info "race barrier timed out" - {:type :test/barrier-timeout})))) - (apply original args)))] - (with-redefs [d/transact! transact] + original-plan staged/plan-batch + rendezvous! + (fn [tx-data] + (when (or (intercept? tx-data) (empty? tx-data)) + (let [thread (Thread/currentThread) + [before _] (swap-vals! intercepted conj thread)] + (when-not (contains? before thread) + (.countDown ready) + (when-not (.await ready 5 TimeUnit/SECONDS) + (throw (ex-info "race barrier timed out" + {:type :test/barrier-timeout})))))))] + ;; Qualified no-op deletes need no transaction. Synchronize their completed + ;; plans too, so both operations have selected the same pre-commit state. + (with-redefs [d/transact! (fn [& args] + (rendezvous! (second args)) + (apply original args)) + staged/plan-batch + (fn [& args] + (let [plan (apply original-plan args)] + (when orchestration/*qualified-authorization-enabled?* + (rendezvous! (:tx-data plan))) + plan))] (let [left-result (future (error-data left)) right-result (future (error-data right))] [(deref left-result 10000 {:type :test/future-timeout}) @@ -170,8 +182,9 @@ #(eacl/create-relationship! client rel) #(eacl/create-relationship! client rel))] (is (= 1 (count (filter nil? results)))) - (is (= [:eacl/relationship-conflict] - (keep :type results))) + (is (= 1 (count (keep :type results)))) + (is (every? #{:eacl/relationship-conflict :eacl/relationship-concurrent-write} + (keep :type results))) (assert-paired! conn))))) (doseq [left-operation [:create :touch]] (testing (str (name left-operation) "/delete") @@ -211,8 +224,9 @@ #(eacl/create-relationship! client rel) #(eacl/create-relationship! second-client rel))] (is (= 1 (count (filter nil? results)))) - (is (= [:eacl/relationship-conflict] - (keep :type results))) + (is (= 1 (count (keep :type results)))) + (is (every? #{:eacl/relationship-conflict :eacl/relationship-concurrent-write} + (keep :type results))) (assert-paired! conn) (is (= (:max-tx (d/db conn)) (:viewer-version (coherence-state conn))))) @@ -251,6 +265,9 @@ (deftest object-delete-rescans-at-commit-after-intervening-create-test (with-system (fn [{:keys [conn client alice] :as system}] + (d/transact! conn [{:eacl/id "document-2"}]) + (eacl/create-relationship! client alice :viewer + (eacl/spice-object :document "document-2")) (let [rel (relationship system) injected? (atom false) original d/transact! @@ -262,7 +279,7 @@ tx-data))] (with-redefs [d/transact! - (fn [& args] + (fn [& args] (when (and (delete-function-call? (second args)) (compare-and-set! injected? false true)) (eacl/create-relationship! client rel)) diff --git a/modules/eacl-datalevin/test/eacl/datalevin/ordered_generation_test.clj b/modules/eacl-datalevin/test/eacl/datalevin/ordered_generation_test.clj index 9ce8ebfd..62004ba9 100644 --- a/modules/eacl-datalevin/test/eacl/datalevin/ordered_generation_test.clj +++ b/modules/eacl-datalevin/test/eacl/datalevin/ordered_generation_test.clj @@ -6,6 +6,7 @@ [eacl.backend.v8 :as backend] [eacl.contract-support :as contract] [eacl.core :as eacl] + [eacl.client.orchestration :as orchestration] [eacl.datalevin.core :as datalevin] [eacl.proof-frame :as proof-frame] [eacl.spicedb.consistency :as consistency])) @@ -64,7 +65,7 @@ (ex-info "unexpected schema identity lookup" {:type :test/unexpected-entid})))] (is (integer? (backend/invoke adapter - :schema-generation))))))) + :schema-generation))))))) (let [alice (eacl/spice-object :user "alice") bob (eacl/spice-object :user "bob") document-1 (eacl/spice-object :document "document-1") @@ -83,16 +84,16 @@ (is (true? (:cached? response))) (is (zero? (get @operations :proof-frame 0))))) - (testing "an unrelated relation commit is a managed hit" + (testing "an unrelated commit uses the epoch's certified cache path" (let [before (:managed-hits (datalevin/cache-stats client))] (eacl/create-relationship! client editor) (let [operations (atom {}) response (binding [backend/*backend-op-stats* operations] (eacl/check-permission client demand))] - (is (true? (:cached? response))) - (is (= 1 (get @operations :proof-frame 0))) - (is (= (inc before) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? response))) + (is (= (if orchestration/*qualified-authorization-enabled?* 0 1) (get @operations :proof-frame 0))) + (is (= (if orchestration/*qualified-authorization-enabled?* before (inc before)) (:managed-hits (datalevin/cache-stats client))))))) (testing "a relevant relation commit invalidates the answer" @@ -214,13 +215,13 @@ :first-page first-page :oracle-stream oracle-stream :durability :durable})) - (testing "managed reuse remains enabled after reopen" + (testing "reopened sources preserve the epoch's cache policy" (eacl/create-relationship! second-client editor) (let [before (:managed-hits (datalevin/cache-stats second-client)) response (eacl/check-permission second-client demand)] - (is (true? (:cached? response))) - (is (= (inc before) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? response))) + (is (= (if orchestration/*qualified-authorization-enabled?* before (inc before)) (:managed-hits (datalevin/cache-stats second-client))))))) (finally diff --git a/modules/eacl-datalevin/test/eacl/datalevin/qualified_write_test.clj b/modules/eacl-datalevin/test/eacl/datalevin/qualified_write_test.clj new file mode 100644 index 00000000..03d76a32 --- /dev/null +++ b/modules/eacl-datalevin/test/eacl/datalevin/qualified_write_test.clj @@ -0,0 +1,172 @@ +(ns eacl.datalevin.qualified-write-test + (:require [clojure.test :refer [deftest is]] + [datalevin.core :as d] + [datalevin.util :as util] + [eacl.caveats.publication-batch-contract :as batch] + [eacl.caveats.public-write-contract :as public] + [eacl.caveats.schema-allowance-contract :as allowance] + [eacl.caveats.inspection-contract :as inspection] + [eacl.caveats.deletion-contract :as deletion] + [eacl.caveats.cache-trace-contract :as cache-trace] + [eacl.authorization.qualification-test :as fixtures] + [eacl.datalevin.core :as api] + [eacl.datalevin.db :as db] + [eacl.relationships.storage :as storage] + [eacl.schema.relation-allowance :as relation-allowance] + [eacl.client.orchestration :as orchestration] + [eacl.datalevin.caveat-schema-test :as schema-races] + [eacl.datalevin.schema :as schema] + [eacl.datalevin.qualifiers :as qualifiers])) + +(deftest qualified-batches-publish-atomically + (let [dir (util/tmp-dir (str "qualified-batch-" (random-uuid))) + conn (schema/create-conn dir {:app/flag {:db/valueType :db.type/long}}) + token (:write-token (schema/ensure-physical-schema! conn))] + (try + (batch/check! {:write-schema! #(schema/write-schema! conn % {} (schema/current-schema-generation (d/db conn)) token) + :writer #(qualifiers/writer conn) :entid d/entid :strategy :inline + :allowance-stamps (fn [database] + (let [eid (d/entid database [:eacl/id "schema-string"])] + [[:db/add eid :eacl.datalevin/schema-generation :db/current-tx] + [:db/add eid :eacl.datalevin/schema-write-fence :db/current-tx]]))}) + (finally (d/close conn) (util/delete-files dir))))) + +(deftest public-qualified-writes-preserve-identity-and-commit-atomically + (let [dir (util/tmp-dir (str "public-qualified-" (random-uuid))) + conn (schema/create-conn dir {:app/flag {:db/valueType :db.type/long}}) + now (atom 99) + watermark (atom 0)] + (try + (public/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0)) + :source-lifecycle "public-qualified-write" + :security-key "01234567890123456789012345678901" + :revision-watermark watermark + :advance-revision-watermark! (fn [revision] (swap! watermark max revision))}) + :writer #(qualifiers/writer conn) :entid d/entid :now now :speculative? false + :allowance-stamps (fn [database] + (let [eid (d/entid database [:eacl/id "schema-string"])] + [[:db/add eid :eacl.datalevin/schema-generation :db/current-tx] + [:db/add eid :eacl.datalevin/schema-write-fence :db/current-tx]]))}) + (finally (d/close conn) (util/delete-files dir))))) + +(deftest schema-alternatives-preserve-relation-identities-and-retained-data + (let [dir (util/tmp-dir (str "schema-allowance-" (random-uuid))) + conn (schema/create-conn dir {}) + token (:write-token (schema/ensure-physical-schema! conn)) + watermark (atom 0)] + (try + (allowance/check! {:client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0)) + :source-lifecycle "schema-allowance" + :security-key "01234567890123456789012345678901" + :revision-watermark watermark + :advance-revision-watermark! (fn [revision] (swap! watermark max revision))}) + :writer #(qualifiers/writer conn) + :read-schema schema/read-schema :interleave! schema-races/interleave! :entid d/entid}) + (finally (d/close conn) (util/delete-files dir))))) + +(deftest schema-allowance-scan-keeps-every-row-across-native-batches + (let [dir (util/tmp-dir (str "schema-allowance-scan-" (random-uuid))) + conn (schema/create-conn dir {}) + token (:write-token (schema/ensure-physical-schema! conn))] + (try + (schema/write-schema! conn "definition user {}\ndefinition doc {\n relation viewer: user\n}" + {} (schema/current-schema-generation (d/db conn)) token) + (d/transact! conn (mapv #(hash-map :eacl/id (str "scan/" %)) (range 1030))) + (let [database (d/db conn) + rid (d/entid database [:eacl.relation/resource-type+relation-name+subject-type [:doc :viewer :user]]) + ids (mapv #(d/entid database [:eacl/id (str "scan/" %)]) (range 1030)) + ;; Equal endpoint tails cross the batch boundary; owner + complete + ;; tuple value must advance without omitting or repeating rows. + rows (mapv (fn [owner] [:db/add owner storage/forward-attribute [:user rid :doc (first ids) nil]]) ids)] + (d/transact! conn (conj rows [:db/add rid :eacl.datalevin/relation-generation :db/current-tx]) + {:datalevin/write-token token}) + (let [observed (db/qualified-relation-datoms (d/db conn) storage/forward-attribute [:user rid :doc])] + (is (= ids (mapv :e observed))) + (is (= 1030 (count observed))))) + (finally (d/close conn) (util/delete-files dir))))) + +(deftest schema-validation-and-commit-guards-share-one-owned-snapshot + (let [dir (util/tmp-dir (str "schema-owned-race-" (random-uuid))) + conn (schema/create-conn dir {}) + watermark (atom 0) + before (d/active-read-snapshot-info)] + (try + (allowance/check! + {:client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0)) + :source-lifecycle "schema-owned-race" + :security-key "01234567890123456789012345678901" + :revision-watermark watermark + :advance-revision-watermark! (fn [revision] (swap! watermark max revision))}) + :writer #(qualifiers/writer conn) :read-schema schema/read-schema :entid d/entid + :interleave! + (fn [competitor outer] + (let [validate relation-allowance/validate-existing! + armed (atom true)] + (with-redefs [relation-allowance/validate-existing! + (fn [deltas referenced] + (let [result (validate deltas referenced)] + (when (compare-and-set! armed true false) + ;; A separate thread owns its own native reads. + ;; The schema reader remains pinned throughout. + (let [outcome (promise) + worker (Thread. (fn [] + (try (binding [orchestration/*qualified-authorization-enabled?* true] (competitor)) + (deliver outcome nil) + (catch Throwable e (deliver outcome e)))))] + (.start worker) + (let [result (deref outcome 10000 ::timeout)] + (when (= ::timeout result) (throw (ex-info "Competing writer timed out" {}))) + (when result (throw result))))) + result))] + (outer))))}) + (is (= before (d/active-read-snapshot-info))) + (finally (d/close conn) (util/delete-files dir))))) + +(deftest stored-and-active-inspection-preserve-aligned-native-qualifiers + (let [dir (util/tmp-dir (str "public-qualified-" (random-uuid))) + conn (schema/create-conn dir {:app/flag {:db/valueType :db.type/long}}) + now (atom 99) + watermark (atom 0)] + (try + (inspection/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0)) + :source-lifecycle "public-qualified-write" + :security-key "01234567890123456789012345678901" + :revision-watermark watermark + :advance-revision-watermark! (fn [revision] (swap! watermark max revision))}) + :writer #(qualifiers/writer conn) :entid d/entid :now now :speculative? false + :allowance-stamps (fn [database] + (let [eid (d/entid database [:eacl/id "schema-string"])] + [[:db/add eid :eacl.datalevin/schema-generation :db/current-tx] + [:db/add eid :eacl.datalevin/schema-write-fence :db/current-tx]]))}) + (finally (d/close conn) (util/delete-files dir))))) + +(deftest qualified-cache-traces-match-uncached-authorization + (let [dir (util/tmp-dir (str "qualified-cache-trace-" (random-uuid))) + conn (schema/create-conn dir {}) now (atom 99) watermark (atom 0) + lifecycle-file (str dir "/trace-lifecycle.txt") + _ (spit lifecycle-file "qualified-cache-trace") + make-client #(api/make-client conn {:clock (fn [] @now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0)) + :source-lifecycle (slurp lifecycle-file) + :security-key "01234567890123456789012345678901" + :revision-watermark watermark + :advance-revision-watermark! (fn [revision] (swap! watermark max revision))})] + (try + (cache-trace/check! {:client (make-client) :writer #(qualifiers/writer conn) :now now + :rotate-client! (fn [_ lifecycle] (spit lifecycle-file lifecycle) (make-client))}) + (finally (d/close conn) (util/delete-files dir))))) + +(deftest qualified-object-deletion-is-atomic-and-bounded + (let [dir (util/tmp-dir (str "qualified-deletion-" (random-uuid))) + conn (schema/create-conn dir {}) watermark (atom 0)] + (try + (deletion/check! {:client (api/make-client conn {:clock (constantly 200) + :caveat-evaluator (fixtures/portable-evaluator (atom 0)) + :source-lifecycle "qualified-deletion" + :security-key "01234567890123456789012345678901" + :revision-watermark watermark + :advance-revision-watermark! #(swap! watermark max %)}) + :writer #(qualifiers/writer conn)}) + (finally (d/close conn) (util/delete-files dir))))) diff --git a/modules/eacl-datascript/README.md b/modules/eacl-datascript/README.md index c4931393..7fa16322 100644 --- a/modules/eacl-datascript/README.md +++ b/modules/eacl-datascript/README.md @@ -129,7 +129,7 @@ repairs old peer-only ghosts by enumerating relation definitions and making exact index probes. Use the integrity report when the old eid is unknown. ```clojure -{:deps {dev.eacl/eacl-datascript {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datascript {:mvn/version "9.0.0-SNAPSHOT"}}} ``` Its POM depends on `dev.eacl/eacl` at the exact same version, so consumers do @@ -142,10 +142,12 @@ Maven consumers install no formal tools. ## Relationship storage 9 -This EACL v8 adapter uses five-slot endpoint pairs with a trailing nullable -`qualifier-eid`. This phase writes `nil` and raises `:eacl/unsupported-qualifier` -when serving encounters a qualifier. Upgrades are explicit and restartable; -ordinary client construction requires a completed target store. Follow the -[7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before starting clients. +This adapter uses five-slot endpoint pairs with a trailing nullable +`qualifier-eid`. V8 writes only `nil` and rejects non-nil qualifiers. V9 adds +[Caveats and expiring Relationships](../../docs/caveats.md) through coordinated +qualified activation; older readers must be drained first. Upgrades are explicit +and restartable, and client construction requires a completed target store. +Follow the [7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before +starting clients, then the v9 serving rollout guide before qualified writes. The adapter's `create-conn` helper explicitly bootstraps fresh stores. diff --git a/modules/eacl-datascript/src/eacl/datascript/backend.cljc b/modules/eacl-datascript/src/eacl/datascript/backend.cljc index e58de168..9361d40d 100644 --- a/modules/eacl-datascript/src/eacl/datascript/backend.cljc +++ b/modules/eacl-datascript/src/eacl/datascript/backend.cljc @@ -2,6 +2,7 @@ "DataScript storage operations for the shared v8 authorization engine." (:require [datascript.core :as ds] [datascript.db :as dsdb] + [eacl.authorization.data :as qualification-data] [eacl.backend.source :as source] [eacl.schema.expression-persistence :as expression-persistence] [eacl.backend.v8 :as backend] @@ -44,7 +45,9 @@ :branch nil}))) (def adapter-capabilities - {:cursor #{:forward :reverse :opaque :authenticated :encrypted} + {:qualification #{qualification-data/capability} + :qualified-publication #{:atomic-prepared-v1} + :cursor #{:forward :reverse :opaque :authenticated :encrypted} :cache-proofs #{:ordered-generations :snapshot-bound :database-visible} :runtime #{:clj :cljs}}) @@ -202,6 +205,14 @@ (impl/direct-match? db subject-type subject-id relation-id resource-type resource-id)) + :direct-edge + (fn [subject-type subject-id relation-id resource-type resource-id] + (impl/direct-edge db subject-type subject-id relation-id resource-type resource-id)) + + :qualification-data + (fn [eid] + (qualification-data/collect eid (ds/datoms db :eavt eid) identity true)) + :all-permission-nodes (fn [] (->> (ds/datoms diff --git a/modules/eacl-datascript/src/eacl/datascript/core.cljc b/modules/eacl-datascript/src/eacl/datascript/core.cljc index 7b194ca2..0c64b7bd 100644 --- a/modules/eacl-datascript/src/eacl/datascript/core.cljc +++ b/modules/eacl-datascript/src/eacl/datascript/core.cljc @@ -10,10 +10,12 @@ [eacl.cursor :as cursor] [eacl.datascript.backend :as datascript-backend] [eacl.datascript.impl :as impl] + [eacl.datascript.qualifiers :as qualifiers] [eacl.datascript.schema :as schema] [eacl.datascript.storage :as target-storage] [eacl.relationships.upgrade :as storage-upgrade] - [eacl.relationships.storage :as relationship-storage])) + [eacl.relationships.storage :as relationship-storage] + [eacl.relationships.staged :as staged])) (def cursor->token cursor/cursor->token) (def token->cursor cursor/token->cursor) @@ -80,6 +82,11 @@ (def ^:private api {:backend-id :datascript + :writer-max-attempts 8 + :writer-contention? staged/prepared-contention? + :qualified-writer #'qualifiers/writer + :qualified-publication-capability #'qualifiers/publication-capability + :qualified-plan #'qualifiers/plan :db ds/db :entid ds/entid :default-entid->object-id (fn [db eid] (:eacl/id (ds/entity db eid))) @@ -104,11 +111,13 @@ ;; the impl suites) and REPL redefinition visible through the shared ;; orchestration. :schema {:read-schema #'schema/read-schema + :read-authorization-schema #'schema/read-authorization-schema :generation #'schema/current-schema-generation :plan-replacement #'schema/plan-schema-replacement :write-schema! #'schema/write-schema!} :impl {:validate-relationship-operation! #'impl/validate-relationship-operation! + :relationship-publication-input #'impl/relationship-publication-input :relationship-relation-id #'impl/relationship-relation-id :relation-coordinate relation-coordinate :tx-update-relationship #'impl/tx-update-relationship diff --git a/modules/eacl-datascript/src/eacl/datascript/db.cljc b/modules/eacl-datascript/src/eacl/datascript/db.cljc index 433594a6..afc2d23c 100644 --- a/modules/eacl-datascript/src/eacl/datascript/db.cljc +++ b/modules/eacl-datascript/src/eacl/datascript/db.cljc @@ -66,6 +66,8 @@ ([db entity attr prefix] (eavt-endpoint-prefix db entity attr prefix nil :asc)) ([db entity attr prefix cursor-eid direction] + (eavt-endpoint-prefix db entity attr prefix cursor-eid direction false)) + ([db entity attr prefix cursor-eid direction include-qualifier?] (if-not (and (nat-int? entity) (endpoint-pair/valid-prefix? prefix) (#{:asc :desc} direction)) @@ -83,7 +85,8 @@ (if (and first-datom (matching-eavt-prefix? entity attr prefix first-datom)) (endpoint-pair/checked-datoms - (take-while #(matching-eavt-prefix? entity attr prefix %) scan)) + (take-while #(matching-eavt-prefix? entity attr prefix %) scan) + include-qualifier?) []))))) (defn avet-endpoint-prefix @@ -92,6 +95,8 @@ ([db attr prefix] (avet-endpoint-prefix db attr prefix nil :asc)) ([db attr prefix cursor-eid direction] + (avet-endpoint-prefix db attr prefix cursor-eid direction false)) + ([db attr prefix cursor-eid direction include-qualifier?] (if-not (and (endpoint-pair/valid-prefix? prefix) (#{:asc :desc} direction)) [] @@ -105,5 +110,15 @@ (if (and first-datom (matching-avet-prefix? attr prefix first-datom)) (endpoint-pair/checked-datoms - (take-while #(matching-avet-prefix? attr prefix %) scan)) + (take-while #(matching-avet-prefix? attr prefix %) scan) include-qualifier?) []))))) + +(defn entity-facts [database eid] + (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) (ds/datoms database :eavt eid))) + +(defn entity-data [database eid] + (let [rows (entity-facts database eid)] + (when (seq rows) + (reduce (fn [result [a v]] + (if (= :eacl.relation/caveats a) (update result a (fnil conj #{}) v) (assoc result a v))) + {:db/id eid} rows)))) diff --git a/modules/eacl-datascript/src/eacl/datascript/impl.cljc b/modules/eacl-datascript/src/eacl/datascript/impl.cljc index 1a29f79c..2e7b580e 100644 --- a/modules/eacl-datascript/src/eacl/datascript/impl.cljc +++ b/modules/eacl-datascript/src/eacl/datascript/impl.cljc @@ -3,6 +3,8 @@ [eacl.core :as eacl :refer [spice-object]] [eacl.datascript.db :as ddb] [eacl.engine.relationships :as relationship-engine] + [eacl.relationships.edge :as edge] + [eacl.relationships.inspection :as inspection] [eacl.relationships.endpoint-pair :as endpoint-pair] [eacl.relationships.filters :as relationship-filters] [eacl.relationships.mutations :as relationship-mutations] @@ -73,7 +75,7 @@ ;; EAVT contains at most one datom for one [entity attribute value] ;; tuple, so an exclusive native seek can expose at most one boundary ;; row. Avoid constructing a predicate-bearing lazy sequence per scan. - (if (= bound-eid (first values)) (rest values) values) + (if (= bound-eid (some-> (first values) edge/endpoint)) (rest values) values) values)) (def ^:private scan-value (comp #(nth % 3) :v)) @@ -84,13 +86,13 @@ wrapper allocation); non-empty scans remain lazy and bounded by their caller." [db endpoint-id attribute prefix cursor-or-options] - (let [{:keys [direction bound-eid inclusive-bound?]} + (let [{:keys [direction bound-eid inclusive-bound? include-qualifier?]} (relationship-storage/normalize-scan-options cursor-or-options) datoms (ddb/eavt-endpoint-prefix - db endpoint-id attribute prefix bound-eid direction)] + db endpoint-id attribute prefix bound-eid direction include-qualifier?)] (if-let [datoms (seq datoms)] (apply-exclusive-scan-bound - (map scan-value datoms) + (map (if include-qualifier? edge/from-datom scan-value) datoms) bound-eid inclusive-bound?) []))) @@ -232,6 +234,14 @@ :resource-identity-guard (endpoint-identity-guard db :resource resource-id resource)})) +(defn relationship-publication-input + "Resolves the same endpoint identities and commit guards as ordinary writes." + [db relationship] + (let [resolved (resolve-relationship db relationship)] + {:relationship (mapv resolved [:subject-type :subject-id :relation-id :resource-type :resource-id]) + :identity-guards [(:subject-identity-guard resolved) + (:resource-identity-guard resolved)]})) + (defn- stored-halves [db resolved] [(vec (ddb/relationship-identity-datoms @@ -302,6 +312,16 @@ (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)))))) +(defn direct-edge + "Stored compact edge or nil, prior to request qualification." + [db subject-type subject-id relation-id resource-type resource-id] + (some-> (first (endpoint-pair/checked-datoms + (ddb/relationship-identity-datoms + db subject-id relationship-storage/forward-attribute + (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)) + true)) + edge/from-datom)) + (defn- reverse-match? [db resource-type resource-id relation-id subject-type subject-id] (boolean @@ -377,7 +397,8 @@ ;; (backend-unification 9.1). (when-not relationship-filters/*validated-request?* (relationship-filters/validate! filters)) - (let [subject-id' (when (contains? filters :subject/id) + (let [include-qualifier? (true? (:include-qualifier? window-options)) + subject-id' (when (contains? filters :subject/id) (internal-id db (:subject/id filters))) resource-id' (when (contains? filters :resource/id) (internal-id db (:resource/id filters))) @@ -387,15 +408,17 @@ (if (or (and (contains? filters :subject/id) (nil? subject-id')) (and (contains? filters :resource/id) (nil? resource-id'))) {:data [] :cursor nil} - (letfn [(relationship-row [spec subject-id resource-id] + (letfn [(relationship-row [spec subject-id resource-id qualifier-id] {:spec-idx (:idx spec) :subject-id subject-id :resource-id resource-id :relationship - (eacl/->Relationship - (spice-object (:subject-type spec) subject-id) - (:relation-name spec) - (spice-object (:resource-type spec) resource-id))}) + (inspection/row + (eacl/->Relationship + (spice-object (:subject-type spec) subject-id) + (:relation-name spec) + (spice-object (:resource-type spec) resource-id)) + (:relation-id spec) qualifier-id)}) (normalized-cursor [cursor] (when cursor (cond-> @@ -415,19 +438,17 @@ (exact-match-row [spec cursor direction] (let [row (when (and (:subject-id spec) (:resource-id spec)) - (when - (direct-match? - db - (:subject-type spec) - (:subject-id spec) - (:relation-id spec) - (:resource-type spec) - (:resource-id spec)) - (relationship-row - spec (:subject-id spec) (:resource-id spec))))] + (when-let [compact + (if include-qualifier? + (direct-edge db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (when (direct-match? db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (:resource-id spec)))] + (relationship-row spec (:subject-id spec) (:resource-id spec) + (edge/qualifier-id compact))))] (if row - (drop-until-beyond-cursor - spec cursor direction [row]) + (drop-until-beyond-cursor spec cursor direction [row]) []))) (scan-forward-anchored [spec cursor direction] (if (:resource-id spec) @@ -441,11 +462,11 @@ (:resource-type spec)] (or (:resource-id cursor) (:resource cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [v]}] (relationship-row - spec (:subject-id spec) (nth v 3)))) + spec (:subject-id spec) (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-reverse-anchored [spec cursor direction] @@ -460,11 +481,11 @@ (:subject-type spec)] (or (:subject-id cursor) (:subject cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [v]}] (relationship-row - spec (nth v 3) (:resource-id spec)))) + spec (nth v 3) (:resource-id spec) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-forward-partial [spec cursor direction] @@ -476,10 +497,10 @@ (:resource-type spec)] (or (:resource-id cursor) (:resource cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [e v]}] - (relationship-row spec e (nth v 3)))) + (relationship-row spec e (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))) (scan-reverse-partial [spec cursor direction] @@ -491,10 +512,10 @@ (:subject-type spec)] (or (:subject-id cursor) (:subject cursor)) - direction) + direction include-qualifier?) (map (fn [{:keys [e v]}] - (relationship-row spec (nth v 3) e))) + (relationship-row spec (nth v 3) e (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))) (scan-spec @@ -516,7 +537,7 @@ (let [scan-specs (relationship-engine/plan-scans (all-relation-defs db) filters')] - (if window-options + (if (:accept? window-options) (relationship-engine/execute-filtered-window scan-specs filters' decision-kernel scan-spec window-options) (if-not (or (contains? filters' :limit) diff --git a/modules/eacl-datascript/src/eacl/datascript/migrations/v7_to_v9.cljc b/modules/eacl-datascript/src/eacl/datascript/migrations/v7_to_v9.cljc index 89f8e00e..8a28bcb1 100644 --- a/modules/eacl-datascript/src/eacl/datascript/migrations/v7_to_v9.cljc +++ b/modules/eacl-datascript/src/eacl/datascript/migrations/v7_to_v9.cljc @@ -15,7 +15,7 @@ :let [present (get actual attr)] :when (and present (not= present expected))] (upgrade/fail! :incompatible-schema {:attribute attr})) - (let [next-schema (merge actual admission/metadata-schema target)] + (let [next-schema (merge schema/datascript-schema actual admission/metadata-schema target)] (when-not (= actual next-schema) (d/reset-schema! conn next-schema))))) (defn- commit! [conn before operations] diff --git a/modules/eacl-datascript/src/eacl/datascript/qualifiers.cljc b/modules/eacl-datascript/src/eacl/datascript/qualifiers.cljc index fecd3456..8faef305 100644 --- a/modules/eacl-datascript/src/eacl/datascript/qualifiers.cljc +++ b/modules/eacl-datascript/src/eacl/datascript/qualifiers.cljc @@ -1,18 +1,15 @@ (ns eacl.datascript.qualifiers "Certified prepared-reference publication for explicitly non-serving data." - (:require [datascript.core :as ds] + (:require [eacl.datascript.db :as native-db] + [datascript.core :as ds] + [eacl.datascript.backend :as backend] [eacl.datascript.schema :as schema] - [eacl.relationships.staged :as staged])) + [eacl.relationships.staged :as staged] + [eacl.schema.qualification-admission :as admission])) -(defn- facts [database eid] - (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) (ds/datoms database :eavt eid))) +(defn- facts [database eid] (native-db/entity-facts database eid)) -(defn- entity [database eid] - (let [rows (facts database eid)] - (when (seq rows) - (reduce (fn [result [a v]] - (if (= :eacl.relation/caveats a) (update result a (fnil conj #{}) v) (assoc result a v))) - {:db/id eid} rows)))) +(defn- entity [database eid] (native-db/entity-data database eid)) (defn- assert-entity [database eid expected] (when-not (= expected (facts database eid)) (staged/error! :qualifier-changed-at-commit)) @@ -28,23 +25,27 @@ (vector? v) (= 5 (count v)) (= prefix (subvec v 0 4))))) (ds/seek-datoms database :eavt owner attribute (conj prefix nil))))) -(defn- fence [database relation-id reference-added?] +(defn- schema-fence [database reference-added?] (let [schema-id (ds/entid database [:eacl/id "schema-string"]) expected (:eacl/schema-write-fence (entity database schema-id))] (when-not expected (staged/error! :schema-unprepared)) (cond-> [[:db.fn/cas schema-id :eacl/schema-write-fence expected expected]] - reference-added? (conj [:db/add schema-id :eacl/schema-write-fence :db/current-tx]) - relation-id - (into [[:db.fn/cas relation-id :eacl/relation-version - (:eacl/relation-version (entity database relation-id)) - (:eacl/relation-version (entity database relation-id))] - [:db/add relation-id :eacl/relation-version :db/current-tx]])))) + reference-added? (conj [:db/add schema-id :eacl/schema-write-fence :db/current-tx])))) + +(defn- relation-fence [database relation-id] + (when relation-id + (let [expected (:eacl/relation-version (entity database relation-id))] + [[:db.fn/cas relation-id :eacl/relation-version expected expected] + [:db/add relation-id :eacl/relation-version :db/current-tx]]))) + +(defn- fence [database relation-id reference-added?] + (into (schema-fence database reference-added?) (relation-fence database relation-id))) (defn read-api "Read-only native inputs; constructing this map never prepares or writes a store." [] {:backend :datascript :entity entity :facts facts :rows identity-rows - :source (fn [database] (get (ds/entity database [:eacl/id "datascript-metadata"]) :eacl.datascript/source-id)) :generation schema/current-schema-generation + :source backend/database-source-scope :generation schema/current-schema-generation :all-rows (fn [database attribute] (ds/datoms database :aevt attribute)) :relation-version-attribute :eacl/relation-version :revision :max-tx @@ -56,11 +57,30 @@ :qualifier-cache-scope :assertion-version :qualifier-version (fn [database eid] (some-> (ds/datoms database :eavt eid :eacl.relationship-qualifier/format-version) first :tx))}) +(defn planner-api + "Pure native reads and transaction-data construction, safe for snapshots." + [] + (merge (read-api) + {:strategy :prepared :fence fence + :schema-fence schema-fence :relation-fence relation-fence + :assert-entity (fn [eid expected] [:db.fn/call assert-entity eid expected]) + :tempid #(str "eacl-qualifier-" (random-uuid))})) + +(defn plan + "Builds qualified transaction data from one immutable basis without writing." + ([database entries app-datoms] + (plan database entries app-datoms (backend/database-source-scope database))) + ([database entries app-datoms source-scope] + (let [native (assoc (planner-api) :source #(or (backend/database-source-scope %) source-scope))] + (staged/plan-batch (staged/planner native database) database entries app-datoms)))) + (defn writer [conn] (schema/prepare-cache-coherence! conn) (staged/native-writer - (merge (read-api) - {:strategy :prepared :snapshot #(ds/db conn) - :fence fence :assert-entity (fn [eid expected] [:db.fn/call assert-entity eid expected]) - :tempid #(str "eacl-qualifier-" (random-uuid)) - :transact! #(ds/transact! conn %)}))) + (merge (planner-api) + (let [scope {:source-id {:connection-id (backend/connection-source-id conn)} :branch nil}] + {:source #(or (backend/database-source-scope %) scope)}) + {:snapshot #(ds/db conn) :transact! #(ds/transact! conn %)}))) + +(defn publication-capability [database] + (admission/publication-descriptor :prepared)) diff --git a/modules/eacl-datascript/src/eacl/datascript/schema.cljc b/modules/eacl-datascript/src/eacl/datascript/schema.cljc index a46ecb70..67ca4ff5 100644 --- a/modules/eacl-datascript/src/eacl/datascript/schema.cljc +++ b/modules/eacl-datascript/src/eacl/datascript/schema.cljc @@ -9,6 +9,7 @@ [eacl.schema.expression-policy :as expression-policy] [eacl.schema.expression-resolver :as expression-resolver] [eacl.schema.model :as model] + [eacl.schema.relation-allowance :as relation-allowance] [eacl.schema.replacement-plan :as replacement-plan])) (def datascript-schema @@ -66,14 +67,17 @@ conn))) (defn read-relations + "Canonical Relation definitions, including named Caveat alternatives." [db] - (ds/q '[:find [(pull ?relation [:eacl/id - :eacl.relation/subject-type - :eacl.relation/resource-type - :eacl.relation/relation-name]) ...] - :where - [?relation :eacl.relation/relation-name]] - db)) + (let [pattern (cond-> [:eacl/id :eacl.relation/subject-type + :eacl.relation/resource-type :eacl.relation/relation-name] + true + (into [:eacl.relation/allows-unqualified? + {:eacl.relation/caveats [:eacl.caveat/name]}]))] + (mapv relation-allowance/canonicalize + (ds/q '[:find [(pull ?relation pattern) ...] + :in $ pattern + :where [?relation :eacl.relation/relation-name]] db pattern)))) (defn read-permissions [db] @@ -102,13 +106,18 @@ :where [?q :eacl.relationship-qualifier/caveat ?c]] db eid) [])) -(defn read-schema - [db & [_format]] +(defn read-authorization-schema + "Reads permission structure without compiling undemanded Caveat programs." + [db] (let [permissions (read-permissions db)] (expression-persistence/validate-entities permissions) - (let [caveats (read-caveats db)] - (cond-> {:relations (read-relations db) :permissions permissions} - (seq caveats) (assoc :caveats caveats))))) + {:relations (read-relations db) :permissions permissions})) + +(defn read-schema + [db & [_format]] + (let [schema (read-authorization-schema db) + caveats (read-caveats db)] + (cond-> schema (seq caveats) (assoc :caveats caveats)))) (defn prepare-cache-coherence! "Initializes missing physical schema/relation generations and the schema @@ -282,16 +291,22 @@ throwable)) (throw throwable))))) +(defn- stored-relation-caveats [db relation] + (relation-allowance/stored-caveats + {:entid #(ds/entid db %) :entity #(ddb/entity-data db %) + :rows #(ddb/relationship-identity-datoms db %1 %2 %3) + :scan #(ddb/avet-endpoint-prefix db %1 %2 nil :asc true)} relation)) + (defn plan-schema-replacement "Pure schema replacement planner shared by committed and speculative paths." [db schema-string - {:keys [allow-empty-schema? expression-limits orphan-policy] + {:keys [allow-empty-schema? expression-limits orphan-policy allow-caveats?] :or {orphan-policy :error}}] (let [expression-limits (expression-policy/normalize-client-limits expression-limits) new-schema-map (expression-persistence/candidate-schema - (expression-resolver/validate-schema schema-string expression-limits)) + (expression-resolver/validate-schema schema-string expression-limits {:allow-caveats? allow-caveats?})) existing-schema (binding [expression-persistence/*expression-limits* expression-limits] (read-schema db)) @@ -310,6 +325,7 @@ :existing {:relations (count (:relations existing-schema)) :permissions (count (:permissions existing-schema))}}))) deltas (compare-schema existing-schema new-schema-map) + _ (relation-allowance/validate-existing! (:relations deltas) #(stored-relation-caveats db %)) semantic (replacement-plan/plan {:deltas deltas @@ -319,7 +335,7 @@ #(relationship-present-for-relation? db %)}) {:keys [relations permissions caveats]} deltas _ (caveat-definition/validate-replacements! caveats #(caveat-references db %)) - relation-retractions (:retractions relations) + relation-retractions (relation-allowance/entity-deletions relations) permission-retractions (expression-persistence/entity-deletions permissions) relation-additions @@ -345,13 +361,14 @@ :relation-id (:eacl/id relation)}))) [:db.fn/cas relation-eid :eacl/relation-version relation-generation relation-generation])) - relation-retractions) + (:retractions relations)) tx-data (vec (concat + (:additions caveats) + (relation-allowance/attribute-retractions relations) relation-additions (:additions permissions) - (:additions caveats) (for [caveat (caveat-definition/entity-deletions caveats) :let [eid (ds/entid db [:eacl.caveat/name (:eacl.caveat/name caveat)])] :when eid] diff --git a/modules/eacl-datascript/test/eacl/datascript/batch_test.cljc b/modules/eacl-datascript/test/eacl/datascript/batch_test.cljc index 68a0aa69..a77d4d04 100644 --- a/modules/eacl-datascript/test/eacl/datascript/batch_test.cljc +++ b/modules/eacl-datascript/test/eacl/datascript/batch_test.cljc @@ -8,6 +8,7 @@ [eacl.backend.v8 :as backend] [eacl.authorization.filters :as authorization-filters] [eacl.cache :as cache] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl] [eacl.datascript.core :as datascript] [eacl.execution :as execution] @@ -781,7 +782,7 @@ client (scan-query dense {:first 2 :aggregate-limits {:candidate-window 10}}))) - (is (= 1 (:proof-frame @backend-stats 0))))) + (is (= (if orchestration/*qualified-authorization-enabled?* 0 1) (:proof-frame @backend-stats 0))))) (testing "candidate-stream mutation invalidates continuation" (let [{:keys [client dense marker documents]} (scan-fixture) diff --git a/modules/eacl-datascript/test/eacl/datascript/cache_model_test.cljc b/modules/eacl-datascript/test/eacl/datascript/cache_model_test.cljc index d634a930..58b0691a 100644 --- a/modules/eacl-datascript/test/eacl/datascript/cache_model_test.cljc +++ b/modules/eacl-datascript/test/eacl/datascript/cache_model_test.cljc @@ -44,7 +44,10 @@ (defn- without-cache-provenance [value] (if (map? value) - (dissoc value :cached? :cache-basis) + ;; Separate live requests may mint different time-scoped cursor bytes. + ;; Compare authorization and page bounds; cursor validity has its own suite. + (cond-> (dissoc value :cached? :cache-basis) + (:page-info value) (update :page-info dissoc :start-cursor :end-cursor)) value)) (defn- assert-same-answers! diff --git a/modules/eacl-datascript/test/eacl/datascript/caveat_context_test.cljc b/modules/eacl-datascript/test/eacl/datascript/caveat_context_test.cljc new file mode 100644 index 00000000..3d624066 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/caveat_context_test.cljc @@ -0,0 +1,78 @@ +(ns eacl.datascript.caveat-context-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.context :as caveat-context] + [eacl.authorization.context-test :as context-test] + [eacl.backend.source :as source] + [eacl.core :as eacl] + [eacl.datascript.core :as datascript] + [eacl.request.context :as context])) + +(defn fixture [] + (let [conn (datascript/create-conn) + client (datascript/make-client conn {:clock (constantly 99)}) + user (eacl/spice-object :user "user") + doc (eacl/spice-object :doc "doc")] + (eacl/write-schema! client "definition user {} + definition doc { + relation reader: user + permission view = reader + }") + (ds/transact! conn [{:eacl/id "user"} {:eacl/id "doc"}]) + (eacl/create-relationship! client (eacl/->Relationship user :reader doc)) + {:client client :check {:subject user :permission :view :resource doc} + :forward {:subject user :resource/type :doc :permission :view :first 10} + :reverse {:resource doc :subject/type :user :permission :view :first 10}})) + +(defn operations [{:keys [check forward reverse]} target input] + (let [with-context #(assoc % :caveat-context input)] + [#(eacl/check-permission target (with-context check)) + #(eacl/check-permissions target (with-context {:checks [check check check]})) + #(eacl/lookup-resources target (with-context forward)) + #(eacl/lookup-subjects target (with-context reverse)) + #(eacl/count-resources target (with-context (dissoc forward :first))) + #(eacl/count-subjects target (with-context (dissoc reverse :first))) + #(eacl/read-relationships target (with-context {:subject/type :user :first 10}))])) + +(deftest public-operations-prepare-one-complete-context + (let [{:keys [client] :as data} (fixture) + prepare caveat-context/prepare + with-context context/call-with-context + prepared (atom []) + seen (atom [])] + (with-redefs [caveat-context/prepare + (fn [input] + (let [result (prepare input)] + (swap! prepared conj result) + result)) + context/call-with-context + (fn [ctx f] + (swap! seen conj (:caveat-context (context/active-state ctx))) + (with-context ctx f))] + (let [snapshot (eacl/snapshot client)] + (try + (doseq [target [client snapshot] + input [{"flag" true "unused" 3} {"flag" false} {}] + operation (operations data target input)] + (reset! prepared []) + (reset! seen []) + (operation) + (is (<= (count @prepared) 1)) + (is (= 1 (count @seen))) + (is (identical? (or (first @prepared) (prepare {})) (first @seen))) + (is (= input (caveat-context/value (first @seen))))) + (finally (eacl/release! snapshot))))))) + +(deftest invalid-context-fails-before-selection-even-on-warm-or-empty-requests + (let [{:keys [client check] :as data} (fixture)] + (is (:allowed? (eacl/check-permission client check))) + (with-redefs [source/acquire! (fn [& _] (throw (ex-info "Unexpected snapshot selection" {})))] + (doseq [input context-test/invalid-contexts + operation (conj (operations data client input) + #(eacl/check-permissions client {:checks [] :caveat-context input}))] + (is (= :eacl.caveat/invalid + (:type (context-test/error-data operation)))))) + (is (= :per-demand-control + (:reason (context-test/error-data + #(eacl/check-permissions client + {:checks [(assoc check :caveat-context {"flag" true})]}))))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/caveat_schema_test.cljc b/modules/eacl-datascript/test/eacl/datascript/caveat_schema_test.cljc index e264abda..41cd5dcd 100644 --- a/modules/eacl-datascript/test/eacl/datascript/caveat_schema_test.cljc +++ b/modules/eacl-datascript/test/eacl/datascript/caveat_schema_test.cljc @@ -1,5 +1,6 @@ (ns eacl.datascript.caveat-schema-test (:require [eacl.datascript.core :as api] + [eacl.datascript.backend :as backend] [eacl.cache :as cache] [eacl.caveats.hot-path-contract :as hot-path] [#?(:clj clojure.test :cljs cljs.test) :refer [deftest]] @@ -7,6 +8,8 @@ [eacl.datascript.schema :as schema] [eacl.caveats.persistence-contract :as contract] [eacl.caveats.publication-contract :as publication] + [eacl.relationships.edge-contract :as edge-contract] + [eacl.datascript.impl :as scan-impl] [eacl.datascript.qualifiers :as qualifiers])) (defn interleave! [competitor outer] @@ -32,3 +35,12 @@ (publication/check-publication! {:write-schema! #(schema/write-schema! conn %) :writer #(qualifiers/writer conn) :entid ds/entid :strategy :prepared :interleave! interleave!}))) + +(deftest compact-qualified-scans + (let [conn (schema/create-conn {})] + (edge-contract/check! + {:write-schema! #(schema/write-schema! conn %) :writer #(qualifiers/writer conn) + :entid ds/entid :forward scan-impl/subject->resources + :reverse scan-impl/resource->subjects :direct scan-impl/direct-edge + :adapter #(backend/basis-adapter % {})}) +)) diff --git a/modules/eacl-datascript/test/eacl/datascript/cljs_test_runner.cljs b/modules/eacl-datascript/test/eacl/datascript/cljs_test_runner.cljs index 323baa94..6d0e675a 100644 --- a/modules/eacl-datascript/test/eacl/datascript/cljs_test_runner.cljs +++ b/modules/eacl-datascript/test/eacl/datascript/cljs_test_runner.cljs @@ -4,6 +4,15 @@ [eacl.backend.v8-test] [eacl.backend.direct-membership-test] [eacl.authorization.batch-test] + [eacl.authorization.evidence-test] + [eacl.authorization.evidence-index-test] + [eacl.authorization.data-test] + [eacl.authorization.context-test] + [eacl.authorization.result-test] + [eacl.authorization.qualification-test] + [eacl.authorization.qualifier-cache-test] + [eacl.authorization.temporal-test] + [eacl.datascript.qualifier-cache-test] [eacl.cache.derived-schema-test] [eacl.cache.key-test] [eacl.cache.standard-lru-test] @@ -16,11 +25,32 @@ [eacl.caveats.plan-test] [eacl.caveats.partial-test] [eacl.relationships.qualifier-test] + [eacl.relationships.mutations-test] [eacl.core-test] [eacl.causal-model-test] [eacl.consistency-test] [eacl.engine.relationships-test] [eacl.engine.scan-cache-test] + [eacl.engine.stable-route-evidence-test] + [eacl.engine.stable-reducer-evidence-test] + [eacl.engine.stable-page-evidence-test] + [eacl.engine.stable-route-native-evidence-test] + [eacl.engine.least-path-evidence-test] + [eacl.operator.seekable-evidence-test] + [eacl.operator.lookup-evidence-test] + [eacl.operator.arrow-evidence-test] + [eacl.datascript.evaluation-clock-test] + [eacl.datascript.caveat-context-test] + [eacl.datascript.qualified-check-test] + [eacl.datascript.qualified-lookup-test] + [eacl.datascript.qualified-cursor-test] + [eacl.datascript.qualified-cache-trace-test] + [eacl.datascript.qualified-write-test] + [eacl.caveats.schema-admission-test] + [eacl.schema.relation-allowance-test] + [eacl.datascript.qualified-schema-test] + [eacl.authorization.inspection-test] + [eacl.datascript.qualified-inspection-test] [eacl.client.lookahead-test] [eacl.client.range-reuse-test] [eacl.datascript.scan-cache-integration-test] @@ -41,6 +71,7 @@ [eacl.operator.feature-gate-test] [eacl.operator.recursive-test] [eacl.relationships.endpoint-pair-test] + [eacl.relationships.edge-test] [eacl.relationships.upgrade-test] [eacl.datascript.qualifier-storage-test] [eacl.datascript.migrations.v7-to-v9-test] @@ -90,6 +121,15 @@ (t/run-tests 'eacl.backend.v8-test 'eacl.backend.direct-membership-test 'eacl.authorization.batch-test + 'eacl.authorization.evidence-test + 'eacl.authorization.evidence-index-test + 'eacl.authorization.data-test + 'eacl.authorization.context-test + 'eacl.authorization.result-test + 'eacl.authorization.qualification-test + 'eacl.authorization.qualifier-cache-test + 'eacl.authorization.temporal-test + 'eacl.datascript.qualifier-cache-test 'eacl.cache.derived-schema-test 'eacl.cache.key-test 'eacl.cache.standard-lru-test @@ -99,6 +139,26 @@ 'eacl.consistency-test 'eacl.engine.relationships-test 'eacl.engine.scan-cache-test + 'eacl.engine.stable-route-evidence-test + 'eacl.engine.stable-reducer-evidence-test + 'eacl.engine.stable-page-evidence-test + 'eacl.engine.stable-route-native-evidence-test + 'eacl.engine.least-path-evidence-test + 'eacl.operator.seekable-evidence-test + 'eacl.operator.lookup-evidence-test + 'eacl.operator.arrow-evidence-test + 'eacl.datascript.evaluation-clock-test + 'eacl.datascript.caveat-context-test + 'eacl.datascript.qualified-check-test + 'eacl.datascript.qualified-lookup-test + 'eacl.datascript.qualified-cursor-test + 'eacl.datascript.qualified-cache-trace-test + 'eacl.datascript.qualified-write-test + 'eacl.caveats.schema-admission-test + 'eacl.schema.relation-allowance-test + 'eacl.datascript.qualified-schema-test + 'eacl.authorization.inspection-test + 'eacl.datascript.qualified-inspection-test 'eacl.client.lookahead-test 'eacl.client.range-reuse-test 'eacl.datascript.scan-cache-integration-test @@ -119,6 +179,7 @@ 'eacl.operator.feature-gate-test 'eacl.operator.recursive-test 'eacl.relationships.endpoint-pair-test + 'eacl.relationships.edge-test 'eacl.relationships.upgrade-test 'eacl.datascript.qualifier-storage-test 'eacl.datascript.migrations.v7-to-v9-test @@ -134,6 +195,7 @@ 'eacl.caveats.plan-test 'eacl.caveats.partial-test 'eacl.relationships.qualifier-test + 'eacl.relationships.mutations-test 'eacl.schema.expression-test 'eacl.schema.expression-limits-test 'eacl.schema.expression-resolver-test diff --git a/modules/eacl-datascript/test/eacl/datascript/consistency_v3_test.cljc b/modules/eacl-datascript/test/eacl/datascript/consistency_v3_test.cljc index 3aee57a5..ac991c19 100644 --- a/modules/eacl-datascript/test/eacl/datascript/consistency_v3_test.cljc +++ b/modules/eacl-datascript/test/eacl/datascript/consistency_v3_test.cljc @@ -5,6 +5,7 @@ [eacl.backend.v8 :as backend] [eacl.cache :as cache] [eacl.contract-support :as contract] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl] [eacl.datascript.backend :as datascript-backend] [eacl.datascript.core :as datascript] @@ -166,12 +167,12 @@ (finally (eacl/release! snapshot)))) (let [stats (datascript/cache-stats client)] - (is (true? (:managed-lifting-disabled? stats))) - (is (= 1 (:proof-contract-violations stats))) - (is (= {:relation-generation-above-revision 1} + (is (= (not orchestration/*qualified-authorization-enabled?*) (:managed-lifting-disabled? stats))) + (is (= (if orchestration/*qualified-authorization-enabled?* 0 1) (:proof-contract-violations stats))) + (is (= (if orchestration/*qualified-authorization-enabled?* {} {:relation-generation-above-revision 1}) (:proof-contract-violation-reasons stats)))) - (is (= 1 (count @reports))) - (is (= :relation-generation-above-revision + (is (= (if orchestration/*qualified-authorization-enabled?* 0 1) (count @reports))) + (is (= (when-not orchestration/*qualified-authorization-enabled?* :relation-generation-above-revision) (:reason (first @reports))))))) (deftest explicit-cache-expiry-installs-a-fresh-lifecycle-test @@ -295,7 +296,9 @@ (is (false? (:cached? original-page-2))) (ds/transact! conn [{:eacl/id "unrelated-completed-answer-write"}]) (let [recovered-page-2 - (eacl/lookup-resources authorization original-page-2-query) + (if orchestration/*qualified-authorization-enabled?* + (error-data #(eacl/lookup-resources authorization original-page-2-query)) + (eacl/lookup-resources authorization original-page-2-query)) fresh-page-1 (eacl/lookup-resources authorization query) fresh-page-2-query @@ -306,16 +309,18 @@ (eacl/lookup-resources authorization fresh-page-2-query)] (testing "an unrelated write leaves the dependency proof equal: the continuation is reused without recovery" - (is (= [(second documents)] (:data recovered-page-2))) - (is (nil? (get-in recovered-page-2 - [:page-info :cursor-recovery]))) - (is (true? (:cached? recovered-page-2)))) + (if orchestration/*qualified-authorization-enabled?* + (is (= :eacl.pagination/stale-cursor (:type recovered-page-2))) + (do + (is (= [(second documents)] (:data recovered-page-2))) + (is (nil? (get-in recovered-page-2 [:page-info :cursor-recovery]))) + (is (true? (:cached? recovered-page-2)))))) (testing "a newly signed cursor for the same boundary also reuses it" (is (not= (:after original-page-2-query) (:after fresh-page-2-query))) (is (= [(second documents)] (:data fresh-page-2))) - (is (true? (:cached? fresh-page-2))))))) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? fresh-page-2))))))) (deftest repeated-relationship-page-uses-completed-answer-cache-test (let [conn (datascript/create-conn) @@ -554,9 +559,9 @@ (is (false? (:cached? first-page))) (is (true? (:cached? exact-hit))) (ds/transact! conn [{:eacl/id "unrelated"}]) - (is (true? - (:cached? - (eacl/lookup-resources authorization query)))) + (is (= (not orchestration/*qualified-authorization-enabled?*) + (:cached? + (eacl/lookup-resources authorization query)))) (eacl/delete-relationship! authorization relationship) (is (= :eacl.consistency/exact-snapshot-unavailable (:type diff --git a/modules/eacl-datascript/test/eacl/datascript/contract_test.cljc b/modules/eacl-datascript/test/eacl/datascript/contract_test.cljc index dad74f07..45677e7a 100644 --- a/modules/eacl-datascript/test/eacl/datascript/contract_test.cljc +++ b/modules/eacl-datascript/test/eacl/datascript/contract_test.cljc @@ -6,6 +6,7 @@ [eacl.cache :as cache] [eacl.cache.standard-lru :as lru] [eacl.causal-token :as causal-token] + [eacl.client.orchestration :as orchestration] [eacl.contract-support :as contract] [eacl.core :as eacl] [eacl.cursor :as cursor] @@ -59,7 +60,7 @@ missing-user (contract/->user "missing") missing-server (contract/->server "missing") reads (atom 0) - original-read-schema schema/read-schema + original-read-schema schema/read-authorization-schema exercise! (fn [] (is (= [] @@ -84,7 +85,7 @@ :cache? false}))))] (eacl/write-schema! client contract/smoke-schema) #?(:clj - (with-redefs [schema/read-schema + (with-redefs [schema/read-authorization-schema (fn [db & format] (swap! reads inc) (apply original-read-schema db format))] @@ -479,13 +480,16 @@ (is (false? (:changed? (datascript/prepare-cache-coherence! conn)))) (eacl/write-schema! client-a custom-codec-cache-schema) - (is (true? (eacl/can? client-a user :view document))) - (is (true? (eacl/can? client-b user :view document)))) + (is (= (not orchestration/*qualified-authorization-enabled?*) + (eacl/can? client-a user :view document))) + (is (= (not orchestration/*qualified-authorization-enabled?*) + (eacl/can? client-b user :view document)))) (testing "every process-local client must rotate after quiescence" (datascript/expire-cache! client-a) (is (false? (eacl/can? client-a user :view document))) - (is (true? (eacl/can? client-b user :view document))) + (is (= (not orchestration/*qualified-authorization-enabled?*) + (eacl/can? client-b user :view document))) (datascript/expire-cache! client-b) (is (false? (eacl/can? client-b user :view document)))) @@ -617,7 +621,9 @@ second-page (eacl/lookup-resources stable-client query) after (datascript/cache-stats stable-client)] (is (= (:data first-page) (:data second-page))) - (is (= (inc (:managed-hits before)) (:managed-hits after))) + (is (= (cond-> (:managed-hits before) + (not orchestration/*qualified-authorization-enabled?*) inc) + (:managed-hits after))) (is (seq @stable-observations)) (is (every? #{selected-basis} @stable-observations) "managed semantic results are externalized from the selected DB")))) @@ -1639,14 +1645,14 @@ :consistency (consistency/at-least-as-fresh lower-floor)} first-page (eacl/lookup-resources client first-query) raw-cursor (get-in first-page [:page-info :end-cursor]) + lower-request (assoc first-query :after raw-cursor :first 2) + lower-page (eacl/lookup-resources client lower-request) higher-floor (:zed/token (eacl/create-relationship! client (eacl/->Relationship unrelated-user :owner unrelated-document))) - lower-request (assoc first-query :after raw-cursor :first 2) - lower-page (eacl/lookup-resources client lower-request) before-high (datascript/cache-stats client) error (try diff --git a/modules/eacl-datascript/test/eacl/datascript/evaluation_clock_test.cljc b/modules/eacl-datascript/test/eacl/datascript/evaluation_clock_test.cljc new file mode 100644 index 00000000..9fb5a876 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/evaluation_clock_test.cljc @@ -0,0 +1,74 @@ +(ns eacl.datascript.evaluation-clock-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.cache :as cache] + [eacl.core :as eacl] + [eacl.datascript.core :as datascript] + [eacl.request.context :as context])) + +(deftest client-samples-once-and-snapshots-pin-time + (let [conn (datascript/create-conn) + now (atom 99) samples (atom []) times (atom []) + client (datascript/make-client conn {:cache cache/no-cache + :clock #(do (swap! samples conj @now) @now)}) + user (eacl/spice-object :user "user") doc (eacl/spice-object :doc "doc") + check {:subject user :permission :view :resource doc} + with-context context/call-with-context] + (eacl/write-schema! client "definition user {} + definition doc { + relation reader: user + permission view = reader + }") + (ds/transact! conn [{:eacl/id "user"} {:eacl/id "doc"}]) + (eacl/create-relationship! client (eacl/->Relationship user :reader doc)) + (reset! samples []) + (with-redefs [context/call-with-context + (fn [ctx f] + (swap! times conj (:evaluation-time-ms (context/runtime ctx))) + (reset! now 100) + (with-context ctx f))] + (is (:allowed? (eacl/check-permission client check))) + (is (= [99] @samples)) + (is (= [99] @times)) + (reset! samples []) (reset! times []) + (is (= 3 (count (eacl/check-permissions client {:checks [check check check]})))) + (is (= [100] @samples)) + (is (= [100] @times)) + (reset! samples []) (reset! times []) (reset! now 90) + (is (:allowed? (eacl/check-permission client check))) + (is (= [90] @samples)) + (is (= [100] @times)) + (doseq [read [#(eacl/check-permission client (assoc check :evaluation-time-ms 0)) + #(eacl/lookup-resources client {:subject user :resource/type :doc :permission :view :first 10}) + #(eacl/lookup-subjects client {:resource doc :subject/type :user :permission :view :first 10}) + #(eacl/count-resources client {:subject user :resource/type :doc :permission :view}) + #(eacl/count-subjects client {:resource doc :subject/type :user :permission :view}) + #(eacl/read-relationships client {:subject/type :user :first 10})]] + (reset! now 105) (reset! samples []) (reset! times []) + (read) + (is (= [105] @samples)) + (is (= [105] @times))) + (reset! now 110) (reset! samples []) (reset! times []) + (let [snapshot (eacl/snapshot client)] + (try + (is (= [110] @samples)) + (reset! now 200) (reset! samples []) + (is (:allowed? (eacl/check-permission snapshot check))) + (is (:allowed? (eacl/check-permission snapshot check))) + (is (= [] @samples)) + (is (= [110 110] @times)) + (let [prospective (eacl/with snapshot [])] + (try + (reset! times []) + (is (:allowed? (eacl/check-permission prospective check))) + (is (= [110] @times)) + (is (= [] @samples)) + (finally (eacl/release! prospective)))) + (finally (eacl/release! snapshot))))))) + +(deftest evaluation-clock-is-a-trusted-client-dependency + (let [conn (datascript/create-conn)] + (doseq [clock [nil 0 {} "clock"]] + (is (= :eacl/invalid-config + (try (datascript/make-client conn {:clock clock}) nil + (catch #?(:clj Throwable :cljs :default) error (:type (ex-data error))))))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/impl_test.cljc b/modules/eacl-datascript/test/eacl/datascript/impl_test.cljc index 594a34f1..b2633c1b 100644 --- a/modules/eacl-datascript/test/eacl/datascript/impl_test.cljc +++ b/modules/eacl-datascript/test/eacl/datascript/impl_test.cljc @@ -361,7 +361,7 @@ ;; The public three-arity delegates to the new window-aware ;; four-arity. Count the public engine invocation, not that local ;; arity dispatch, as a second backend query. - (when (= 3 (count args)) + (when (= (if (:include-qualifier? (nth args 3 nil)) 4 3) (count args)) (swap! internal-queries conj (second args))) (apply read-relationships args)))) page (eacl/read-relationships counting-client diff --git a/modules/eacl-datascript/test/eacl/datascript/keyset_recursion_test.clj b/modules/eacl-datascript/test/eacl/datascript/keyset_recursion_test.clj index 65157e39..d35d99b9 100644 --- a/modules/eacl-datascript/test/eacl/datascript/keyset_recursion_test.clj +++ b/modules/eacl-datascript/test/eacl/datascript/keyset_recursion_test.clj @@ -10,6 +10,7 @@ [datascript.core :as ds] [eacl.bench.recursive-fixture :as rf] [eacl.core :as eacl] + [eacl.client.orchestration :as orchestration] [eacl.datascript.core :as dsc])) (def ^:private config {:shape :star :accounts 60}) @@ -80,12 +81,12 @@ client [(eacl/->Relationship rf/stranger :reader (rf/object :account (rf/account-id 3)))]) - page-2 (lookup client {:after (end-cursor page-1)})] - (is (nil? (get-in page-2 [:page-info :cursor-recovery]))) - (is (empty? - (set/intersection - (set (page-ids page-1)) - (set (page-ids page-2))))))) + request {:after (end-cursor page-1)}] + (if orchestration/*qualified-authorization-enabled?* + (is (= :eacl.pagination/stale-cursor (:type (error-data #(lookup client request))))) + (let [page-2 (lookup client request)] + (is (nil? (get-in page-2 [:page-info :cursor-recovery]))) + (is (empty? (set/intersection (set (page-ids page-1)) (set (page-ids page-2))))))))) (deftest bare-last-requires-explicit-completion-and-preserves-logical-order-test (let [{:keys [client]} (seed!) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualified_cache_trace_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualified_cache_trace_test.cljc new file mode 100644 index 00000000..31b1e00c --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualified_cache_trace_test.cljc @@ -0,0 +1,50 @@ +(ns eacl.datascript.qualified-cache-trace-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.caveats.cache-trace-contract :as trace] + [datascript.core :as ds] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl] + [eacl.datascript.qualified-check-test :as checks] + [eacl.datascript.qualified-lookup-test :as lookups] + [eacl.relationships.qualifier :as qualifier] + [eacl.authorization.qualification-test :as fixtures] + [eacl.datascript.core :as api] + [eacl.datascript.qualifiers :as qualifiers])) + +(deftest qualified-cache-traces-match-uncached-authorization + (let [conn (api/create-conn) now (atom 99) + client (api/make-client conn {:clock #(deref now) :caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (trace/check! {:client client :writer #(qualifiers/writer conn) :now now :expire-cache! api/expire-cache!}))) + +(deftest unknown-writer-faults-and-restored-entity-identities-never-reuse-old-answers + (let [{:keys [conn client check]} (checks/fixture) + check (assoc check :permission :both :caveat-context {"flag" true}) + original (ds/db conn) + qid (ds/q '[:find ?e . :where [?e :eacl.relationship-qualifier/valid-until-ms 100]] original) + point #(dissoc (eacl/check-permission client (merge check %)) :cached? :cache-basis :evaluation) + operations (into [point] + (for [direction [:forward :reverse] policy [:definite :detailed] + kind [:count :lookup] + :let [query (lookups/count-query check direction policy {"flag" true})]] + (fn [options] + (let [query (merge query options {:evaluation :complete-denotation})] + (if (= :count kind) + (dissoc (lookups/count! client direction query) :cached? :cache-basis) + (trace/drain (if (= :forward direction) eacl/lookup-resources eacl/lookup-subjects) + client query))))))] + (binding [orchestration/*qualified-authorization-enabled?* true] + (is (= :no-permission (:permissionship (trace/compare-operation! :healthy point)))) + (doseq [mutate! [#(ds/transact! conn [[:db/add qid :unknown/field "invalid"]]) + #(ds/transact! conn [[:db/retractEntity qid]])]] + (mutate!) + (doseq [read operations] + (let [results (mapv #(trace/outcome (fn [] (read %))) + [{:cache? false} {} {} {:populate-cache? false}])] + (is (apply = results)) + (is (= :eacl.authorization/evaluation-failure (get-in (first results) [:fault :type]))))) + (is (false? (eacl/can? client check)))) + (ds/reset-conn! conn original) + (api/expire-cache! client "qualified-restored-eid-lifecycle") + (is (= :no-permission (:permissionship (trace/compare-operation! :restored point)))) + (ds/transact! conn [[:db/add qid qualifier/expiration-attribute 99]]) + (is (= :has-permission (:permissionship (trace/compare-operation! :unstamped-ban-expiry point))))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualified_check_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualified_check_test.cljc new file mode 100644 index 00000000..2106a1d9 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualified_check_test.cljc @@ -0,0 +1,165 @@ +(ns eacl.datascript.qualified-check-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.context-test :as errors] + [eacl.authorization.qualification-test :as qualification-fixtures] + [eacl.authorization.qualification :as qualification] + [eacl.client.orchestration :as orchestration] + [eacl.caveats.plan :as caveat-plan] + [eacl.core :as eacl] + [eacl.datascript.core :as datascript] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.relationships.staged :as staged])) + +(defn fixture [] + (let [conn (datascript/create-conn) + now (atom 99) + evaluator (qualification-fixtures/portable-evaluator (atom 0)) + client (datascript/make-client conn {:clock #(deref now) :caveat-evaluator evaluator}) + user (eacl/spice-object :user "user") + folder (eacl/spice-object :folder "folder")] + (eacl/write-schema! client + "caveat enabled(flag bool) { flag } + definition user {} + definition folder { + relation member: user + relation writer: user + relation banned: user + relation parent: folder + permission direct = member + permission either = member + writer + permission both = (member & writer) - banned + permission recursive = member + (parent->recursive & writer) + permission walk = member + parent->walk + permission via = parent->direct + permission delegated = direct + permission united = (member + writer) & writer + }") + (ds/transact! conn [{:eacl/id "user"} {:eacl/id "folder"}]) + (doseq [relation [:member :writer :banned]] + (eacl/create-relationship! client (eacl/->Relationship user relation folder))) + (eacl/create-relationship! client (eacl/->Relationship folder :parent folder)) + (let [db (ds/db conn) + eid #(ds/entid db [:eacl/id %]) + relation #(ds/entid db [:eacl.relation/resource-type+relation-name+subject-type + [:folder % :user]]) + caveat (ds/entid db [:eacl.caveat/name "enabled"]) + identity #(vector :user (eid "user") (relation %) :folder (eid "folder")) + writer (qualifiers/writer conn)] + (ds/transact! conn [{:db/id (relation :member) :eacl.relation/caveats [caveat] + :eacl.relation/allows-unqualified? true}]) + (staged/write! writer :replace (identity :member) {:caveat caveat :valid-until-ms 200}) + (staged/write! writer :replace (identity :banned) {:valid-until-ms 100}) + {:conn conn :client client :now now :writer writer :identity identity + :check {:subject user :resource folder :permission :direct}}))) + +(deftest public-point-routes-preserve-conditional-evidence-and-expiring-bans + (let [{:keys [client now check]} (fixture)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [time [99 100 200] + [context member] [[{} :conditional-permission] + [{"flag" true} :has-permission] + [{"flag" false} :no-permission]] + permission [:direct :either :both :recursive]] + (reset! now time) + (let [request (assoc check :permission permission :caveat-context context) + expected (case permission + :either :has-permission + :both (if (= time 99) :no-permission + (if (< time 200) member :no-permission)) + (if (< time 200) member :no-permission)) + cold (eacl/check-permission client request) + warm (eacl/check-permission client request) + uncached (eacl/check-permission client (assoc request :cache? false))] + (is (= expected (:permissionship cold))) + (is (= (= expected :has-permission) (eacl/can? client request))) + (is (= (dissoc cold :cached? :cache-basis) + (dissoc warm :cached? :cache-basis) + (dissoc uncached :cached? :cache-basis))) + (is (:cached? warm)) + (is (= (when (= expected :conditional-permission) ["flag"]) + (:missing-fields cold)))))))) + +(deftest expired-public-points-never-compile-undemanded-caveats + (let [{:keys [client now check]} (fixture)] + (reset! now 200) + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [caveat-plan/compile-plan (fn [& _] (throw (ex-info "Unexpected program compilation" {})))] + (doseq [permission [:direct :both :recursive]] + (is (= :no-permission + (:permissionship (eacl/check-permission client (assoc check :permission permission :cache? false)))))))))) + +(deftest public-batch-shares-qualifier-resolution-across-all-point-routes + (let [{:keys [client now check]} (fixture) + request-from-adapter qualification/request-from-adapter + reads (atom [])] + (reset! now 100) + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [qualification/request-from-adapter + (fn [adapter options] + (update (request-from-adapter adapter options) :lookup + (fn [lookup] + (fn [eid] + (swap! reads conj eid) + (lookup eid)))))] + (let [checks (mapv #(assoc check :permission %) [:direct :either :both :recursive]) + decisions (eacl/check-permissions client {:checks checks :cache? false})] + (is (= [:conditional-permission :has-permission :conditional-permission :conditional-permission] + (mapv :permissionship decisions))) + (is (= 5 (count @reads))) + (is (every? #(= 1 %) (vals (frequencies @reads))))))))) + +(deftest certified-point-reuse-does-not-alias-unused-context-or-unstamped-mutation + (let [{:keys [conn client now check]} (fixture) + request (assoc check :caveat-context {"flag" true "unused" 1})] + (binding [orchestration/*qualified-authorization-enabled?* true] + (is (:allowed? (eacl/check-permission client request))) + (is (:cached? (eacl/check-permission client request))) + (is (false? (:cached? (eacl/check-permission client (assoc-in request [:caveat-context "unused"] 2))))) + (reset! now 100) + (is (:cached? (eacl/check-permission client request))) + (let [snapshot (eacl/snapshot client)] + (try + (reset! now 200) + (is (eacl/can? snapshot request)) + (is (false? (eacl/can? client request))) + (finally (eacl/release! snapshot)))) + (let [qid (ds/q '[:find ?e . :where [?e :eacl.relationship-qualifier/valid-until-ms 200]] (ds/db conn))] + (ds/transact! conn [[:db/add qid :eacl.relationship-qualifier/valid-until-ms 300]]) + (is (eacl/can? client request)) + (ds/transact! conn [[:db/add qid :unknown/private "must not be disclosed"]]) + (is (= :eacl.authorization/evaluation-failure + (:type (errors/error-data #(eacl/check-permission client request))))) + (is (false? (eacl/can? client request))))))) + +(deftest temporal-point-cache-expires-denials-and-retains-permanent-witnesses + (let [{:keys [client now check]} (fixture) + request (assoc check :permission :both :caveat-context {"flag" true})] + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [pinned (eacl/snapshot client)] + (try + (is (false? (:allowed? (eacl/check-permission client request)))) + (is (:cached? (eacl/check-permission client request))) + (reset! now 100) + (let [after-ban (eacl/check-permission client request)] + (is (:allowed? after-ban)) + (is (false? (:cached? after-ban)))) + (reset! now 150) + (is (:cached? (eacl/check-permission client request))) + (is (false? (eacl/can? pinned request)) "explicit snapshot retains the active ban at time 99") + (is (:cached? (eacl/check-permission client request)) "older pinned publication leaves the later interval available") + (reset! now 200) + (let [after-grant (eacl/check-permission client request)] + (is (false? (:allowed? after-grant))) + (is (false? (:cached? after-grant)))) + (reset! now 201) + (is (:cached? (eacl/check-permission client request))) + (finally (eacl/release! pinned)))))) + (let [{:keys [client now check]} (fixture) + request (assoc check :permission :either :caveat-context {"flag" false})] + (binding [orchestration/*qualified-authorization-enabled?* true] + (is (eacl/can? client request)) + (reset! now 200) + (let [permanent (eacl/check-permission client request)] + (is (:allowed? permanent)) + (is (:cached? permanent) "the proven permanent writer survives the irrelevant member expiry"))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualified_cursor_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualified_cursor_test.cljc new file mode 100644 index 00000000..62df1f6c --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualified_cursor_test.cljc @@ -0,0 +1,152 @@ +(ns eacl.datascript.qualified-cursor-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.context-test :as errors] + [eacl.authorization.qualification :as qualification] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl] + [eacl.caveats.evaluator :as evaluator] + [eacl.datascript.qualified-lookup-test :as fixtures])) + +(defn query [check direction permission] + (assoc (fixtures/count-query (assoc check :permission permission) direction :detailed {"flag" true}) + :first 1 :evaluation :complete-denotation)) + +(defn resume [client direction query page] + (fixtures/lookup! client direction (assoc query (if (:last query) :before :after) + (get-in page [:page-info (if (:last query) :start-cursor :end-cursor)])))) + +(deftest live-cursors-stop-at-expiry-and-pinned-cursors-retain-their-time + (doseq [direction [:forward :reverse] evaluation [:demand :complete-denotation] order [:first :last]] + (let [{:keys [client check now]} (fixtures/mixed-fixture direction 6)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [query (assoc (dissoc (query check direction :direct) :first) order 1 :evaluation evaluation) + pinned (eacl/snapshot client)] + (try + (let [live (fixtures/lookup! client direction query) + historical (fixtures/lookup! pinned direction query)] + (is (seq (:data live))) + (is (not (contains? live :qualification-certificate))) + (reset! now 150) + (let [next-page (resume client direction query live)] + (is (seq (:data next-page))) + (is (not= (:data live) (:data next-page))) + (reset! now 200) + (is (= :eacl.pagination/restart-required + (:type (errors/error-data #(resume client direction query next-page)))))) + (is (= :eacl.pagination/restart-required + (:type (errors/error-data #(resume client direction query live))))) + (is (seq (:data (resume pinned direction query historical)))) + (is (empty? (:data (fixtures/lookup! client direction query)))) + (is (some? (:type (errors/error-data #(resume client direction query historical))))) + (is (some? (:type (errors/error-data #(resume pinned direction query live)))))) + (finally (eacl/release! pinned)))))))) + +(deftest an-expiring-ban-before-the-boundary-requires-a-new-lookup + (doseq [direction [:forward :reverse] + cached? [true false] evaluation [:demand :complete-denotation]] + (let [{:keys [client check now]} (fixtures/mixed-fixture direction 6)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [query (assoc (query check direction :both) :cache? cached? :evaluation evaluation) + first-page (fixtures/lookup! client direction query) + initial-object (get-in first-page [:data 0 :object])] + (is (seq (:data first-page))) + (is (not= (get check (if (= :forward direction) :resource :subject)) initial-object)) + (reset! now 100) + (is (= :eacl.pagination/restart-required + (:type (errors/error-data #(resume client direction query first-page))))) + (is (= (get check (if (= :forward direction) :resource :subject)) + (get-in (fixtures/lookup! client direction query) [:data 0 :object])))))))) + +(deftest context-and-result-policy-mismatches-fail-before-qualification + (let [{:keys [client check]} (fixtures/mixed-fixture :forward 6)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [query (query check :forward :direct) + page (fixtures/lookup! client :forward query) + reads (atom 0) + request-from-adapter qualification/request-from-adapter] + (with-redefs [qualification/qualify (fn [& _] (swap! reads inc) (throw (ex-info "Unexpected traversal" {})))] + (doseq [changed [(assoc query :caveat-context {"flag" false}) + (assoc query :caveat-context {"flag" true "unused" 1}) + (assoc query :result-policy :definite)]] + (is (some? (:type (errors/error-data #(resume client :forward changed page))))))) + (with-redefs [qualification/request-from-adapter + (fn [adapter options] + (let [engine (:evaluator options) + changed (reify evaluator/Evaluator + (descriptor [_] (assoc (evaluator/descriptor engine) :fingerprint "test/changed-evaluator")) + (-evaluate [_ entity context bound] (evaluator/-evaluate engine entity context bound)))] + (request-from-adapter adapter (assoc options :evaluator changed)))) + qualification/qualify (fn [& _] (swap! reads inc) (throw (ex-info "Unexpected traversal" {})))] + (is (some? (:type (errors/error-data #(resume client :forward query page)))))) + (is (zero? @reads)))))) + +(deftest incomplete-certificates-allow-only-the-original-time-without-proof-building + (let [{:keys [client check now]} (fixtures/mixed-fixture :forward 6) + certificate qualification/certificate] + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [qualification/certificate #(assoc (certificate %) :complete? false)] + (let [query (query check :forward :direct) + page (fixtures/lookup! client :forward query)] + (is (seq (:data (resume client :forward query page)))) + (reset! now 100) + (let [reads (atom 0) + failure (with-redefs [qualification/qualify (fn [& _] (swap! reads inc) (throw (ex-info "Unexpected traversal" {})))] + (errors/error-data #(resume client :forward query page)))] + (is (= :eacl.pagination/restart-required (:type failure))) + (is (= :temporal-certificate-incomplete (:reason failure))) + (is (zero? @reads)))))))) + +(deftest cached-range-pages-retain-evidence-deadlines + (doseq [direction [:forward :reverse]] + (let [{:keys [client check now]} (fixtures/mixed-fixture direction 8)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [query (query check direction :walk) + _ (fixtures/lookup! client direction (assoc query :first 5)) + page (fixtures/lookup! client direction query)] + (is (:cached? page)) + (is (seq (:data page))) + (reset! now 200) + (is (= :eacl.pagination/restart-required + (:type (errors/error-data #(resume client direction query page)))))))))) + +(deftest stored-inspection-cursors-are-timeless-but-active-inspection-is-certified + (doseq [state [:stored :expiry-active]] + (let [{:keys [client now]} (fixtures/mixed-fixture :forward 6)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [query {:resource/type :folder :relationship-state state :first 1} + page (eacl/read-relationships client query)] + (is (seq (:data page))) + (reset! now 200) + (let [next-query (assoc query :after (get-in page [:page-info :end-cursor]))] + (if (= :stored state) + (is (seq (:data (eacl/read-relationships client next-query)))) + (is (= :eacl.pagination/restart-required + (:type (errors/error-data #(eacl/read-relationships client next-query)))))))))))) + +(deftest live-pages-and-counts-reuse-only-their-certified-time-interval + (doseq [direction [:forward :reverse] evaluation [:demand :complete-denotation]] + (let [{:keys [client check now]} (fixtures/mixed-fixture direction 6)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [query (assoc (query check direction :direct) :evaluation evaluation) + count-query (dissoc query :first) + first-page (fixtures/lookup! client direction query) + first-count (fixtures/count! client direction count-query)] + (reset! now 150) + (with-redefs [qualification/qualify (fn [& _] (throw (ex-info "Unexpected repeated traversal" {})))] + (let [page (fixtures/lookup! client direction query) + count (fixtures/count! client direction count-query)] + (is (:cached? page)) + (is (:cached? count)) + (is (= (:data first-page) (:data page))) + (is (= (:page-info first-page) (:page-info page))) + (is (= (:count first-count) (:count count))))) + (reset! now 200) + (let [page (fixtures/lookup! client direction query) + count (fixtures/count! client direction count-query)] + (is (false? (:cached? page))) + (is (false? (:cached? count))) + (is (empty? (:data page))) + (is (zero? (:count count)))) + (reset! now 201) + (is (:cached? (fixtures/lookup! client direction query))) + (is (:cached? (fixtures/count! client direction count-query)))))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualified_inspection_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualified_inspection_test.cljc new file mode 100644 index 00000000..6d5272ac --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualified_inspection_test.cljc @@ -0,0 +1,63 @@ +(ns eacl.datascript.qualified-inspection-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.caveats.plan :as plan] + [eacl.caveats.definition-test :as errors] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl] + [eacl.datascript.core :as api] + [eacl.datascript.qualified-check-test :as fixtures])) + +(deftest stored-and-expiry-active-inspection-retain-metadata-without-caveat-evaluation + (let [{:keys [client now]} (fixtures/fixture)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [plan/compile-plan (fn [& _] (throw (ex-info "Inspection compiled a Caveat" {})))] + (doseq [[time expected] [[99 #{:banned :member :parent :writer}] + [100 #{:member :parent :writer}] + [200 #{:parent :writer}]]] + (reset! now time) + (doseq [page-size [{:first 20} {:last 20}]] + (let [query (merge {:resource/type :folder} page-size) + stored (eacl/read-relationships client (assoc query :relationship-state :stored)) + active (eacl/read-relationships client (assoc query :relationship-state :expiry-active))] + (is (= #{:banned :member :parent :writer} (set (map :relation (:data stored))))) + (is (= expected (set (map :relation (:data active))))) + (is (= :stored (:relationship-state stored))) + (is (= :expiry-active (:relationship-state active))) + (is (= time (:evaluation-time-ms active))) + (is (= "enabled" (:caveat (first (filter #(= :member (:relation %)) (:data stored)))))) + (is (= 100 (:valid-until-ms (first (filter #(= :banned (:relation %)) (:data stored)))))) + (is (every? #(instance? eacl.core.Relationship %) (:data stored))) + (is (not-any? #(or (contains? % :allowed?) (contains? % :permissionship)) (:data active))) + (is (= (:data active) + (:data (eacl/read-relationships client (assoc query :relationship-state :expiry-active :cache? false)))))))))))) + +(deftest expiry-filter-keeps-the-existing-candidate-work-bound + (let [{:keys [client now]} (fixtures/fixture) + query {:resource/type :folder :first 1 :relationship-state :expiry-active + :aggregate-limits {:candidate-window 1}}] + (reset! now 100) + (binding [orchestration/*qualified-authorization-enabled?* true] + (let [page (eacl/read-relationships client query)] + (is (empty? (:data page))) + (is (true? (get-in page [:page-info :bounded?]))) + (is (true? (get-in page [:page-info :has-next-page?]))) + (is (string? (get-in page [:page-info :end-cursor]))) + (let [next (eacl/read-relationships client (assoc query :after (get-in page [:page-info :end-cursor])))] + (is (= [:member] (mapv :relation (:data next))))))))) + +(deftest physical-inspection-does-not-require-a-caveat-evaluator + (let [{:keys [conn now]} (fixtures/fixture) + client (api/make-client conn {:clock #(deref now) :caveat-evaluator nil})] + (reset! now 100) + (binding [orchestration/*qualified-authorization-enabled?* true] + (is (= 4 (count (:data (eacl/read-relationships client {:resource/type :folder :first 20}))))) + (is (= 3 (count (:data (eacl/read-relationships client {:resource/type :folder :first 20 + :relationship-state :expiry-active})))))))) + +(deftest inspection-mode-is-validated-before-clock-or-basis-work + (let [calls (atom 0) + client (api/make-client (api/create-conn) {:clock #(do (swap! calls inc) 100)})] + (doseq [mode [nil :active :authorized "stored"]] + (is (= :eacl.filters/invalid-filter + (errors/error-type #(eacl/read-relationships client {:resource/type :folder :relationship-state mode}))))) + (is (zero? @calls)))) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualified_lookup_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualified_lookup_test.cljc new file mode 100644 index 00000000..b5e5c8b1 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualified_lookup_test.cljc @@ -0,0 +1,281 @@ +(ns eacl.datascript.qualified-lookup-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.context-test :as errors] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.result :as result] + [eacl.core :as eacl] + [eacl.cache :as cache] + [eacl.client.orchestration :as orchestration] + [eacl.datascript.core :as datascript] + [eacl.relationships.staged :as staged] + [eacl.datascript.qualified-check-test :as fixture])) + +(defn count-query [check direction policy context] + (merge {:permission (:permission check) :result-policy policy :caveat-context context} + (if (= :forward direction) + {:subject (:subject check) :resource/type :folder} + {:resource (:resource check) :subject/type :user}))) + +(defn count! [client direction query] + ((if (= :forward direction) eacl/count-resources eacl/count-subjects) client query)) + +(defn lookup! [client direction query] + ((if (= :forward direction) eacl/lookup-resources eacl/lookup-subjects) client query)) + +(defn drain-lookup [client direction order query] + (loop [boundary nil pages 0 collected []] + (when (> pages 30) (throw (ex-info "Qualified lookup failed to progress" {}))) + (let [page (lookup! client direction + (cond-> query boundary (assoc (if (= order :asc) :after :before) boundary))) + data (:data page) + collected (if (= order :asc) (into collected data) (into data collected)) + more? (get-in page [:page-info (if (= order :asc) :has-next-page? :has-previous-page?)]) + next-boundary (get-in page [:page-info (if (= order :asc) :end-cursor :start-cursor)])] + (if more? + (do (is (and next-boundary (not= boundary next-boundary))) + (recur next-boundary (inc pages) collected)) + collected)))) + +(deftest public-qualified-lookups-preserve-detailed-results-and-definite-defaults + (let [{:keys [client now check]} (fixture/fixture)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [time [99 100 200]] + (reset! now time) + (doseq [context [{} {"flag" true} {"flag" false}] + permission [:direct :either :both :recursive :walk :via :delegated :united] + direction [:forward :reverse] + policy [:definite :detailed]] + (let [check (assoc check :permission permission :caveat-context context) + decision (dissoc (eacl/check-permission client check) :cached? :cache-basis :evaluation) + membership (:permissionship decision) + object (get check (if (= :forward direction) :resource :subject)) + selected? (if (= :detailed policy) + (not= :no-permission membership) + (= :has-permission membership)) + expected (if selected? + [(if (= :detailed policy) (assoc decision :object object) object)] + []) + query (assoc (count-query check direction policy context) :first 1) + cold (lookup! client direction query) + warm (lookup! client direction query) + uncached (lookup! client direction (assoc query :cache? false))] + (is (= expected (:data cold) (:data warm) (:data uncached))) + (is (:cached? warm))) + (let [query (assoc (count-query (assoc check :permission permission) direction :definite context) :first 1)] + (is (= (:data (lookup! client direction query)) + (:data (lookup! client direction (dissoc query :result-policy))))))))))) + +(deftest public-qualified-counts-distinguish-conditional-results-and-expiring-bans + (let [{:keys [client now check]} (fixture/fixture)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [time [99 100 200]] + (reset! now time) + (doseq [context [{} {"flag" true} {"flag" false}] + permission [:direct :either :both :recursive] + direction [:forward :reverse] + policy [:definite :detailed]] + (let [check (assoc check :permission permission :caveat-context context) + membership (:permissionship (eacl/check-permission client check)) + definite (if (= :has-permission membership) 1 0) + conditional (if (= :conditional-permission membership) 1 0) + query (count-query check direction policy context) + cold (count! client direction query) + warm (count! client direction query) + uncached (count! client direction (assoc query :cache? false))] + (is (= (+ definite (if (= :detailed policy) conditional 0)) (:count cold))) + (when (= :detailed policy) + (is (= definite (:definite-count cold))) + (is (= conditional (:conditional-count cold)))) + (is (= (dissoc cold :cached? :cache-basis) (dissoc warm :cached? :cache-basis) + (dissoc uncached :cached? :cache-basis))) + (is (:cached? warm)) + (let [capped (count! client direction (assoc query :count-limit 0))] + (is (zero? (:count capped))) + (is (= (pos? (:count cold)) (:truncated? capped))) + (when (= :detailed policy) + (is (zero? (:definite-count capped)))) + (when (= :detailed policy) + (is (zero? (:conditional-count capped))))))))))) + +(deftest detailed-empty-counts-retain-both-categories + (let [{:keys [client check]} (fixture/fixture)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [direction [:forward :reverse]] + (let [query (count-query check direction :detailed {}) + query (assoc-in query [(if (= :forward direction) :subject :resource) :id] "missing")] + (is (= {:count 0 :limit -1 :definite-count 0 :conditional-count 0} + (dissoc (count! client direction query) :cached? :cache-basis)))))))) + +(deftest public-count-policies-are-admitted-before-clock-or-basis-work + (let [conn (datascript/create-conn) + ticks (atom 0) + client (datascript/make-client conn {:clock #(swap! ticks inc)})] + (doseq [operation [count! lookup!] + direction [:forward :reverse] + policy [nil false "detailed" :unknown]] + (is (= :eacl.authorization/invalid-result-policy + (:type (errors/error-data #(operation client direction {:result-policy policy})))))) + (is (zero? @ticks)))) + +(deftest detailed-count-cache-ingress-requires-consistent-closed-categories + (doseq [operation [:count-resources :count-subjects] + limit [nil 2]] + (let [query (cond-> {:result-policy :detailed} limit (assoc :count-limit limit)) + key {:operation operation :query {:public query}} + valid (cond-> {:count 2 :limit (or limit -1) :definite-count 1 :conditional-count 1} + limit (assoc :truncated? true))] + (is (cache/completed-answer-value-valid? operation key valid)) + (doseq [invalid [(dissoc valid :conditional-count) + (assoc valid :conditional-count 2) + (assoc valid :definite-count -1 :conditional-count 3) + (assoc valid :definite-count 0.5 :conditional-count 1.5) + (assoc valid :conditional-count nil) + (assoc valid :extra true)]] + (is (false? (cache/completed-answer-value-valid? operation key invalid)))) + (is (false? (cache/completed-answer-value-valid? + operation (assoc-in key [:query :public :result-policy] :definite) valid)))))) + +(deftest qualified-counts-propagate-demanded-faults-in-both-policies + (let [{:keys [conn client check]} (fixture/fixture) + qid (ds/q '[:find ?e . :where [?e :eacl.relationship-qualifier/valid-until-ms 200]] (ds/db conn))] + (ds/transact! conn [[:db/add qid :unknown/private "must not be disclosed"]]) + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [direction [:forward :reverse] + policy [:definite :detailed] + operation [count! lookup!] + permission [:direct :recursive]] + (is (= :eacl.authorization/evaluation-failure + (:type (errors/error-data + #(operation client direction (count-query (assoc check :permission permission) + direction policy {})))))))))) + +(defn mixed-fixture [direction n] + (let [{:keys [conn client check writer] :as env} (fixture/fixture) + candidates (into [(get check (if (= :forward direction) :resource :subject))] + (map #(eacl/spice-object (if (= :forward direction) :folder :user) + (str "candidate-" %))) + (range (dec n))) + db (ds/db conn) + relation (ds/entid db [:eacl.relation/resource-type+relation-name+subject-type [:folder :member :user]]) + caveat (ds/entid db [:eacl.caveat/name "enabled"])] + (doseq [[index candidate] (map-indexed vector (next candidates))] + (let [subject (if (= :forward direction) (:subject check) candidate) + resource (if (= :forward direction) candidate (:resource check))] + (ds/transact! conn [{:eacl/id (:id candidate)}]) + (eacl/create-relationship! client (eacl/->Relationship subject :member resource)) + (eacl/create-relationship! client (eacl/->Relationship subject :writer resource)) + (when (= :forward direction) + (eacl/create-relationship! client (eacl/->Relationship resource :parent resource))) + (let [db (ds/db conn)] + (staged/write! writer :replace + [:user (ds/entid db [:eacl/id (:id subject)]) relation + :folder (ds/entid db [:eacl/id (:id resource)])] + (cond-> {:caveat caveat :valid-until-ms 200} + (not= 0 (mod index 3)) + (assoc :caveat-context {"flag" (= 1 (mod index 3))})))))) + (assoc env :candidates candidates))) + +(deftest qualified-counts-cross-recursive-batch-boundaries-with-mixed-categories + (doseq [direction [:forward :reverse]] + (let [{:keys [client check candidates]} (mixed-fixture direction 100)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [permission [:direct :recursive] + policy [:definite :detailed]] + (let [check (assoc check :permission permission) + memberships (frequencies + (map (fn [candidate] + (:permissionship + (eacl/check-permission client (assoc check (if (= :forward direction) :resource :subject) candidate)))) + candidates)) + definite (get memberships :has-permission 0) + conditional (get memberships :conditional-permission 0) + total (+ definite (if (= :detailed policy) conditional 0)) + query (count-query check direction policy {}) + answer (count! client direction query)] + (is (= total (:count answer))) + (when (= :detailed policy) + (is (= [definite conditional] ((juxt :definite-count :conditional-count) answer)))) + (doseq [limit [0 1 32 64 67 68]] + (let [capped (count! client direction (assoc query :count-limit limit))] + (is (= (min total limit) (:count capped))) + (is (= (> total limit) (:truncated? capped))) + (when (= :detailed policy) + (is (= (:count capped) (+ (:definite-count capped) (:conditional-count capped))))) + (when (= :detailed policy) + (is (<= 0 (:definite-count capped) definite))) + (when (= :detailed policy) + (is (<= 0 (:conditional-count capped) conditional))))))))))) + +(deftest public-qualified-pages-retain-evidence-through-cursors-filters-and-ranges + (doseq [direction [:forward :reverse]] + (let [{:keys [client check candidates]} (mixed-fixture direction 5)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [permission [:direct :recursive :walk :via] + policy [:definite :detailed]] + (let [check (assoc check :permission permission) + point-for (fn [object] + (dissoc (eacl/check-permission + client (assoc check (if (= :forward direction) :resource :subject) object)) + :cached? :cache-basis :evaluation)) + expected (into {} (keep (fn [object] + (let [decision (point-for object)] + (when (if (= :detailed policy) + (not= :no-permission (:permissionship decision)) + (:allowed? decision)) + [(:id object) (if (= :detailed policy) + (assoc decision :object object) object)])))) + candidates) + query (assoc (count-query check direction policy {}) :evaluation :complete-denotation) + id-of (if (= :detailed policy) #(get-in % [:object :id]) :id)] + (doseq [size [1 3] + cached? [true false] + filtered? [false true]] + (let [query (cond-> (assoc query :cache? cached?) + filtered? (assoc (if (= :forward direction) :resource/relationship :subject/relationship) + (if (= :forward direction) + {:relation :writer :subject (:subject check)} + {:relation :writer :resource (:resource check)}))) + forward (drain-lookup client direction :asc (assoc query :first size)) + backward (drain-lookup client direction :desc (assoc query :last size))] + (is (= [expected (count expected) forward] + [(into {} (map (juxt id-of identity)) forward) (count forward) backward])))))))))) + +(deftest qualified-relationship-filters-compose-with-whole-permission-evidence + (let [{:keys [client now check]} (fixture/fixture)] + (binding [orchestration/*qualified-authorization-enabled?* true] + (doseq [time [99 200]] + (reset! now time) + (doseq [direction [:forward :reverse] + permission [:either :direct :walk :recursive :united] + policy [:definite :detailed] + context [{} {"flag" true} {"flag" false}]] + (let [query (assoc (count-query (assoc check :permission permission) direction policy context) :first 1) + filtered (assoc query (if (= :forward direction) :resource/relationship :subject/relationship) + (if (= :forward direction) + {:relation :member :subject (:subject check)} + {:relation :member :resource (:resource check)})) + expected (:data (lookup! client direction + (assoc query :permission :direct)))] + (is (= expected (:data (lookup! client direction filtered)))))))))) + +(deftest detailed-lookup-cache-ingress-is-policy-and-residual-aware + (doseq [operation [:lookup-resources :lookup-subjects]] + (let [object (eacl/spice-object :folder "folder") + conditional (result/lookup-result object (evidence/conditional [:a] ["flag"])) + info {:start-cursor nil :end-cursor nil :has-next-page? false :has-previous-page? false} + key {:operation operation :query {:public {:result-policy :detailed}}} + valid {:data [conditional] :page-info info} + validate (fn [key page] + [(cache/completed-answer-value-valid? operation key page) + (cache/rendered-page-entry-valid? + key {:format cache/rendered-page-entry-format :page page})])] + (is (= [true true] (validate key valid))) + (is (= [false false] (validate (assoc-in key [:query :public :result-policy] :definite) valid))) + (doseq [item [object + (assoc conditional :allowed? true) + (assoc conditional :missing-fields ["other"]) + (assoc conditional :residual (evidence/encode true)) + (assoc conditional :residual (evidence/encode false)) + (assoc conditional :residual (evidence/encode (evidence/fault :invalid :qualifier)))]] + (is (= [false false] (validate key (assoc valid :data [item])))))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualified_schema_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualified_schema_test.cljc new file mode 100644 index 00000000..127a950f --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualified_schema_test.cljc @@ -0,0 +1,83 @@ +(ns eacl.datascript.qualified-schema-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.qualification-test :as fixtures] + [eacl.caveats.definition-test :as errors] + [eacl.caveats.evaluator :as evaluator] + [eacl.caveats.schema-admission-test :as schemas] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl] + [eacl.datascript.core :as api] + [eacl.backend.v8 :as backend] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.datascript.schema :as schema])) + +(def unvisited-schema + (str (schemas/source "user") + "\ndefinition secret { relation viewer: user with enabled\n permission view = viewer\n}")) + +(deftest schema-admission-requires-evaluator-before-writes-or-empty-reads + (let [conn (schema/create-conn) + reader (api/make-client conn {:caveat-evaluator nil}) + client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + subject (eacl/spice-object :user "schema/u") + resource (eacl/spice-object :doc "schema/doc") + request {:subject subject :permission :view :resource resource}] + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-schema! reader {:schema (schemas/source "user")}) + (let [before @conn] + (is (= :eacl.caveat/evaluator-unavailable + (errors/error-type #(eacl/write-schema! reader {:schema unvisited-schema})))) + (is (identical? before @conn))) + (ds/transact! conn [{:eacl/id "schema/u"} {:eacl/id "schema/doc"}]) + (is (false? (eacl/can? reader request))) + (eacl/write-schema! client {:schema unvisited-schema}) + (is (false? (eacl/can? client request))) + (doseq [run [#(eacl/can? reader request) + #(eacl/check-permission reader request) + #(eacl/lookup-resources reader {:subject subject :permission :view :resource/type :doc}) + #(eacl/count-resources reader {:subject subject :permission :view :resource/type :doc}) + #(eacl/lookup-subjects reader {:subject/type :user :permission :view :resource resource}) + #(eacl/count-subjects reader {:subject/type :user :permission :view :resource resource})]] + (is (= :eacl.caveat/evaluator-unavailable (errors/error-type run)))) + (let [parent (eacl/snapshot client)] + (try + (let [child (eacl/with-schema parent (schemas/source "user with enabled"))] + (try + (is (false? (:eacl.relation/allows-unqualified? (first (:relations (eacl/read-schema child)))))) + (finally (eacl/release! child)))) + (finally (eacl/release! parent))))))) + +(deftest qualified-publication-capability-is-required-at-schema-boundaries + (let [conn (schema/create-conn) + client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (is (= :prepared (:strategy (qualifiers/publication-capability @conn)))) + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [qualifiers/publication-capability (constantly nil)] + (let [before @conn] + (is (= :eacl/unsupported-capability + (errors/error-type #(eacl/write-schema! client {:schema unvisited-schema})))) + (is (identical? before @conn))))))) + +(deftest mismatched-custom-evaluator-is-rejected-at-client-construction + (let [bad (reify evaluator/Evaluator + (descriptor [_] {:profile "eacl-cel/1" :profile-fingerprint "wrong" + :fingerprint "custom" :capability-version 1}) + (-evaluate [_ _ _ _] true))] + (is (= :eacl.caveat/evaluator-unavailable + (errors/error-type #(api/make-client (schema/create-conn) {:caveat-evaluator bad})))))) + +(deftest unsupported-publication-is-rejected-before-a-warm-answer + (let [conn (schema/create-conn) + client (api/make-client conn {:clock (constantly 100) :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + request {:subject (eacl/spice-object :user "warm/u") + :resource (eacl/spice-object :doc "warm/doc") :permission :view}] + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-schema! client {:schema (schemas/source "user")}) + (ds/transact! conn [{:eacl/id "warm/u"} {:eacl/id "warm/doc"}]) + (eacl/create-relationship! client (eacl/->Relationship (:subject request) :viewer (:resource request))) + (is (true? (eacl/can? client request))) + (is (true? (:cached? (eacl/check-permission client request)))) + (let [capabilities backend/capabilities] + (with-redefs [backend/capabilities #(dissoc (capabilities %) :qualified-publication)] + (is (= :eacl/unsupported-capability (errors/error-type #(eacl/can? client request))))))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualified_write_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualified_write_test.cljc new file mode 100644 index 00000000..200345f0 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualified_write_test.cljc @@ -0,0 +1,91 @@ +(ns eacl.datascript.qualified-write-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.caveats.publication-batch-contract :as batch] + [eacl.caveats.public-write-contract :as public] + [eacl.caveats.write-contention-contract :as contention] + [eacl.caveats.schema-allowance-contract :as allowance] + [eacl.caveats.inspection-contract :as inspection] + [eacl.caveats.deletion-contract :as deletion] + [eacl.caveats.cache-trace-contract :as cache-trace] + [eacl.core :as eacl] + [eacl.client.orchestration :as orchestration] + [eacl.relationships.storage :as storage] + [eacl.authorization.qualification-test :as fixtures] + [eacl.datascript.core :as api] + [eacl.datascript.caveat-schema-test :as schema-races] + [eacl.datascript.schema :as schema] + [eacl.datascript.storage :as admission] + [eacl.datascript.qualifiers :as qualifiers])) + +(deftest qualified-batches-publish-atomically + (let [conn (schema/create-conn {:app/flag {}})] + (batch/check! {:write-schema! #(schema/write-schema! conn %) + :writer #(qualifiers/writer conn) :entid ds/entid :strategy :prepared}))) + +(deftest public-qualified-writes-preserve-identity-and-commit-atomically + (let [conn (schema/create-conn {:app/flag {}}) + now (atom 99) + client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (public/check! {:client client :writer #(qualifiers/writer conn) :entid ds/entid :now now}))) + +(deftest schema-alternatives-preserve-relation-identities-and-retained-data + (let [conn (schema/create-conn) + client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (allowance/check! {:client client :writer #(qualifiers/writer conn) + :read-schema schema/read-schema :interleave! schema-races/interleave! :entid ds/entid}))) + +(deftest externally-created-connection-supports-qualified-preparation-and-snapshot-publication + (let [conn (ds/create-conn (schema/merge-schema {:app/flag {}})) + _ (admission/bootstrap! conn) + now (atom 99) + client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (is (nil? (:eacl.datascript/source-id (ds/entity (ds/db conn) [:eacl/id "datascript-metadata"])))) + (public/check! {:client client :writer #(qualifiers/writer conn) :entid ds/entid :now now}))) + +(deftest stored-and-active-inspection-preserve-aligned-native-qualifiers + (let [conn (schema/create-conn {:app/flag {}}) + now (atom 99) + client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (inspection/check! {:client client :writer #(qualifiers/writer conn) :entid ds/entid :now now}))) + +(deftest qualified-object-deletion-is-atomic-and-bounded + (let [conn (schema/create-conn) + client (api/make-client conn {:clock (constantly 200) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))})] + (deletion/check! {:client client :writer #(qualifiers/writer conn)}))) + +(deftest qualified-object-deletion-cleans-surviving-peers-and-unblocks-schema-removal + (let [conn (schema/create-conn) + client (api/make-client conn {:clock (constantly 200) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + subject (eacl/spice-object :user "deleted/u") + resource (eacl/spice-object :doc "deleted/doc") + replacement "definition user {}\ndefinition doc {\n relation member: user\n permission direct = member\n}"] + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-schema! client {:schema (cache-trace/schema "flag")}) + (ds/transact! conn [{:eacl/id "deleted/u"} {:eacl/id "deleted/doc"}]) + (eacl/create-relationship! client (assoc (eacl/->Relationship subject :member resource) + :caveat "enabled" :valid-until-ms 100)) + (let [before (ds/db conn) + sid (ds/entid before [:eacl/id "deleted/u"]) + qid (:e (first (ds/datoms before :aevt :eacl.relationship-qualifier/format-version))) + failure (cache-trace/outcome #(eacl/write-schema! client {:schema replacement}))] + (is (contains? #{:eacl.schema/caveat-in-use :eacl.schema/relationship-qualifier-in-use} + (get-in failure [:fault :type]))) + (is (identical? before (ds/db conn))) + (ds/transact! conn [[:db/retractEntity sid]]) + (is (= 1 (count (ds/datoms (ds/db conn) :aevt storage/reverse-attribute)))) + (eacl/delete-object! client (eacl/spice-object :user sid)) + (is (empty? (ds/datoms (ds/db conn) :aevt storage/reverse-attribute))) + (is (empty? (ds/datoms (ds/db conn) :eavt qid))) + (is (not (contains? (cache-trace/outcome #(eacl/write-schema! client {:schema replacement})) :fault))))))) + +(deftest qualified-native-cas-contention-replans-from-a-new-basis + (let [conn (schema/create-conn) + client (api/make-client conn {})] + (contention/check! client #(qualifiers/writer conn)) + (contention/terminal-validation-check! client))) diff --git a/modules/eacl-datascript/test/eacl/datascript/qualifier_cache_test.cljc b/modules/eacl-datascript/test/eacl/datascript/qualifier_cache_test.cljc new file mode 100644 index 00000000..6147e79e --- /dev/null +++ b/modules/eacl-datascript/test/eacl/datascript/qualifier_cache_test.cljc @@ -0,0 +1,70 @@ +(ns eacl.datascript.qualifier-cache-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.cache :as answer-cache] + [eacl.authorization.qualifier-cache :as cache] + [eacl.authorization.qualification-test :as qualification-fixtures] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl] + [eacl.datascript.core :as datascript] + [eacl.datascript.qualified-check-test :as fixtures] + [eacl.relationships.qualifier :as qualifier])) + +(defn- cache-state [client] + @(get-in client [:runtime :runtime-lifecycle-state])) + +(deftest public-decode-reuse-rechecks-unknown-native-writers + (let [{:keys [conn client now check]} (fixtures/fixture) + request (assoc check :caveat-context {"flag" true}) + decodes (atom 0) decode qualifier/decode + qid (:e (first (ds/datoms (ds/db conn) :aevt qualifier/caveat-attribute)))] + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [qualifier/decode (fn [& args] (swap! decodes inc) (apply decode args))] + (is (true? (eacl/can? client request))) + (is (= 1 @decodes)) + (reset! now 100) + (is (true? (eacl/can? client request))) + (is (= 1 @decodes) "same basis reuses decoded data at a different time") + (ds/transact! conn [{:db/id [:eacl/id "folder"] :app/noise "unrelated"}]) + (reset! now 101) + (is (true? (eacl/can? client request))) + (is (= 1 @decodes) "equal complete content reuses decoding across a native write") + (ds/transact! conn [[:db/add qid qualifier/expiration-attribute 101]]) + (is (false? (eacl/can? client request))) + (is (= 2 @decodes) "in-place mutation with unchanged marker and Relation stamp misses") + (is (= (eacl/can? client request) (eacl/can? client (assoc request :cache? false)))))))) + +(deftest public-decode-retention-respects-request-and-lifecycle-controls + (let [{:keys [client now check]} (fixtures/fixture) + request (assoc check :caveat-context {"flag" true}) + decodes (atom 0) decode qualifier/decode] + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [qualifier/decode (fn [& args] (swap! decodes inc) (apply decode args))] + (dotimes [_ 2] (is (true? (eacl/can? client (assoc request :populate-cache? false))))) + (is (= 2 @decodes)) + (is (true? (eacl/can? client request))) + (is (= 3 @decodes)) + (reset! now 100) + (is (true? (eacl/can? client (assoc request :populate-cache? false)))) + (is (= 3 @decodes) "read-only cache requests may use a retained decode") + (is (true? (eacl/can? client (assoc request :cache? false)))) + (is (= 4 @decodes)) + (let [before (:qualifier-decode-cache (cache-state client))] + (datascript/expire-cache! client "qualifier-lifecycle-reset") + (is (not (identical? before (:qualifier-decode-cache (cache-state client)))))) + (is (true? (eacl/can? client request))) + (is (= 5 @decodes)))))) + +(deftest optional-decode-cache-configuration-is-uniform + (let [{:keys [conn now check]} (fixtures/fixture)] + (doseq [options [{:qualifier-cache false} {:cache answer-cache/no-cache}]] + (let [client (datascript/make-client conn (merge options (hash-map :clock #(deref now) :caveat-evaluator (qualification-fixtures/portable-evaluator (atom 0))))) + decodes (atom 0) decode qualifier/decode] + (is (nil? (:qualifier-decode-cache (cache-state client)))) + (binding [orchestration/*qualified-authorization-enabled?* true] + (with-redefs [qualifier/decode (fn [& args] (swap! decodes inc) (apply decode args))] + (dotimes [_ 2] + (swap! now inc) + (is (true? (eacl/can? client (assoc check :caveat-context {"flag" true} :populate-cache? false))))) + (is (= 2 @decodes)))))) + (is (cache/cache? (:qualifier-decode-cache (cache-state (datascript/make-client conn {:qualifier-cache {:max-entries 3}}))))))) diff --git a/modules/eacl-datascript/test/eacl/datascript/snapshot_lifecycle_test.cljc b/modules/eacl-datascript/test/eacl/datascript/snapshot_lifecycle_test.cljc index d757a553..a7c5046d 100644 --- a/modules/eacl-datascript/test/eacl/datascript/snapshot_lifecycle_test.cljc +++ b/modules/eacl-datascript/test/eacl/datascript/snapshot_lifecycle_test.cljc @@ -31,7 +31,9 @@ (defn- fixture [] (let [conn (datascript/create-conn) - client (datascript/make-client conn {:cache cache/no-cache}) + ;; These contracts isolate basis/lifecycle changes. Dedicated qualified + ;; cursor tests exercise differing captured times. + client (datascript/make-client conn {:cache cache/no-cache :clock (constantly 100)}) user (eacl/spice-object :user "user") account (eacl/spice-object :account "account")] (eacl/write-schema! client schema) @@ -256,10 +258,11 @@ :permission :admin :cache? false})}))) counts (request-counters/snapshot ledger)] - (is (= {:allowed? true - :cached? false - :cache-basis nil - :evaluation :demand} + (is (= (cond-> {:allowed? true + :cached? false + :cache-basis nil + :evaluation :demand} + orchestration/*qualified-authorization-enabled?* (assoc :permissionship :has-permission)) (:uncached-decision value))) (is (= [false true] (mapv :cached? (:cached-decisions value)))) @@ -484,7 +487,7 @@ (:forbidden-operation-errors value))) (is (empty? (set/intersection - #{:conn :writer :selected-snapshot + #{:conn :writer :qualified-writer :transact! :selected-snapshot :acquire-current! :acquire-authoritative! :acquire-at-least! :acquire-exact!} (set (filter keyword? reachable))))) @@ -492,7 +495,7 @@ (fn [value] (some #(identical? value %) forbidden-identities)) reachable)) - (is (= #{:backend-id :schema :impl + (is (= #{:backend-id :schema :impl :entid :qualified-plan :qualified-publication-capability :basis-adapter :basis-adapter-config-keys :native-with :normalize-report-datom :schema-storage-datom? :transaction-datom? @@ -966,11 +969,13 @@ (datascript/make-client conn {:cache {:max-entries 32} + :cursor-ttl-seconds 3600 :aggregate-limits {:candidate-window 10}}) strict (datascript/make-client conn {:cache {:max-entries 32} + :cursor-ttl-seconds 3600 :aggregate-limits {:candidate-window 2}}) query {:resource/type :document @@ -1457,7 +1462,7 @@ (let [error (ex-info "injected foreign failure" {:type :test/foreign-failure})] (with-redefs - [engine/can? (fn [& _] (throw error))] + [engine/check-evidence (fn [& _] (throw error))] (assert-one-release! (observed-failure conn #(eacl/can? client user :admin account)) @@ -1466,7 +1471,7 @@ (let [token (eacl/cancellation-token)] (with-redefs [execution/check! - (fail-execution-stage + (fail-execution-stage :consistency-selected (ex-info "EACL authorization execution was cancelled." @@ -1501,7 +1506,7 @@ (datascript/make-client conn {:cache {}})] (with-redefs [execution/check! - (fail-execution-stage + (fail-execution-stage :cache-publication (ex-info "injected cache publication failure" {:type :test/cache-publication-failure}))] diff --git a/modules/eacl-datascript/test/eacl/engine/least_path_evidence_test.cljc b/modules/eacl-datascript/test/eacl/engine/least_path_evidence_test.cljc new file mode 100644 index 00000000..b0908910 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/engine/least_path_evidence_test.cljc @@ -0,0 +1,159 @@ +(ns eacl.engine.least-path-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.evidence-test :as values] + [eacl.authorization.qualification :as qualification] + [eacl.datascript.backend :as backend] + [eacl.datascript.impl :as impl] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.datascript.schema :as schema] + [eacl.engine.least-path :as least-path] + [eacl.engine.sealed-plan :as sealed] + [eacl.operator.evaluator-test :as fixtures] + [eacl.relationships.edge :as edge] + [eacl.relationships.staged :as staged])) + +(def source + "definition user {} + definition group { + relation member: user + permission active = member + } + definition doc { + relation reader: user + relation writer: user + relation parent: group + permission direct = reader + writer + permission delegated = direct + permission via = reader + parent->member + permission inherited = reader + parent->active + }") + +(def rows + [[:user "u0" :reader :doc "d0" :a] [:user "u0" :reader :doc "d1" :b] + [:user "u1" :reader :doc "d2" :a] [:user "u0" :writer :doc "d0" :b] + [:user "u0" :writer :doc "d2" :a] [:user "u1" :writer :doc "d1" :b] + [:group "g0" :parent :doc "d0" :a] [:group "g0" :parent :doc "d1" :b] + [:group "g1" :parent :doc "d1" :a] [:group "g1" :parent :doc "d2" :b] + [:user "u0" :member :group "g0" :a] [:user "u0" :member :group "g1" :b] + [:user "u1" :member :group "g1" :a]]) + +(defn fixture + ([] (fixture true)) + ([qualified?] + (let [conn (schema/create-conn {})] + (schema/write-schema! conn source) + (ds/transact! conn (mapv #(hash-map :eacl/id %) ["u0" "u1" "d0" "d1" "d2" "g0" "g1"])) + (let [db (ds/db conn) writer (qualifiers/writer conn) + ids (into {} (map (fn [name] [name (ds/entid db [:eacl/id name])]) + ["u0" "u1" "d0" "d1" "d2" "g0" "g1"])) + identities (mapv (fn [[stype subject relation rtype resource role]] + [stype (ids subject) + (ds/entid db [:eacl.relation/resource-type+relation-name+subject-type [rtype relation stype]]) + rtype (ids resource) role]) rows)] + (doseq [row identities] + (staged/write! writer :create (subvec row 0 5) (when qualified? {:valid-until-ms 1000}))) + (let [db (ds/db conn)] + {:db db :ids ids :adapter (backend/basis-adapter db {}) + :qid-roles (into {} (keep (fn [row] + (when-let [qid (edge/qualifier-id (apply impl/direct-edge db (subvec row 0 5)))] + [qid (nth row 5)]))) identities)}))))) + +(defn path-roles [permission subject resource] + (let [direct-names (if (#{:direct :delegated} permission) #{:reader :writer} #{:reader}) + direct (for [[_ s r _ o role] rows :when (and (= subject s) (= resource o) (direct-names r))] [role])] + (into (vec direct) + (when (#{:via :inherited} permission) + (for [[_ group r _ object via] rows :when (and (= :parent r) (= resource object)) + [_ s r _ target holding] rows :when (and (= :member r) (= subject s) (= group target))] + [via holding]))))) + +(defn membership [permission subject resource a b] + (reduce #(evidence/combine :union %1 %2) false + (map (fn [roles] (reduce #(evidence/combine :arrow %1 ({:a a :b b} %2)) true roles)) + (path-roles permission subject resource)))) + +(defn options [{:keys [db adapter ids]} permission traversal direction width] + (merge {:adapter adapter :plan (sealed/seal-plan adapter [:doc permission]) + :subject-type :user :physical-chunk-size width :page-size width :raw-candidates? true + :qualification (fixtures/qualified-request db 99 {})} + (if (= traversal :forward) {:subject-eid (ids "u0")} {:resource-eid (ids "d1")}) + (when (= direction :desc) {:last? true}))) + +(defn drain [options traversal direction] + (loop [boundary nil pages 0 result []] + (when (> pages 32) (throw (ex-info "Legacy qualified page failed to progress" {}))) + (let [page ((if (= traversal :forward) least-path/forward-page least-path/reverse-page) + (cond-> options boundary (assoc (if (= direction :asc) :after-coords :before-coords) boundary))) + rows (:emissions page) result (into result rows)] + (if (:exhausted? page) result + (recur (:coords (peek rows)) (inc pages) result))))) + +(defn evaluate [env permission traversal direction width a b] + (with-redefs [qualification/qualify + (fn [_ _ value] + (if-let [qid (edge/qualifier-id value)] + ({:a a :b b} (get (:qid-roles env) qid)) (some? value)))] + (drain (options env permission traversal direction width) traversal direction))) + +(deftest qualified-unions-keep-native-order-and-complete-node-evidence + (let [env (fixture)] + (doseq [permission [:direct :delegated :via :inherited] + traversal [:forward :reverse] direction [:asc :desc] width [1 2] + a [false true values/x values/y] b [false true values/x values/y]] + (let [actual (evaluate env permission traversal direction width a b) + names (if (= traversal :forward) ["d0" "d1" "d2"] ["u0" "u1"]) + expected (into {} (keep (fn [name] + (let [value (if (= traversal :forward) + (membership permission "u0" name a b) + (membership permission name "d1" a b))] + (when-not (evidence/no? value) + [(get-in env [:ids name]) (evidence/value value)])))) names)] + (is (= expected (into {} (map (juxt :value (comp evidence/value :evidence))) actual))) + (is (= (count expected) (count actual))) + (is (every? (if (= direction :asc) neg? pos?) + (map #(least-path/compare-coords (:coords %1) (:coords %2)) actual (rest actual)))))))) + +(deftest nil-qualifier-unions-retain-existing-coordinates-without-context-work + (let [env (fixture false)] + (doseq [permission [:direct :delegated :via :inherited] + traversal [:forward :reverse] direction [:asc :desc] width [1 2]] + (let [options (options env permission traversal direction width) + ordinary (drain (dissoc options :qualification) traversal direction) + qualified (with-redefs [qualification/exact-reuse-identity + (fn [_] (throw (ex-info "Ordinary paths need no qualified scope" {})))] + (drain options traversal direction))] + (is (= ordinary (mapv #(dissoc % :evidence) qualified))) + (is (every? #(evidence/has? (:evidence %)) qualified)))))) + +(deftest union-boundaries-are-independent-of-walk-direction + (let [env (fixture)] + (doseq [permission [:direct :delegated :via :inherited] traversal [:forward :reverse] width [1 2] + a [false values/x] b [true values/y]] + (let [ascending (evaluate env permission traversal :asc width a b) + descending (evaluate env permission traversal :desc width a b)] + (is (= (mapv #(select-keys % [:value :coords]) (reverse ascending)) + (mapv #(select-keys % [:value :coords]) descending))))))) + +(deftest legacy-node-evidence-is-bounded-and-demanded-faults-survive + (let [env (fixture)] + (with-redefs [least-path/maximum-qualified-node-evidence 1] + (is (= :node-evidence-limit + (try (evaluate env :direct :forward :asc 3 values/x values/y) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (:reason (ex-data error))))))) + (is (= :eacl.authorization/evaluation-failure + (try (evaluate env :direct :forward :asc 1 (evidence/fault :test/failure :invalid) true) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (:type (ex-data error)))))))) + +(deftest expired-union-paths-do-not-claim-an-earlier-coordinate + (let [env (fixture)] + (doseq [permission [:direct :delegated :via :inherited] + traversal [:forward :reverse] direction [:asc :desc] time [999 1000]] + (let [options (assoc (options env permission traversal direction 1) + :qualification (fixtures/qualified-request (:db env) time {})) + actual (drain options traversal direction)] + (is (= (if (= time 1000) 0 (if (= traversal :forward) 3 2)) + (count actual))))))) diff --git a/modules/eacl-datascript/test/eacl/engine/stable_route_native_evidence_test.cljc b/modules/eacl-datascript/test/eacl/engine/stable_route_native_evidence_test.cljc new file mode 100644 index 00000000..b868825d --- /dev/null +++ b/modules/eacl-datascript/test/eacl/engine/stable_route_native_evidence_test.cljc @@ -0,0 +1,71 @@ +(ns eacl.engine.stable-route-native-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.datascript.backend :as backend] + [eacl.datascript.impl :as impl] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.datascript.schema :as schema] + [eacl.engine.scan-cache :as scan-cache] + [eacl.engine.sealed-plan :as plan] + [eacl.engine.stable-reducer :as reducer] + [eacl.engine.stable-route :as route] + [eacl.operator.evaluator-test :as fixtures] + [eacl.relationships.edge :as edge] + [eacl.relationships.staged :as staged])) + +(deftest native-compact-scans-and-memos-preserve-qualified-point-evidence + (let [conn (schema/create-conn {})] + (schema/write-schema! conn + "caveat enabled(flag bool) { flag } + definition user {} + definition doc { + relation reader: user + relation parent: doc + permission direct = reader + permission via = parent->reader + permission inherited = parent->direct + permission view = reader + parent->view + }") + (ds/transact! conn [{:eacl/id "user"} {:eacl/id "root"} {:eacl/id "leaf"}]) + (let [eid #(ds/entid (ds/db conn) [:eacl/id %]) + user (eid "user") root (eid "root") leaf (eid "leaf") + relation (fn [name type] + (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type [:doc name type]])) + reader (relation :reader :user) parent (relation :parent :doc) + caveat (ds/entid (ds/db conn) [:eacl.caveat/name "enabled"]) + writer (qualifiers/writer conn)] + (ds/transact! conn [(hash-map :db/id reader :eacl.relation/caveats [caveat] + :eacl.relation/allows-unqualified? true)]) + (staged/write! writer :create [:user user reader :doc leaf] {:caveat caveat}) + (staged/write! writer :create [:doc leaf parent :doc root] {:valid-until-ms 100}) + (staged/write! writer :create [:doc root parent :doc leaf] nil) + (let [db (ds/db conn) adapter (backend/basis-adapter db {}) + fetch (scan-cache/caching-fetch-fn (reducer/adapter-fetch-fn adapter) + {:memo (scan-cache/memo)}) + run (fn [permission resource time context] + (route/check-eids {:adapter adapter :fetch-fn fetch + :plan (plan/seal-plan adapter [:doc permission]) + :subject-type :user :subject-eid user :resource-eid resource + :physical-chunk-size 1 + :qualification (fixtures/qualified-request db time context)}))] + (doseq [permission [:via :inherited :view] context [{} {"flag" true} {"flag" false}]] + (let [before (run permission root 99 context) + after (run permission root 100 context)] + (is (= (case (get context "flag" :missing) + :missing :conditional-permission true :has-permission false :no-permission) + (evidence/permissionship before))) + (is (= (when-not (= false (get context "flag")) 100) (evidence/valid-until before))) + (is (false? after)))) + (is (= :conditional-permission (evidence/permissionship (run :direct leaf 100 {})))) + (let [qid (edge/qualifier-id (impl/direct-edge db :user user reader :doc leaf))] + (ds/transact! conn [[:db.fn/retractEntity qid]]) + (let [damaged (ds/db conn) adapter (backend/basis-adapter damaged {}) + check (fn [permission resource time] + (route/check-eids {:adapter adapter + :plan (plan/seal-plan adapter [:doc permission]) + :subject-type :user :subject-eid user :resource-eid resource + :qualification (fixtures/qualified-request damaged time {})}))] + (is (evidence/fault? (check :direct leaf 100))) + (is (false? (check :via root 100))) + (is (false? (check :view root 100))))))))) diff --git a/modules/eacl-datascript/test/eacl/operator/arrow_evidence_test.cljc b/modules/eacl-datascript/test/eacl/operator/arrow_evidence_test.cljc new file mode 100644 index 00000000..f9a4cf52 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/operator/arrow_evidence_test.cljc @@ -0,0 +1,160 @@ +(ns eacl.operator.arrow-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.evidence-test :as symbolic] + [eacl.authorization.qualification :as qualification] + [eacl.backend.v8 :as backend] + [eacl.datascript.backend :as datascript-backend] + [eacl.datascript.impl :as impl] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.datascript.schema :as schema] + [eacl.operator.evaluator :as scalar] + [eacl.operator.evaluator-test :as fixtures] + [eacl.operator.plan :as plan] + [eacl.operator.lookup :as lookup] + [eacl.operator.lookup-evidence-test :as lookup-fixtures] + [eacl.operator.seekable-evidence-test :as errors] + [eacl.relationships.edge :as edge] + [eacl.relationships.staged :as staged])) + +(defn fixture [] + (let [conn (schema/create-conn {})] + (schema/write-schema! conn fixtures/arrow-schema) + (ds/transact! conn (mapv #(hash-map :eacl/id %) ["user" "group0" "group1" "document"])) + (let [[user g0 g1 document] (mapv #(ds/entid (ds/db conn) [:eacl/id %]) + ["user" "group0" "group1" "document"]) + groups [g0 g1] + relation (fn [rt r st] (ds/entid (ds/db conn) + [:eacl.relation/resource-type+relation-name+subject-type [rt r st]])) + parent (relation :document :parent :group) + member (relation :group :member :user) + writer (qualifiers/writer conn)] + (doseq [group groups] + (staged/write! writer :create [:group group parent :document document] {:valid-until-ms 1000}) + (staged/write! writer :create [:user user member :group group] {:valid-until-ms 1000})) + (staged/write! writer :create [:user user (relation :document :reader :user) :document document] nil) + (let [db (ds/db conn)] + {:db db :conn conn :adapter (datascript-backend/basis-adapter db {}) + :user user :groups groups :document document :member member + :qids (into {} (mapcat (fn [[i group]] + [[(edge/qualifier-id (impl/direct-edge db :group group parent :document document)) [:via i]] + [(edge/qualifier-id (impl/direct-edge db :user user member :group group)) [:target i]]]) + (map-indexed vector groups)))})))) + +(defn options [{:keys [adapter db user document groups]} name] + (let [permission [:document name] sealed (plan/seal-plan adapter permission) + node-id (first (keep (fn [[id p]] (when (= :arrow-membership (:instruction p)) id)) + (get (:predicate-programs sealed) permission))) + request (fixtures/qualified-request db 99 {})] + {:adapter adapter :plan sealed :permission permission :node-id node-id + :subject-type :user :subject-eid user :resource-eid document :qualification request + :arrow-witness {:point [permission node-id :user user document] :partition 0 + :intermediate (first groups) :scope (qualification/exact-reuse-identity request) + :evidence false}})) + +(defn resolver [qids semantic] + (fn [_ _ compact] + (if (vector? compact) (get semantic (get qids (edge/qualifier-id compact))) (some? compact)))) + +(defn lookup-fixture [] + (let [{:keys [conn user groups document member] :as env} (fixture)] + (ds/transact! conn (mapv #(hash-map :eacl/id %) ["user2" "document2" "document3"])) + (let [users [user (ds/entid (ds/db conn) [:eacl/id "user2"])] + documents [document (ds/entid (ds/db conn) [:eacl/id "document2"]) + (ds/entid (ds/db conn) [:eacl/id "document3"])] + parent (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type + [:document :parent :group]]) + writer (qualifiers/writer conn)] + (doseq [group groups target (rest documents)] + (staged/write! writer :create [:group group parent :document target] {:valid-until-ms 1000})) + (doseq [subject (rest users) group groups] + (staged/write! writer :create [:user subject member :group group] {:valid-until-ms 1000})) + (let [db (ds/db conn) + qids (into {} (concat + (for [[i group] (map-indexed vector groups) target documents] + [(edge/qualifier-id (impl/direct-edge db :group group parent :document target)) [:via i]]) + (for [[i group] (map-indexed vector groups) subject users] + [(edge/qualifier-id (impl/direct-edge db :user subject member :group group)) [:target i]])))] + (assoc env :db db :adapter (datascript-backend/basis-adapter db {}) + :users users :documents documents :qids qids))))) + +(defn lookup-options [env permission traversal direction width policy] + (-> (options env permission) + (dissoc :node-id :arrow-witness) + (assoc :traversal traversal :anchor-eid (if (= traversal :forward) (:user env) (:document env)) + :order-direction direction :page-size width :candidate-window 2 + :result-policy policy :direct-specializations? false + :traversal-limits {:physical-chunk-size 1}))) + +(defn check-lookup-case! [env permission traversal direction width policy values] + (let [semantic (zipmap [[:via 0] [:target 0] [:via 1] [:target 1]] values) + result (evidence/combine :union (evidence/combine :arrow (nth values 0) (nth values 1)) + (evidence/combine :arrow (nth values 2) (nth values 3))) + options (lookup-options env permission traversal direction width policy) + rows (with-redefs [qualification/qualify (resolver (:qids env) semantic)] + (lookup-fixtures/drain options)) + selected? (if (= policy :definite) (evidence/has? result) (not (evidence/no? result))) + expected (if selected? (if (= traversal :forward) (:documents env) (:users env)) [])] + (is (= (set expected) (set (map :value rows)))) + (is (= (count expected) (count rows))) + (is (every? #(= (evidence/value result) (evidence/value (:evidence %))) rows)))) + +(deftest ordered-arrows-compose-whole-child-evidence-and-resume-bindings + (let [env (lookup-fixture) x symbolic/x y symbolic/y + negative-x (evidence/combine :exclusion true x) + inputs (for [a [true x negative-x] b [x negative-x] c [false true] d [true y]] [a b c d])] + (doseq [permission [:inherited :inherited_direct] traversal [:forward :reverse] + direction [:asc :desc] width [1 2] policy [:definite :detailed] values inputs] + (check-lookup-case! env permission traversal direction width policy values)))) + +(deftest expired-group-bans-change-arrow-lookups-without-a-write + (let [{:keys [conn users groups] :as env} (lookup-fixture) + disabled (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type + [:group :disabled :user]]) + writer (qualifiers/writer conn)] + (doseq [subject users group groups] + (staged/write! writer :create [:user subject disabled :group group] {:valid-until-ms 100})) + (let [db (ds/db conn) env (assoc env :db db :adapter (datascript-backend/basis-adapter db {}))] + (doseq [time [99 100] traversal [:forward :reverse]] + (let [options (assoc (lookup-options env :inherited traversal :asc 1 :definite) + :qualification (fixtures/qualified-request db time {})) + rows (lookup-fixtures/drain options) + expected (if (< time 100) [] (if (= traversal :forward) (:documents env) users))] + (is (= (set expected) (set (map :value rows)))) + (is (every? #(evidence/has? (:evidence %)) rows))))))) + +(deftest a-known-arrow-binding-is-completed-without-rechecking-its-target + (let [{:keys [qids groups member] :as env} (fixture) + x (evidence/with-certificate symbolic/x 100 true) + y (evidence/with-certificate symbolic/y 110 true) + semantic {[:via 0] true [:via 1] true [:target 0] x [:target 1] y} + invoker backend/direct-edge-invoker] + (doseq [permission [:view :direct_inherited]] + (let [options (assoc-in (options env permission) [:arrow-witness :evidence] x) + calls (atom []) + result (with-redefs [qualification/qualify (resolver qids semantic) + backend/direct-edge-invoker + (fn [adapter] + (let [invoke (invoker adapter)] + (fn [st s r rt o] + (when (= member r) (swap! calls conj o)) + (invoke st s r rt o))))] + (scalar/check-eids options))] + (is (= (evidence/combine :union x y) result)) + (is (= [(second groups)] @calls)) + (with-redefs [backend/scan-invoker (fn [& _] (fn [& _] (throw (ex-info "Decisive seed needs no scan" {}))))] + (is (= true (scalar/check-eids (assoc-in options [:arrow-witness :evidence] true)))) + (let [fault (evidence/fault :eacl.qualifier/invalid :missing)] + (is (= fault (scalar/check-eids (assoc-in options [:arrow-witness :evidence] fault)))))) + (is (= :arrow-witness-scope + (:reason (errors/error-data #(scalar/check-eids (assoc-in options [:arrow-witness :point 3] 999)))))) + (is (= :arrow-witness-scope + (:reason (errors/error-data #(scalar/check-eids + (update options :qualification + (fn [request] (qualification/request (assoc request :time 100))))))))) + (is (= :arrow-witness-binding + (:reason (errors/error-data #(scalar/check-eids (assoc-in options [:arrow-witness :partition] 1)))))) + (is (= :expired-arrow-witness + (:reason (errors/error-data #(scalar/check-eids (assoc-in options [:arrow-witness :evidence] + (evidence/with-certificate symbolic/x 99 true))))))))))) diff --git a/modules/eacl-datascript/test/eacl/operator/evaluator_test.cljc b/modules/eacl-datascript/test/eacl/operator/evaluator_test.cljc index 1b1894f5..e81bf52c 100644 --- a/modules/eacl-datascript/test/eacl/operator/evaluator_test.cljc +++ b/modules/eacl-datascript/test/eacl/operator/evaluator_test.cljc @@ -2,17 +2,30 @@ (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is testing]] [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as qualification-fixtures] [eacl.backend.v8 :as backend] [eacl.cache.key :as cache-key] [eacl.core :as eacl] [eacl.datascript.backend :as datascript-backend] [eacl.datascript.core :as datascript] [eacl.datascript.impl :as datascript-impl] + [eacl.datascript.qualifiers :as qualifiers] [eacl.datascript.schema :as datascript-schema] [eacl.execution :as execution] [eacl.operator.evaluator :as evaluator] [eacl.operator.plan :as plan] - [eacl.subproblem-cache :as subproblem])) + [eacl.subproblem-cache :as subproblem] + [eacl.relationships.staged :as staged])) + +(defn qualified-request [db time context] + (let [native (qualifiers/read-api)] + (qualification/request-from-adapter + (datascript-backend/basis-adapter db {}) + {:time time :context context + :evaluator (qualification-fixtures/portable-evaluator (atom 0)) + :basis {:source ((:source native) db) :revision (:max-tx db)}}))) (defn- test-exact-key [semantic] @@ -42,8 +55,11 @@ definition document { relation reader: user relation parent: group + relation banned: user permission view = reader & parent->active permission direct_inherited = reader & parent->member + permission inherited = parent->active - banned + permission inherited_direct = parent->member - banned }") (def shared-dag-schema @@ -266,3 +282,57 @@ recursive-plan (plan/seal-plan (:adapter env) [:folder :view])] (is (= :eacl.operator/recursive-plan-required (:type (error-data #(check env recursive-plan user folder))))))) + +(deftest qualified-direct-exclusion-keeps-residuals-and-ban-deadline + (let [user (object :user "qualified/user") + document (object :document "qualified/document") + schema (str "caveat enabled(flag bool) { flag }\n" direct-schema) + env (fixture schema [user document] + [(eacl/->Relationship user :reader document) + (eacl/->Relationship user :writer document) + (eacl/->Relationship user :banned document)]) + conn (:conn env) + writer (qualifiers/writer conn) + relation (fn [name] (ds/entid (ds/db conn) + [:eacl.relation/resource-type+relation-name+subject-type + [:document name :user]])) + caveat (ds/entid (ds/db conn) [:eacl.caveat/name "enabled"]) + identity (fn [name] [:user ((:eid env) user) (relation name) + :document ((:eid env) document)])] + (ds/transact! conn [{:db/id (relation :reader) :eacl.relation/caveats [caveat] + :eacl.relation/allows-unqualified? true}]) + (staged/write! writer :replace (identity :reader) {:caveat caveat}) + (staged/write! writer :replace (identity :banned) {:valid-until-ms 100}) + (let [db (ds/db conn) + env (assoc env :db db :adapter (datascript-backend/basis-adapter db {})) + sealed (plan/seal-plan (:adapter env) [:document :view]) + run (fn [time context] + (check env sealed user document {:qualification (qualified-request db time context)}))] + (is (= :no-permission (evidence/permissionship (run 99 {})))) + (is (= 100 (evidence/valid-until (run 99 {})))) + (is (= :conditional-permission (evidence/permissionship (run 100 {})))) + (is (= ["flag"] (evidence/missing-fields (run 100 {})))) + (is (true? (run 100 {"flag" true}))) + (is (false? (run 100 {"flag" false})))))) + +(deftest qualified-arrow-composes-via-and-target-evidence + (let [user (object :user "qualified/user") + group (object :group "qualified/group") + document (object :document "qualified/document") + env (fixture arrow-schema [user group document] + [(eacl/->Relationship user :reader document) + (eacl/->Relationship group :parent document) + (eacl/->Relationship user :member group)]) + conn (:conn env) writer (qualifiers/writer conn) + parent (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type + [:document :parent :group]])] + (staged/write! writer :replace + [:group ((:eid env) group) parent :document ((:eid env) document)] + {:valid-until-ms 100}) + (let [db (ds/db conn) + env (assoc env :db db :adapter (datascript-backend/basis-adapter db {}))] + (doseq [permission [:view :direct_inherited] time [99 100]] + (let [result (check env (plan/seal-plan (:adapter env) [:document permission]) user document + {:qualification (qualified-request db time {})})] + (is (= (< time 100) (evidence/has? result))) + (is (= (when (< time 100) 100) (evidence/valid-until result)))))))) diff --git a/modules/eacl-datascript/test/eacl/operator/lookup_evidence_test.cljc b/modules/eacl-datascript/test/eacl/operator/lookup_evidence_test.cljc new file mode 100644 index 00000000..de7b0ec7 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/operator/lookup_evidence_test.cljc @@ -0,0 +1,81 @@ +(ns eacl.operator.lookup-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.backend.direct-membership :as direct] + [eacl.engine.least-path :as least-path] + [eacl.operator.evaluator :as scalar] + [eacl.operator.batch-schedule :as schedule] + [eacl.operator.lookup :as lookup] + [eacl.operator.plan :as plan] + [eacl.operator.seekable-evidence-test :as fixtures])) + +(defn drain [options] + (loop [boundary nil pages 0 rows []] + (when (> pages 32) (throw (ex-info "Qualified cover did not progress" {}))) + (let [page (lookup/lookup-page (assoc options :boundary boundary)) + rows (into rows (:emissions page))] + (if (:has-more? page) + (recur (:resume-coords page) (inc pages) rows) + rows)))) + +(deftest general-cover-completes-conditional-nodes-before-emission + (let [{:keys [adapter user docs] :as env} (fixtures/fixture)] + (doseq [permission [:both :allowed :either] time [99 100] + context [{} {"flag" true} {"flag" false}] + policy [:definite :detailed] direction [:asc :desc] traversal [:forward :reverse]] + (let [options (assoc (fixtures/options env permission time context direction) + :direct-specializations? false :page-size 1 :candidate-window 2 + :result-policy policy :traversal traversal) + expected (into {} (keep (fn [doc] + (let [result (scalar/check-eids + {:adapter adapter :plan (:plan options) + :subject-type :user + :subject-eid (if (= :forward traversal) user doc) + :resource-eid (if (= :forward traversal) doc user) + :qualification (:qualification options)})] + (when (if (= :definite policy) + (evidence/has? result) (not (evidence/no? result))) + [doc (evidence/value result)])))) docs) + rows (drain options) + comparison (map #(least-path/compare-coords (:coords %1) (:coords %2)) rows (rest rows))] + (is (= expected (into {} (map (juxt :value (comp evidence/value :evidence))) rows))) + (is (= (count expected) (count rows))) + (is (every? (if (= :asc direction) neg? pos?) comparison)))))) + +(deftest local-exact-evidence-is-bounded-and-resolution-is-request-shared + (let [env (fixtures/fixture) + options (assoc (fixtures/options env :both 99 {} :asc) + :direct-specializations? false :page-size 1 :candidate-window 2 + :result-policy :detailed) + request (:qualification options) reads (atom {}) + observed (qualification/request + (assoc request :lookup (fn [eid] + (swap! reads update eid (fnil inc 0)) + ((:lookup request) eid))))] + (is (= 4 (count (drain (assoc options :qualification observed))))) + (is (seq @reads)) + (is (every? #(= 1 %) (vals @reads))) + (with-redefs [lookup/maximum-local-node-evidence 1] + (is (= :node-evidence-limit + (:reason (fixtures/error-data #(lookup/lookup-page options)))))) + (with-redefs [lookup/maximum-local-node-evidence 3 schedule/maximum-width 1] + (is (= 4 (:count (lookup/count-results options))))) + (is (= :max-values + (:limit (fixtures/error-data + #(lookup/lookup-page (assoc-in options [:traversal-limits :max-values] 1)))))))) + +(deftest a-generated-direct-node-is-not-probed-again-by-its-parent + (let [env (fixtures/fixture) + options (assoc (fixtures/options env :both 99 {} :asc) + :direct-specializations? false :page-size 10 :result-policy :detailed) + sealed (:plan options) root (get (plan/expression-roots sealed) [:doc :both]) + source (get-in sealed [:generators [:doc :both] root :source-node]) + source-relation (get-in sealed [:predicate-programs [:doc :both] source :descriptor :partitions 0 :relation-id]) + probes (atom []) dispatch direct/dispatch-edges] + (with-redefs [direct/dispatch-edges (fn [adapter requests] + (swap! probes into requests) + (dispatch adapter requests))] + (is (= 4 (count (:emissions (lookup/lookup-page options)))))) + (is (seq @probes)) + (is (not-any? #(= source-relation (get-in % [:descriptor :relation-eid])) @probes)))) diff --git a/modules/eacl-datascript/test/eacl/operator/lookup_test.cljc b/modules/eacl-datascript/test/eacl/operator/lookup_test.cljc index aab1ad10..9c600137 100644 --- a/modules/eacl-datascript/test/eacl/operator/lookup_test.cljc +++ b/modules/eacl-datascript/test/eacl/operator/lookup_test.cljc @@ -2,6 +2,7 @@ (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is testing]] [datascript.core :as ds] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl] [eacl.datascript.backend :as datascript-backend] [eacl.datascript.core :as datascript] @@ -502,10 +503,10 @@ (let [lifted (eacl/check-permission client query) after-unrelated (datascript/cache-stats client)] (is (true? (:allowed? lifted))) - (is (true? (:cached? lifted))) - (is (= (inc (:managed-hits before)) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? lifted))) + (is (= (cond-> (:managed-hits before) (not orchestration/*qualified-authorization-enabled?*) inc) (:managed-hits after-unrelated))) - (is (= (:misses before) (:misses after-unrelated))) + (is (= (cond-> (:misses before) orchestration/*qualified-authorization-enabled?* inc) (:misses after-unrelated))) (is (= (:stamp-failures before) (:stamp-failures after-unrelated)))) (eacl/create-relationship! diff --git a/modules/eacl-datascript/test/eacl/operator/recursive_test.cljc b/modules/eacl-datascript/test/eacl/operator/recursive_test.cljc index e3b8eb86..0d7d1ae5 100644 --- a/modules/eacl-datascript/test/eacl/operator/recursive_test.cljc +++ b/modules/eacl-datascript/test/eacl/operator/recursive_test.cljc @@ -3,18 +3,23 @@ :refer [deftest is testing]] [clojure.string :as str] [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] [eacl.cache.key :as cache-key] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl] [eacl.datascript.backend :as datascript-backend] [eacl.datascript.core :as datascript] [eacl.datascript.impl :as datascript-impl] + [eacl.datascript.qualifiers :as qualifiers] [eacl.datascript.schema :as datascript-schema] [eacl.engine.v8 :as engine] [eacl.execution :as execution] [eacl.operator.plan :as plan] + [eacl.operator.evaluator-test :as scalar-fixtures] [eacl.operator.recursive :as recursive] [eacl.operator-engine.oracle :as oracle] - [eacl.subproblem-cache :as subproblem])) + [eacl.subproblem-cache :as subproblem] + [eacl.relationships.staged :as staged])) (defn- test-exact-key [semantic] @@ -112,7 +117,7 @@ :entid->object-id (fn [snapshot internal-id] (:eacl/id (ds/entity snapshot internal-id)))})] - {:adapter adapter + {:conn conn :db db :adapter adapter :public-adapter public-adapter :client (datascript/make-client conn {}) :plan (plan/seal-plan adapter [:folder :allowed]) @@ -814,7 +819,7 @@ (is (true? (:allowed? cold))) (is (false? (:cached? cold))) (is (true? (:cached? warm))) - (is (pos? (:managed-entries before))) + (is (= (not orchestration/*qualified-authorization-enabled?*) (pos? (:managed-entries before)))) (is (zero? (:stamp-failures before))) (eacl/create-relationship! @@ -822,10 +827,10 @@ (let [lifted (eacl/check-permission client f0-query) after-unrelated (datascript/cache-stats client)] (is (true? (:allowed? lifted))) - (is (true? (:cached? lifted))) - (is (= (inc (:managed-hits before)) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? lifted))) + (is (= (cond-> (:managed-hits before) (not orchestration/*qualified-authorization-enabled?*) inc) (:managed-hits after-unrelated))) - (is (= (:misses before) (:misses after-unrelated))) + (is (= (cond-> (:misses before) orchestration/*qualified-authorization-enabled?* inc) (:misses after-unrelated))) (is (= (:stamp-failures before) (:stamp-failures after-unrelated)))) @@ -933,3 +938,64 @@ (get-in envelope [:edge :kind]))) (is (= recursive/checkpoint-version (get-in envelope [:edge :recursive-checkpoint-version]))))))) + +(deftest conditional-cycle-reaches-an-evidence-and-certificate-fixed-point + (let [alice (object :user "alice") + f0 (object :folder "f0") f1 (object :folder "f1") f2 (object :folder "f2") + relationships [(eacl/->Relationship alice :direct f0) + (eacl/->Relationship alice :eligible f1) + (eacl/->Relationship alice :eligible f2) + (eacl/->Relationship f0 :parent f1) + (eacl/->Relationship f1 :parent f2) + (eacl/->Relationship f2 :parent f1) + (eacl/->Relationship alice :banned f2)] + {:keys [conn eid]} (seed-schema (str "caveat enabled(flag bool) { flag }\n" recursive-schema) relationships) + writer (qualifiers/writer conn) + relation (fn [name type] (ds/entid (ds/db conn) + [:eacl.relation/resource-type+relation-name+subject-type [:folder name type]])) + caveat (ds/entid (ds/db conn) [:eacl.caveat/name "enabled"])] + (ds/transact! conn [(hash-map :db/id (relation :direct :user) + :eacl.relation/caveats [caveat] + :eacl.relation/allows-unqualified? true)]) + (staged/write! writer :replace [:user (eid alice) (relation :direct :user) :folder (eid f0)] {:caveat caveat}) + (staged/write! writer :replace [:folder (eid f0) (relation :parent :folder) :folder (eid f1)] {:valid-until-ms 110}) + (staged/write! writer :replace [:user (eid alice) (relation :banned :user) :folder (eid f2)] {:valid-until-ms 100}) + (let [db (ds/db conn) adapter (datascript-backend/basis-adapter db {}) + sealed (plan/seal-plan adapter [:folder :allowed]) + options (fn [time context] + {:adapter adapter :plan sealed :scope-identity :qualified-cycle + :qualification (scalar-fixtures/qualified-request db time context) + :candidates (mapv (fn [folder] {:direction :forward :subject-type :user + :subject-eid (eid alice) :resource-eid (eid folder)}) + [f0 f1 f2])}) + before (recursive/evaluate-many (options 99 {})) + after (recursive/evaluate-many (options 100 {})) + granted (recursive/evaluate-many (options 109 {"flag" true})) + expired (recursive/evaluate-many (options 110 {})) + kinds #(mapv evidence/permissionship (:decisions %))] + (is (= [:conditional-permission :conditional-permission :no-permission] (kinds before))) + (is (= [nil 110 100] (mapv evidence/valid-until (:decisions before)))) + (is (= [:conditional-permission :conditional-permission :conditional-permission] (kinds after))) + (is (= [nil 110 110] (mapv evidence/valid-until (:decisions after)))) + (is (every? evidence/has? (:decisions granted))) + (is (= [nil 110 110] (mapv evidence/valid-until (:decisions granted)))) + (is (= [:conditional-permission :no-permission :no-permission] (kinds expired))) + (let [store (subproblem/store)] + (binding [subproblem/*store* store subproblem/*exact-denotation-key-fn* test-exact-key] + (let [cold (recursive/evaluate-cached-many (options 99 {})) + warm (recursive/evaluate-cached-many (options 99 {})) + later (recursive/evaluate-cached-many (options 100 {})) + entries (get-in (subproblem/stats store) [:tiers :denotation :entries]) + fault (recursive/evaluate-cached-many (options 100 {"flag" "wrong-type"}))] + (is (= (:decisions before) (:decisions cold) (:decisions warm))) + (is (= 3 (get-in warm [:counters :point-cache-hits]))) + (is (= (:decisions after) (:decisions later))) + (is (every? evidence/fault? (:decisions fault))) + (is (= entries (get-in (subproblem/stats store) [:tiers :denotation :entries])))))) + (is (nil? (:checkpoint (recursive/evaluate-many (options 100 {"flag" "wrong-type"}))))) + (is (= (:decisions after) + (:decisions (recursive/evaluate-many (assoc (options 100 {}) :checkpoint (:checkpoint after)))))) + (is (= :invalid-checkpoint + (:reason (error-data #(recursive/evaluate-many (assoc (options 101 {}) :checkpoint (:checkpoint after))))))) + (is (= :invalid-checkpoint + (:reason (error-data #(recursive/evaluate-many (assoc (options 100 {"flag" true}) :checkpoint (:checkpoint after)))))))))) diff --git a/modules/eacl-datascript/test/eacl/operator/seekable_evidence_test.cljc b/modules/eacl-datascript/test/eacl/operator/seekable_evidence_test.cljc new file mode 100644 index 00000000..078061d6 --- /dev/null +++ b/modules/eacl-datascript/test/eacl/operator/seekable_evidence_test.cljc @@ -0,0 +1,164 @@ +(ns eacl.operator.seekable-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.backend.direct-membership :as direct] + [eacl.datascript.backend :as backend] + [eacl.datascript.impl :as impl] + [eacl.datascript.qualifiers :as qualifiers] + [eacl.datascript.schema :as schema] + [eacl.operator.cover-plan :as cover] + [eacl.operator.evaluator :as scalar] + [eacl.operator.evaluator-test :as fixtures] + [eacl.operator.lookup :as lookup] + [eacl.operator.plan :as plan] + [eacl.operator.seekable :as seekable] + [eacl.relationships.edge :as edge] + [eacl.relationships.staged :as staged])) + +(defn fixture [] + (let [conn (schema/create-conn {})] + (schema/write-schema! conn + "caveat enabled(flag bool) { flag } + definition user {} + definition doc { + relation reader: user + relation writer: user + relation banned: user + permission both = reader & writer + permission allowed = reader - banned + permission either = both + allowed + }") + (ds/transact! conn (mapv #(hash-map :eacl/id (str %)) (range 5))) + (let [ids (mapv #(ds/entid (ds/db conn) [:eacl/id (str %)]) (range 5)) + user (first ids) docs (subvec ids 1) + relations (into {} (for [name [:reader :writer :banned]] + [name (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type [:doc name :user]])])) + caveat (ds/entid (ds/db conn) [:eacl.caveat/name "enabled"]) + writer (qualifiers/writer conn) + write! (fn [doc name value] + (staged/write! writer :create [:user user (relations name) :doc (nth docs doc)] value) + (staged/write! writer :create [:user (nth docs doc) (relations name) :doc user] value))] + (ds/transact! conn (mapv #(hash-map :db/id % :eacl.relation/caveats [caveat] + :eacl.relation/allows-unqualified? true) (vals relations))) + (doseq [doc (range 4)] + (write! doc :reader (when (zero? doc) {:caveat caveat})) + (write! doc :writer (case doc 1 {:caveat caveat} 2 {:valid-until-ms 100} nil))) + (write! 1 :banned {:caveat caveat :valid-until-ms 100}) + (write! 2 :banned {:valid-until-ms 100}) + (let [db (ds/db conn)] + {:conn conn :db db :adapter (backend/basis-adapter db {}) :user user :docs docs})))) + +(defn options [{:keys [adapter db user]} permission time context direction] + (let [permission [:doc permission] sealed (plan/seal-plan adapter permission)] + {:adapter adapter :plan sealed :cover-plan (cover/seal-plan adapter sealed permission) + :specialization-node (get (plan/expression-roots sealed) permission) + :traversal :forward :subject-type :user :anchor-eid user :order-direction direction + :qualification (fixtures/qualified-request db time context) + :traversal-limits {:physical-chunk-size 1} :width 1})) + +(defn drain [options] + (loop [boundary nil result []] + (let [page (seekable/page (assoc options :boundary boundary)) + emissions (:emissions page) + result (into result emissions)] + (if (:exhausted? page) result + (recur (:coords (peek emissions)) result))))) + +(deftest direct-specializations-carry-exact-qualified-evidence + (let [{:keys [adapter user docs] :as env} (fixture)] + (doseq [permission [:both :allowed] time [99 100] context [{} {"flag" true} {"flag" false}] + direction [:asc :desc] traversal [:forward :reverse]] + (let [options (assoc (options env permission time context direction) :traversal traversal) + expected (->> (if (= direction :asc) docs (rseq docs)) + (keep (fn [doc] + (let [answer (scalar/check-eids + {:adapter adapter :plan (:plan options) + :subject-type :user + :subject-eid (if (= traversal :forward) user doc) + :resource-eid (if (= traversal :forward) doc user) + :qualification (:qualification options)})] + (when-not (evidence/no? answer) [doc answer])))) vec) + actual (drain options)] + (is (= (mapv first expected) (mapv :value actual))) + (is (= (mapv (comp evidence/value second) expected) (mapv (comp evidence/value :evidence) actual))) + (is (every? #(evidence/complete? (:evidence %)) actual)) + (is (every? #(evidence/before? time (evidence/valid-until (:evidence %))) actual)))))) + +(defn error-data [f] + (try (f) nil (catch #?(:clj Throwable :cljs :default) error (ex-data error)))) + +(deftest qualified-heads-share-request-resolution-and-keep-physical-bounds + (let [env (fixture) base (options env :both 100 {"flag" false} :asc) + request (:qualification base) reads (atom {}) + observed (qualification/request + (assoc request :lookup (fn [eid] + (swap! reads update eid (fnil inc 0)) + ((:lookup request) eid))))] + (is (= [(last (:docs env))] (mapv :value (drain (assoc base :qualification observed))))) + (is (seq @reads)) + (is (every? #(= 1 %) (vals @reads))) + (is (= :fetched-values + (:dimension (error-data #(seekable/page (assoc-in base [:traversal-limits :max-values] 2)))))) + (is (= :commands + (:dimension (error-data #(seekable/page (assoc-in base [:traversal-limits :max-commands] 1)))))))) + +(deftest malformed-heads-fault-instead-of-becoming-absent + (let [{:keys [conn user docs] :as env} (fixture) + relation (ds/entid (ds/db conn) [:eacl.relation/resource-type+relation-name+subject-type [:doc :reader :user]]) + qid (edge/qualifier-id (impl/direct-edge (ds/db conn) :user user relation :doc (first docs)))] + (ds/transact! conn [[:db.fn/retractEntity qid]]) + (let [db (ds/db conn) env (assoc env :db db :adapter (backend/basis-adapter db {}))] + (is (= :eacl.authorization/evaluation-failure + (:type (error-data #(seekable/page (options env :both 100 {} :asc))))))))) + +(deftest lookup-and-count-project-exact-generator-evidence-without-rechecking + (let [env (fixture)] + (doseq [permission [:both :allowed] time [99 100] traversal [:forward :reverse]] + (let [base (assoc (options env permission time {} :asc) :traversal traversal + :page-size 10 :candidate-window 16) + raw (drain base) + definite (filterv #(evidence/has? (:evidence %)) raw) + conditional (- (count raw) (count definite))] + (with-redefs [direct/dispatch-edges (fn [& _] (throw (ex-info "Generator already proved its node" {})))] + (is (= (mapv :value definite) (mapv :value (:emissions (lookup/lookup-page base))))) + (is (= (mapv :value raw) (mapv :value (:emissions (lookup/lookup-page (assoc base :result-policy :detailed)))))) + (is (= (count definite) (:count (lookup/count-results base)))) + (let [counted (lookup/count-results (assoc base :result-policy :detailed))] + (is (= (count raw) (:count counted))) + (is (= (count definite) (:definite-count counted))) + (is (= conditional (:conditional-count counted))))))))) + +(deftest detailed-count-cap-excludes-the-sentinel-from-category-counts + (let [env (fixture) base (assoc (options env :both 99 {} :asc) :result-policy :detailed) + one (lookup/count-results (assoc base :count-limit 1)) + zero (lookup/count-results (assoc base :count-limit 0))] + (is (= {:count 1 :truncated? true :definite-count 0 :conditional-count 1} + (select-keys one [:count :truncated? :definite-count :conditional-count]))) + (is (= {:count 0 :truncated? true :definite-count 0 :conditional-count 0} + (select-keys zero [:count :truncated? :definite-count :conditional-count]))))) + +(deftest conditional-filtering-preserves-bounded-empty-page-progress + (let [env (fixture)] + (doseq [policy [:definite :detailed] direction [:asc :desc] traversal [:forward :reverse]] + (let [base (assoc (options env :both 99 {} direction) + :result-policy policy :traversal traversal + :page-size 1 :candidate-window 1) + expected (filterv #(or (= policy :detailed) (evidence/has? (:evidence %))) + (drain base)) + actual (loop [boundary nil pages 0 results []] + (when (> pages 8) (throw (ex-info "Bounded lookup did not progress" {}))) + (let [page (lookup/lookup-page (assoc base :boundary boundary)) + results (into results (:emissions page))] + (if (:has-more? page) + (recur (:resume-coords page) (inc pages) results) + results)))] + (is (= (mapv :value expected) (mapv :value actual))) + (is (= (mapv (comp evidence/value :evidence) expected) + (mapv (comp evidence/value :evidence) actual))))))) + +(deftest invalid-result-policy-fails-before-scanning + (let [base (assoc (options (fixture) :both 99 {} :asc) :page-size 1)] + (doseq [policy [nil :truthy] operation [lookup/lookup-page lookup/count-results]] + (is (= :result-policy (:reason (error-data #(operation (assoc base :result-policy policy))))))))) diff --git a/modules/eacl-datascript/test/eacl/operator/vector_evaluator_test.cljc b/modules/eacl-datascript/test/eacl/operator/vector_evaluator_test.cljc index d147466b..3bb40bb8 100644 --- a/modules/eacl-datascript/test/eacl/operator/vector_evaluator_test.cljc +++ b/modules/eacl-datascript/test/eacl/operator/vector_evaluator_test.cljc @@ -2,16 +2,23 @@ (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] [datascript.core :as ds] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.evidence-test :as evidence-fixtures] + [eacl.backend.direct-membership :as direct] [eacl.cache.key :as cache-key] [eacl.core :as eacl] [eacl.datascript.backend :as datascript-backend] [eacl.datascript.core :as datascript] [eacl.datascript.impl :as datascript-impl] + [eacl.datascript.qualifiers :as qualifiers] [eacl.datascript.schema :as datascript-schema] [eacl.operator.evaluator :as scalar] + [eacl.operator.evaluator-test :as scalar-fixtures] [eacl.operator.plan :as plan] [eacl.operator.vector-evaluator :as vector-evaluator] - [eacl.subproblem-cache :as subproblem])) + [eacl.subproblem-cache :as subproblem] + [eacl.relationships.staged :as staged])) (defn- test-exact-key [semantic] @@ -62,7 +69,7 @@ (ds/db conn) {:operation :touch :relationship relationship}))) (let [db (ds/db conn) eid #(ds/entid db (:id %))] - {:adapter (datascript-backend/basis-adapter db {}) + {:conn conn :db db :adapter (datascript-backend/basis-adapter db {}) :user (first users) :documents documents :eid eid}))) @@ -434,3 +441,114 @@ (is (>= (:calls different) 2)) (is (zero? (:failures (:stats identical) 0))) (is (zero? (:failures (:stats different) 0)))))) + +(deftest qualified-vectors-retain-alignment-and-exact-cache-scope + (let [{:keys [conn user documents eid]} (fixture) + documents (vec (take 8 documents))] + (datascript-schema/write-schema! conn (str "caveat enabled(flag bool) { flag }\n" schema)) + (let [relation (fn [name] (ds/entid (ds/db conn) + [:eacl.relation/resource-type+relation-name+subject-type + [:document name :user]])) + caveat (ds/entid (ds/db conn) [:eacl.caveat/name "enabled"]) + writer (qualifiers/writer conn)] + (ds/transact! conn [{:db/id (relation :a) :eacl.relation/caveats [caveat] + :eacl.relation/allows-unqualified? true}]) + (doseq [[index document] (map-indexed vector documents)] + (staged/write! writer :replace [:user (eid user) (relation :a) :document (eid document)] + {:caveat caveat}) + (when (zero? (mod index 5)) + (staged/write! writer :replace [:user (eid user) (relation :banned) :document (eid document)] + {:valid-until-ms 100}))) + (let [db (ds/db conn) + adapter (datascript-backend/basis-adapter db {}) + sealed (plan/seal-plan adapter [:document :view]) + candidates (mapv (fn [document] + {:direction :forward :subject-type :user :subject-eid (eid user) + :resource-type :document :resource-eid (eid document)}) documents) + store (subproblem/store) + run (fn [time context] + (let [stats (atom {}) + options {:adapter adapter :plan sealed :candidates candidates :scope-identity :qualified + :qualification (scalar-fixtures/qualified-request db time context)} + result (binding [subproblem/*store* store + subproblem/*exact-denotation-key-fn* test-exact-key + vector-evaluator/*vector-stats* stats] + (vector-evaluator/check-cached-many-eids options))] + {:result result :stats @stats})) + before (run 99 {}) + warm (run 99 {}) + after (run 100 {}) + granted (run 100 {"flag" true}) + denied (run 100 {"flag" false}) + prior-entries (get-in (subproblem/stats store) [:tiers :denotation :entries]) + fault (run 100 {"flag" "wrong-type"})] + (is (= (:result before) (:result warm))) + (is (= 8 (get-in warm [:stats :point-cache-hits]))) + (is (= 8 (get-in after [:stats :point-cache-misses]))) + (is (= [:no-permission :no-permission :conditional-permission :conditional-permission + :conditional-permission :no-permission :conditional-permission :no-permission] + (mapv evidence/permissionship (:result before)))) + (is (= :conditional-permission (evidence/permissionship (first (:result after))))) + (is (= [true false true true true false true false] (:result granted))) + (is (every? false? (:result denied))) + (is (every? evidence/fault? (:result fault))) + (is (= prior-entries (get-in (subproblem/stats store) [:tiers :denotation :entries]))) + (let [reverse-options {:adapter adapter :plan sealed + :candidates (mapv #(assoc % :direction :reverse) candidates) + :qualification (scalar-fixtures/qualified-request db 100 {})}] + (is (= (:result after) (vector-evaluator/check-many-eids reverse-options)))))))) + +(deftest exact-evidence-witnesses-avoid-rechecking-proven-nodes + (let [{:keys [adapter db user documents eid]} (fixture) + sealed (plan/seal-plan adapter [:document :view]) + root-key [[:document :view] (get (plan/expression-roots sealed) [:document :view])] + candidate {:direction :forward :subject-type :user :subject-eid (eid user) + :resource-type :document :resource-eid (eid (nth documents 2))} + request (scalar-fixtures/qualified-request db 99 {}) + options {:adapter adapter :plan sealed :qualification request :candidates [candidate]} + scope (qualification/exact-reuse-identity request)] + (doseq [leaf (list true false evidence-fixtures/x + (evidence/with-certificate true 100 true) + (evidence/with-certificate evidence-fixtures/x 100 false) + (evidence/fault :test/failure :invalid))] + (let [expected (with-redefs [qualification/qualify (fn [_ _ edge] (if edge leaf false))] + (vector-evaluator/check-many-eids options)) + witnessed (assoc candidate :evidence-witnesses {root-key (first expected)})] + (with-redefs [direct/dispatch-edges (fn [& _] (throw (ex-info "Already proved" {})))] + (is (= expected (vector-evaluator/check-many-eids + (assoc options :candidates [witnessed] :witness-scope scope))))))) + (let [witnessed (assoc candidate :evidence-witnesses {root-key true}) + options (assoc options :candidates [witnessed] :witness-scope scope)] + (doseq [changed [(assoc options :witness-scope nil) + (assoc options :qualification (scalar-fixtures/qualified-request db 100 {})) + (assoc options :qualification (scalar-fixtures/qualified-request db 99 {"flag" true})) + (assoc options :qualification (qualification/request (assoc request :basis {:source :another :revision 1}))) + (assoc options :qualification (qualification/request (assoc request :evaluator nil)))]] + (is (= :witness-scope (:reason (error-data #(vector-evaluator/check-many-eids changed)))))) + (is (= :qualified-witness-required + (:reason (error-data #(vector-evaluator/check-many-eids (dissoc options :qualification)))))) + (is (= :expired-witness + (:reason (error-data #(vector-evaluator/check-many-eids + (assoc options :candidates [(assoc candidate :evidence-witnesses + {root-key (evidence/with-certificate true 99 true)})])))))) + (with-redefs [vector-evaluator/maximum-evidence-witnesses 0] + (is (= :witness-limit (:reason (error-data #(vector-evaluator/check-many-eids options)))))) + (with-redefs [vector-evaluator/maximum-evidence-witness-bytes 0] + (is (= :witness-size (:reason (error-data #(vector-evaluator/check-many-eids options)))))) + (let [store (subproblem/store)] + (binding [subproblem/*store* store subproblem/*exact-denotation-key-fn* test-exact-key] + (is (= [true] (vector-evaluator/check-cached-many-eids options))) + (let [leaf-key [[:document :view] + (first (remove #{(second root-key)} + (keys (get-in sealed [:predicate-programs [:document :view]]))))] + fault (evidence/fault :test/failure :invalid)] + (with-redefs [direct/dispatch-edges (fn [& _] (throw (ex-info "Fault already encountered" {})))] + (is (= [fault] (vector-evaluator/check-cached-many-eids + (assoc options :candidates [(assoc candidate :evidence-witnesses {leaf-key fault})])))))) + (is (= :witness-scope + (:reason (error-data #(vector-evaluator/check-cached-many-eids + (dissoc options :witness-scope))))))))) + (is (= :invalid-witness-node + (:reason (error-data #(vector-evaluator/check-many-eids + (assoc options :witness-scope scope + :candidates [(assoc candidate :evidence-witnesses {[[:absent :view] 0] true})])))))))) diff --git a/modules/eacl-datascript/test/eacl/operator_engine/union_only_baseline_test.clj b/modules/eacl-datascript/test/eacl/operator_engine/union_only_baseline_test.clj index 4688af8d..55be3f1c 100644 --- a/modules/eacl-datascript/test/eacl/operator_engine/union_only_baseline_test.clj +++ b/modules/eacl-datascript/test/eacl/operator_engine/union_only_baseline_test.clj @@ -9,6 +9,8 @@ [datascript.core :as ds] [eacl.baseline.capture :as baseline] [eacl.cache :as cache] + [eacl.authorization.temporal :as temporal] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl] [eacl.cursor :as cursor] [eacl.datascript.core :as datascript] @@ -171,10 +173,21 @@ (:behavior (read-baseline-index))] (doseq [[fixture-key expected-digest] fixture-digests] (testing (name fixture-key) - (is (= expected-digest - (secure/canonical-digest - digest-domain - (baseline/capture-fixture fixture-key)))))))) + (let [captured (baseline/capture-fixture fixture-key) + legacy-stale (:stale-basis (baseline/read-snapshot fixture-key))] + (when orchestration/*qualified-authorization-enabled?* + (is (= (if (= :no-cursor (:outcome legacy-stale)) + legacy-stale + {:outcome :error :resumed-page-size nil + :detail {:outcome :error :error-keys [:eacl.pagination/stale-cursor :eacl.pagination/stale-cursor]}}) + (:stale-basis captured)))) + ;; Preserve the frozen order/denotation/error digest. The qualified + ;; epoch's exact-basis continuation change is asserted separately. + (is (= expected-digest + (secure/canonical-digest + digest-domain + (cond-> captured orchestration/*qualified-authorization-enabled?* + (assoc :stale-basis legacy-stale)))))))))) (defn- storage-nine-baseline "The frozen fixture predates one bootstrap transaction and the explicit ABI @@ -198,9 +211,30 @@ (deftest decoded-union-only-cursor-semantics-test (let [expected (storage-nine-baseline (read-cursor-snapshot)) - actual (capture-cursor-payloads)] - (is (= (remove-basis-local-coordinates expected) - (remove-basis-local-coordinates actual))) + actual (capture-cursor-payloads) + comparison + (fn [snapshot] + (cond-> (remove-basis-local-coordinates snapshot) + orchestration/*qualified-authorization-enabled?* + (update :fixtures + (fn [fixtures] + (into {} (for [[fixture directions] fixtures] + [fixture (into {} (for [[direction payload] directions] + [direction (update payload :cursor-common dissoc + :scope :frame :closure-digest :qualification-temporal)]))]))))))] + (when orchestration/*qualified-authorization-enabled?* + (doseq [[fixture directions] (:fixtures actual) + [direction payload] directions + :let [common (:cursor-common payload) + certificate (:qualification-temporal common)]] + (is (= :exact-basis (get-in common [:frame :mode]))) + (is (= (:native-revision common) (get-in common [:frame :native-revision]))) + (is (temporal/cursor-certificate-valid? certificate)) + (is (= :live (:mode certificate))) + (is (true? (:complete? certificate))) + (is (nil? (:valid-until-ms certificate))) + (is (not= (:scope common) (get-in expected [:fixtures fixture direction :cursor-common :scope]))))) + (is (= (comparison expected) (comparison actual))) (is (= (coordinate-shapes expected) (coordinate-shapes actual))))) diff --git a/modules/eacl-datomic/README.md b/modules/eacl-datomic/README.md index ee4b3226..ad015606 100644 --- a/modules/eacl-datomic/README.md +++ b/modules/eacl-datomic/README.md @@ -120,7 +120,7 @@ endpoint identity CAS. For high-degree targets, prefer batched `repair-tx-batches` for existing damage. ```clojure -{:deps {dev.eacl/eacl-datomic {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl-datomic {:mvn/version "9.0.0-SNAPSHOT"}}} ``` Its POM depends on `dev.eacl/eacl` at the exact same version, so consumers do @@ -156,10 +156,12 @@ mutation rules, see the ## Relationship storage 9 -This EACL v8 adapter uses five-slot endpoint pairs with a trailing nullable -`qualifier-eid`. This phase writes `nil` and raises `:eacl/unsupported-qualifier` -when serving encounters a qualifier. Upgrades are explicit and restartable; -ordinary client construction requires a completed target store. Follow the -[7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before starting clients. +This adapter uses five-slot endpoint pairs with a trailing nullable +`qualifier-eid`. V8 writes only `nil` and rejects non-nil qualifiers. V9 adds +[Caveats and expiring Relationships](../../docs/caveats.md) through coordinated +qualified activation; older readers must be drained first. Upgrades are explicit +and restartable, and client construction requires a completed target store. +Follow the [7-to-9 operator guide](../../docs/migration-v7-to-v9.md) before +starting clients, then the v9 serving rollout guide before qualified writes. Use `(eacl.datomic.schema/install! conn)` to bootstrap a fresh native database. diff --git a/modules/eacl-datomic/src/eacl/datomic/backend.clj b/modules/eacl-datomic/src/eacl/datomic/backend.clj index fb6214d5..9945e4a0 100644 --- a/modules/eacl-datomic/src/eacl/datomic/backend.clj +++ b/modules/eacl-datomic/src/eacl/datomic/backend.clj @@ -2,6 +2,7 @@ "Datomic's storage-specific implementation of the shared v8 snapshot adapter. Authorization graph algorithms remain outside this namespace." (:require [datomic.api :as d] + [eacl.authorization.data :as qualification-data] [eacl.backend.source :as source] [eacl.backend.v8 :as backend] [eacl.datomic.db :as ddb] @@ -9,7 +10,9 @@ (:import [java.util.concurrent Future])) (def adapter-capabilities - {:cursor #{:forward :reverse :opaque :authenticated :encrypted} + {:qualification #{qualification-data/capability} + :qualified-publication #{:atomic-inline-v1} + :cursor #{:forward :reverse :opaque :authenticated :encrypted} :cache-proofs #{:ordered-generations :snapshot-bound :database-visible} :runtime #{:clj}}) @@ -380,6 +383,13 @@ (fn [subject-type subject-id relation-id resource-type resource-id] (ddb/direct-match? db subject-type subject-id relation-id resource-type resource-id)) + :direct-edge + (fn [subject-type subject-id relation-id resource-type resource-id] + (ddb/direct-edge db subject-type subject-id relation-id resource-type resource-id)) + :qualification-data + (fn [eid] + (qualification-data/collect eid (d/datoms db :eavt eid) + #(get (d/entity db %) :db/ident) true)) :all-permission-nodes (fn [] (ddb/all-permission-nodes db)) :proof-frame (fn [relation-ids] diff --git a/modules/eacl-datomic/src/eacl/datomic/core.clj b/modules/eacl-datomic/src/eacl/datomic/core.clj index 772e44b7..cf1c7b4d 100644 --- a/modules/eacl-datomic/src/eacl/datomic/core.clj +++ b/modules/eacl-datomic/src/eacl/datomic/core.clj @@ -10,6 +10,7 @@ [eacl.core :as eacl] [eacl.datomic.backend :as datomic-backend] [eacl.datomic.impl :as impl] + [eacl.datomic.qualifiers :as qualifiers] [eacl.datomic.impl.indexed :as indexed] [eacl.datomic.schema :as schema] [eacl.datomic.storage :as target-storage] @@ -110,6 +111,9 @@ (def ^:private api {:backend-id :datomic + :qualified-writer #'qualifiers/writer + :qualified-publication-capability #'qualifiers/publication-capability + :qualified-plan #'qualifiers/plan :db d/db :entid d/entid :default-entid->object-id @@ -140,12 +144,14 @@ db (impl/stamp-relation-versions tx-data))) :schema {:read-schema schema/read-schema + :read-authorization-schema schema/read-authorization-schema :generation indexed/schema-version :plan-replacement schema/plan-schema-replacement :write-schema! write-schema!} :impl {:validate-relationship-operation! impl/validate-relationship-operation! + :relationship-publication-input #'impl/relationship-publication-input :relationship-relation-id impl/relationship-relation-id :relation-coordinate relation-coordinate :tx-update-relationship impl/tx-update-relationship diff --git a/modules/eacl-datomic/src/eacl/datomic/db.clj b/modules/eacl-datomic/src/eacl/datomic/db.clj index f99838e9..b9568682 100644 --- a/modules/eacl-datomic/src/eacl/datomic/db.clj +++ b/modules/eacl-datomic/src/eacl/datomic/db.clj @@ -2,6 +2,7 @@ "Datomic-only entity, schema-definition, and ordered adjacency operations." (:require [datomic.api :as d] [eacl.relationships.storage :as relationship-storage] + [eacl.relationships.edge :as edge] [eacl.relationships.endpoint-pair :as endpoint-pair])) (defn relationship-identity-datoms @@ -34,7 +35,7 @@ entity, strictly after an exclusive bound or from an inclusive one. Lazy: the routed read seam realizes exactly the chunk it asked for." [db endpoint-id attr prefix cursor-or-options] - (let [{:keys [direction bound-eid inclusive-bound?]} + (let [{:keys [direction bound-eid inclusive-bound? include-qualifier?]} (relationship-storage/normalize-scan-options cursor-or-options) attr-id (d/entid db attr) [p0 p1 p2] prefix @@ -54,9 +55,9 @@ (= p0 (nth value 0)) (= p1 (nth value 1)) (= p2 (nth value 2)))))) - true endpoint-pair/checked-datoms + true (#(endpoint-pair/checked-datoms % include-qualifier?)) skip-bound? (drop-while #(= bound-eid (nth (:v %) 3))) - true (map #(nth (:v %) 3))))) + true (map (if include-qualifier? edge/from-datom #(nth (:v %) 3)))))) (defn subject->resources [db subject-type subject-id relation-id resource-type cursor-or-options] @@ -134,8 +135,38 @@ db subject-id relationship-storage/forward-attribute (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)))))) +(defn direct-edge + "Stored compact edge or nil, prior to request qualification." + [db subject-type subject-id relation-id resource-type resource-id] + (some-> (first (endpoint-pair/checked-datoms + (relationship-identity-datoms + db subject-id relationship-storage/forward-attribute + (endpoint-pair/forward-value subject-type relation-id resource-type resource-id)) + true)) + edge/from-datom)) + (defn schema-version [db] (when (d/entid db :eacl/id) (some-> (d/entity db [:eacl/id "schema-string"]) :eacl/schema-version))) + +(defn entity-facts [database eid] + (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) (d/datoms database :eavt eid))) + +(defn entity-data [database eid] + (let [rows (entity-facts database eid)] + (when (seq rows) + (reduce (fn [result [a v]] + (let [attribute (:db/ident (d/entity database a))] + (if (= :eacl.relation/caveats attribute) + (update result attribute (fnil conj #{}) v) (assoc result attribute v)))) + {:db/id eid} rows)))) + + +(defn avet-tuple-prefix + "All stored endpoint rows in one typed Relation partition, including qualifiers." + [database attribute prefix] + (when-let [attribute-id (d/entid database attribute)] + (take-while #(and (= attribute-id (:a %)) (endpoint-pair/value-prefix? (:v %) prefix)) + (d/seek-datoms database :avet attribute-id (into prefix [0 nil]))))) diff --git a/modules/eacl-datomic/src/eacl/datomic/impl.clj b/modules/eacl-datomic/src/eacl/datomic/impl.clj index cf100b02..d4fbeaab 100644 --- a/modules/eacl-datomic/src/eacl/datomic/impl.clj +++ b/modules/eacl-datomic/src/eacl/datomic/impl.clj @@ -10,6 +10,8 @@ [eacl.engine.relationships :as relationship-engine] [eacl.engine.v8 :as engine] [eacl.relationships.endpoint-pair :as endpoint-pair] + [eacl.relationships.edge :as edge] + [eacl.relationships.inspection :as inspection] [eacl.relationships.filters :as relationship-filters] [eacl.relationships.mutations :as relationship-mutations] [eacl.relationships.storage :as relationship-storage] @@ -252,6 +254,14 @@ :resource-identity-guard (endpoint-identity-guard db :resource resource-eid resource)})) +(defn relationship-publication-input + "Resolves the same endpoint identities and commit guards as ordinary writes." + [db relationship] + (let [resolved (resolve-relationship db relationship {})] + {:relationship (mapv resolved [:subject-type :subject-eid :relation-eid :resource-type :resource-eid]) + :identity-guards [(:subject-identity-guard resolved) + (:resource-identity-guard resolved)]})) + (defn- relationship-tuple [{:keys [subject-type relation-eid resource-type resource-eid]}] (endpoint-pair/forward-value @@ -353,37 +363,39 @@ tx-data #{:db/add :db/retract})) (defn- endpoint-datoms - [db endpoint attr prefix cursor-eid direction] - (let [attr-eid (d/entid db attr) - bound (endpoint-pair/seek-bound prefix cursor-eid direction Long/MAX_VALUE) - datoms ((case direction - :asc d/seek-datoms - :desc d/rseek-datoms) - db :eavt endpoint attr-eid bound)] - (endpoint-pair/checked-datoms - (take-while - (fn [{:keys [e a v]}] - (and (== endpoint e) - (== attr-eid a) - (endpoint-pair/value-prefix? v prefix))) - datoms)))) + ([db endpoint attr prefix cursor-eid direction] (endpoint-datoms db endpoint attr prefix cursor-eid direction false)) + ([db endpoint attr prefix cursor-eid direction include-qualifier?] + (let [attr-eid (d/entid db attr) + bound (endpoint-pair/seek-bound prefix cursor-eid direction Long/MAX_VALUE) + datoms ((case direction + :asc d/seek-datoms + :desc d/rseek-datoms) + db :eavt endpoint attr-eid bound)] + (endpoint-pair/checked-datoms + (take-while + (fn [{:keys [e a v]}] + (and (== endpoint e) + (== attr-eid a) + (endpoint-pair/value-prefix? v prefix))) + datoms) include-qualifier?)))) (defn- global-endpoint-datoms - [db attr prefix cursor-eid cursor-endpoint direction] - (let [attr-eid (d/entid db attr) - bound (endpoint-pair/seek-bound prefix cursor-eid direction Long/MAX_VALUE) - components (cond-> [attr-eid bound] - cursor-endpoint (conj cursor-endpoint)) - datoms (apply (case direction - :asc d/seek-datoms - :desc d/rseek-datoms) - db :avet components)] - (endpoint-pair/checked-datoms - (take-while - (fn [{:keys [a v]}] - (and (== attr-eid a) - (endpoint-pair/value-prefix? v prefix))) - datoms)))) + ([db attr prefix cursor-eid cursor-endpoint direction] (global-endpoint-datoms db attr prefix cursor-eid cursor-endpoint direction false)) + ([db attr prefix cursor-eid cursor-endpoint direction include-qualifier?] + (let [attr-eid (d/entid db attr) + bound (endpoint-pair/seek-bound prefix cursor-eid direction Long/MAX_VALUE) + components (cond-> [attr-eid bound] + cursor-endpoint (conj cursor-endpoint)) + datoms (apply (case direction + :asc d/seek-datoms + :desc d/rseek-datoms) + db :avet components)] + (endpoint-pair/checked-datoms + (take-while + (fn [{:keys [a v]}] + (and (== attr-eid a) + (endpoint-pair/value-prefix? v prefix))) + datoms) include-qualifier?)))) (defn read-relationships ([db filters] @@ -396,7 +408,8 @@ ;; present-but-nil anchor throws instead of widening the read. (when-not relationship-filters/*validated-request?* (relationship-filters/validate! filters)) - (let [relation-defs (all-relation-defs db) + (let [include-qualifier? (true? (:include-qualifier? window-options)) + relation-defs (all-relation-defs db) subject-id (:subject/id filters) resource-id (:resource/id filters) ;; object-eid, not d/entid: a raw-impl caller passing a string id got a @@ -418,15 +431,17 @@ {:resource/id resource-id})) :else - (letfn [(relationship-row [spec subject-id resource-id] + (letfn [(relationship-row [spec subject-id resource-id qualifier-id] {:spec-idx (:idx spec) :subject-id subject-id :resource-id resource-id :relationship - (eacl/->Relationship - (spice-object (:subject-type spec) subject-id) - (:relation-name spec) - (spice-object (:resource-type spec) resource-id))}) + (inspection/row + (eacl/->Relationship + (spice-object (:subject-type spec) subject-id) + (:relation-name spec) + (spice-object (:resource-type spec) resource-id)) + (:relation-id spec) qualifier-id)}) (drop-until-beyond-cursor [spec cursor direction rows] (drop-while #(not @@ -436,14 +451,17 @@ (exact-match-row [spec cursor direction] (let [row (when (and (:subject-id spec) (:resource-id spec)) - (when - (db/direct-match? db (:subject-type spec) (:subject-id spec) - (:relation-id spec) (:resource-type spec) (:resource-id spec)) - (relationship-row - spec (:subject-id spec) (:resource-id spec))))] + (when-let [compact + (if include-qualifier? + (db/direct-edge db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (when (db/direct-match? db (:subject-type spec) (:subject-id spec) + (:relation-id spec) (:resource-type spec) (:resource-id spec)) + (:resource-id spec)))] + (relationship-row spec (:subject-id spec) (:resource-id spec) + (edge/qualifier-id compact))))] (if row - (drop-until-beyond-cursor - spec cursor direction [row]) + (drop-until-beyond-cursor spec cursor direction [row]) []))) (scan-forward-anchored [spec cursor direction] (if (:resource-id spec) @@ -456,11 +474,11 @@ (:relation-id spec) (:resource-type spec)] (:resource-id cursor) - direction) + direction include-qualifier?) (map (fn [{:keys [v]}] (relationship-row - spec (:subject-id spec) (nth v 3)))) + spec (:subject-id spec) (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-reverse-anchored [spec cursor direction] @@ -474,11 +492,11 @@ (:relation-id spec) (:subject-type spec)] (:subject-id cursor) - direction) + direction include-qualifier?) (map (fn [{:keys [v]}] (relationship-row - spec (nth v 3) (:resource-id spec)))) + spec (nth v 3) (:resource-id spec) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction)))) (scan-forward-partial [spec cursor direction] @@ -490,10 +508,10 @@ (:resource-type spec)] (:resource-id cursor) (:subject-id cursor) - direction) + direction include-qualifier?) (map (fn [{:keys [e v]}] - (relationship-row spec e (nth v 3)))) + (relationship-row spec e (nth v 3) (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))) (scan-reverse-partial [spec cursor direction] @@ -505,10 +523,10 @@ (:subject-type spec)] (:subject-id cursor) (:resource-id cursor) - direction) + direction include-qualifier?) (map (fn [{:keys [e v]}] - (relationship-row spec (nth v 3) e))) + (relationship-row spec (nth v 3) e (nth v 4)))) (drop-until-beyond-cursor spec cursor direction))) (scan-spec [spec cursor direction] @@ -526,7 +544,7 @@ (let [scan-specs (relationship-engine/plan-scans relation-defs normalized-filters)] - (if window-options + (if (:accept? window-options) (relationship-engine/execute-filtered-window scan-specs normalized-filters decision-kernel scan-spec window-options) diff --git a/modules/eacl-datomic/src/eacl/datomic/migrations/v7_to_v9.clj b/modules/eacl-datomic/src/eacl/datomic/migrations/v7_to_v9.clj index 4538e48f..dc1ffa57 100644 --- a/modules/eacl-datomic/src/eacl/datomic/migrations/v7_to_v9.clj +++ b/modules/eacl-datomic/src/eacl/datomic/migrations/v7_to_v9.clj @@ -8,8 +8,11 @@ (defn- install! [conn] (let [db (d/db conn) - definitions (concat upgrade/metadata-schema [admission/basis-guard] - (filter #(contains? storage/attributes (:db/ident %)) schema/v8-schema))] + ;; The target runtime also needs the Caveat fields and native guarded + ;; publication function. Install missing definitions during migration, + ;; before the completed target storage marker can admit that runtime. + definitions (vals (into {} (map (juxt :db/ident identity)) + (concat upgrade/metadata-schema schema/v8-schema)))] (doseq [attr storage/attributes :when (and (d/entid db attr) (not (admission/schema-compatible? db attr)))] (upgrade/fail! :incompatible-schema {:attribute attr})) diff --git a/modules/eacl-datomic/src/eacl/datomic/qualifiers.clj b/modules/eacl-datomic/src/eacl/datomic/qualifiers.clj index 0ea43cf7..a7871224 100644 --- a/modules/eacl-datomic/src/eacl/datomic/qualifiers.clj +++ b/modules/eacl-datomic/src/eacl/datomic/qualifiers.clj @@ -1,33 +1,32 @@ (ns eacl.datomic.qualifiers "Certified inline native tuple-ref publication for non-serving staging." - (:require [datomic.api :as d] + (:require [eacl.datomic.db :as native-db] + [datomic.api :as d] [eacl.datomic.db :as db] - [eacl.relationships.staged :as staged])) + [eacl.relationships.staged :as staged] + [eacl.schema.qualification-admission :as admission])) -(defn- facts [database eid] - (mapv (fn [datom] [(:a datom) (:v datom) (:tx datom)]) (d/datoms database :eavt eid))) +(defn- facts [database eid] (native-db/entity-facts database eid)) -(defn- entity [database eid] - (let [rows (facts database eid)] - (when (seq rows) - (reduce (fn [result [a v]] - (let [attribute (:db/ident (d/entity database a))] - (if (= :eacl.relation/caveats attribute) - (update result attribute (fnil conj #{}) v) (assoc result attribute v)))) - {:db/id eid} rows)))) +(defn- entity [database eid] (native-db/entity-data database eid)) (defn- generation [database] (:eacl/schema-version (d/entity database [:eacl/id "schema-string"]))) -(defn- fence [database relation-id reference-added?] +(defn- schema-fence [database reference-added?] (let [schema-id (d/entid database [:eacl/id "schema-string"]) expected (generation database)] (when-not expected (staged/error! :schema-unprepared)) - ;; Reference creation participates in the existing schema CAS fence. It - ;; cannot race a Caveat removal that was checked before the reference existed. - (cond-> [[:db.fn/cas schema-id :eacl/schema-version expected (if reference-added? (d/squuid) expected)]] - relation-id (conj [:db.fn/cas relation-id :eacl/relation-version - (:eacl/relation-version (entity database relation-id)) "datomic.tx"])))) + ;; One schema CAS per batch also serializes concurrent Caveat removal. + [[:db.fn/cas schema-id :eacl/schema-version expected (if reference-added? (d/squuid) expected)]])) + +(defn- relation-fence [database relation-id] + (when relation-id + [[:db.fn/cas relation-id :eacl/relation-version + (:eacl/relation-version (entity database relation-id)) "datomic.tx"]])) + +(defn- fence [database relation-id reference-added?] + (into (schema-fence database reference-added?) (relation-fence database relation-id))) (defn read-api "Read-only native inputs; constructing this map never prepares or writes a store." @@ -41,12 +40,29 @@ :qualifier-cache-scope :assertion-version :qualifier-version (fn [database eid] (some-> (d/datoms database :eavt eid :eacl.relationship-qualifier/format-version) first :tx))}) +(defn planner-api + "Pure native reads and transaction-data construction, safe for snapshots." + [] + (merge (read-api) + {:strategy :inline :fence fence + :schema-fence schema-fence :relation-fence relation-fence + :assert-entity (fn [eid expected] [:eacl.fn/assert-qualifier-facts eid expected]) + :tempid #(str "eacl-qualifier-" (random-uuid))})) + +(defn plan + "Builds qualified transaction data from one immutable basis without writing." + ([database entries app-datoms] + (staged/plan-batch (staged/planner (planner-api) database) database entries app-datoms)) + ([database entries app-datoms _source-scope] + (plan database entries app-datoms))) + (defn writer [conn] (when-not (d/entid (d/db conn) :eacl.fn/assert-qualifier-facts) (staged/error! :schema-unprepared)) (staged/native-writer - (merge (read-api) - {:strategy :inline :snapshot #(d/db conn) :fence fence - :generation-after-reference #(generation (:db-after %)) - :assert-entity (fn [eid expected] [:eacl.fn/assert-qualifier-facts eid expected]) - :tempid #(str "eacl-qualifier-" (random-uuid)) - :transact! #(deref (d/transact conn %))}))) + (merge (planner-api) + {:snapshot #(d/db conn) + :generation-after-reference #(generation (:db-after %)) + :transact! #(deref (d/transact conn %))}))) + +(defn publication-capability [database] + (admission/publication-descriptor :inline)) diff --git a/modules/eacl-datomic/src/eacl/datomic/schema.clj b/modules/eacl-datomic/src/eacl/datomic/schema.clj index 042a8859..c38df5d0 100644 --- a/modules/eacl-datomic/src/eacl/datomic/schema.clj +++ b/modules/eacl-datomic/src/eacl/datomic/schema.clj @@ -4,6 +4,7 @@ [eacl.caveats.schema :as caveat-schema] [eacl.caveats.definition :as caveat-definition] [eacl.datomic.impl.indexed :as impl.indexed] + [eacl.datomic.db :as ddb] [eacl.datomic.storage :as target-storage] [eacl.datomic.qualifier-functions :as qualifier-functions] [eacl.relationships.upgrade :as upgrade] @@ -16,6 +17,7 @@ [eacl.schema.expression-policy :as expression-policy] [eacl.schema.expression-resolver :as expression-resolver] [eacl.schema.model :as model] + [eacl.schema.relation-allowance :as relation-allowance] [eacl.schema.replacement-plan :as replacement-plan])) ; should these Malli specs be in a separate namespace, e.g. specs? @@ -434,15 +436,17 @@ (endpoint-pair/reverse-value resource-type relation-eid subject-type Long/MAX_VALUE Long/MAX_VALUE))))))) (defn read-relations - "Enumerates all EACL Relation schema entities in DB and returns pull maps." + "Canonical Relation definitions, including named Caveat alternatives." [db] - (d/q '[:find [(pull ?relation [:eacl/id - :eacl.relation/subject-type - :eacl.relation/resource-type - :eacl.relation/relation-name]) ...] - :where - [?relation :eacl.relation/relation-name ?relation-name]] - db)) + (let [pattern (cond-> [:eacl/id :eacl.relation/subject-type + :eacl.relation/resource-type :eacl.relation/relation-name] + (d/entid db :eacl.relation/caveats) + (into [:eacl.relation/allows-unqualified? + {:eacl.relation/caveats [:eacl.caveat/name]}]))] + (mapv relation-allowance/canonicalize + (d/q '[:find [(pull ?relation pattern) ...] + :in $ pattern + :where [?relation :eacl.relation/relation-name]] db pattern)))) (defn read-permissions "Enumerates all EACL permission schema entities in DB and returns maps." @@ -472,15 +476,20 @@ :where [?q :eacl.relationship-qualifier/caveat ?c]] db eid) [])) +(defn read-authorization-schema + "Reads permission structure without compiling undemanded Caveat programs." + [db] + (let [permissions (read-permissions db)] + (expression-persistence/validate-entities permissions) + {:relations (read-relations db) :permissions permissions})) + (defn read-schema "Enumerates all EACL permission schema entities in DB and returns maps." ; todo: unparse into SpiceDB string schema if desired. [db & [_format]] - (let [permissions (read-permissions db)] - (expression-persistence/validate-entities permissions) - (let [caveats (read-caveats db)] - (cond-> {:relations (read-relations db) :permissions permissions} - (seq caveats) (assoc :caveats caveats))))) + (let [schema (read-authorization-schema db) + caveats (read-caveats db)] + (cond-> schema (seq caveats) (assoc :caveats caveats)))) (defn- read-schema-unchecked "Migration-only physical schema read. Normal readers must use read-schema so @@ -709,6 +718,12 @@ :expressions expressions :expression-metadata metadata}))) +(defn- stored-relation-caveats [db relation] + (relation-allowance/stored-caveats + {:entid #(d/entid db %) :entity #(ddb/entity-data db %) + :rows #(ddb/relationship-identity-datoms db %1 %2 %3) + :scan #(ddb/avet-tuple-prefix db %1 %2)} relation)) + (defn- plan-schema-candidate [db schema-string new-schema-map {:keys [allow-empty-schema? validate-existing? orphan-policy] @@ -723,7 +738,7 @@ (not allow-empty-schema?) (or (seq (:relations existing-schema)) (seq (:permissions existing-schema)) - (seq (:caveats existing-schema)))) + (seq (:caveats existing-schema)))) (throw (ex-info (str "Refusing to replace a non-empty schema with zero definitions." @@ -734,6 +749,7 @@ {:relations (count (:relations existing-schema)) :permissions (count (:permissions existing-schema))}}))) deltas (compare-schema existing-schema new-schema-map) + _ (relation-allowance/validate-existing! (:relations deltas) #(stored-relation-caveats db %)) semantic (replacement-plan/plan {:deltas deltas @@ -744,12 +760,18 @@ #(relationship-present-for-relation? db %)}) {:keys [relations permissions caveats]} deltas _ (caveat-definition/validate-replacements! caveats #(caveat-references db %)) - relation-retractions (:retractions relations) + relation-retractions (relation-allowance/entity-deletions relations) permission-retractions (expression-persistence/entity-deletions permissions) + caveat-addition-entities + (mapv #(assoc % :db/id (d/tempid :db.part/user)) (:additions caveats)) + caveat-refs (into {} (map (juxt :eacl.caveat/name :db/id)) caveat-addition-entities) relation-addition-entities (mapv (fn [relation] - (assoc relation :db/id (d/tempid :db.part/user))) + (cond-> (assoc relation :db/id (d/tempid :db.part/user)) + (contains? relation :eacl.relation/caveats) + (update :eacl.relation/caveats + #(mapv (fn [[_ name :as ref]] (get caveat-refs name ref)) %)))) (:additions relations)) relation-initial-stamps (mapv (fn [relation] @@ -768,10 +790,11 @@ tx-data (vec (concat + caveat-addition-entities + (relation-allowance/attribute-retractions relations) relation-addition-entities relation-initial-stamps (:additions permissions) - (map #(assoc % :db/id (d/tempid :db.part/user)) (:additions caveats)) (for [caveat (caveat-definition/entity-deletions caveats)] [:db.fn/retractEntity [:eacl.caveat/name (:eacl.caveat/name caveat)]]) (for [relation relation-retractions] @@ -780,20 +803,24 @@ [:db.fn/retractEntity [:eacl/id (:eacl/id permission)]]) [schema-stamp-entity])) relation-commit-guards - (mapv - (fn [relation] - [:eacl.fn/assert-relation-unused - (:eacl.relation/resource-type relation) - (d/entid db [:eacl/id (:eacl/id relation)]) - (:eacl.relation/subject-type relation)]) - relation-retractions) + (into + (mapv (fn [relation] + [:eacl.fn/assert-relation-unused + (:eacl.relation/resource-type relation) + (d/entid db [:eacl/id (:eacl/id relation)]) + (:eacl.relation/subject-type relation)]) relation-retractions) + (map (fn [{:keys [before]}] + (let [eid (d/entid db [:eacl/id (:eacl/id before)])] + [:db.fn/cas eid :eacl/relation-version + (:eacl/relation-version (ddb/entity-data db eid)) "datomic.tx"]))) + (relation-allowance/changes relations)) no-op? (not (some seq [(:additions relations) (:retractions relations) (:additions permissions) (:retractions permissions) - (:additions caveats) - (:retractions caveats)])) + (:additions caveats) + (:retractions caveats)])) effective-tx-data (if no-op? [] tx-data)] (assoc semantic :tx-data effective-tx-data @@ -818,7 +845,7 @@ (:expression-limits options)) new-schema-map (expression-persistence/candidate-schema - (expression-resolver/validate-schema schema-string expression-limits))] + (expression-resolver/validate-schema schema-string expression-limits (select-keys options [:allow-caveats?])))] (binding [expression-persistence/*expression-limits* expression-limits] (plan-schema-candidate db schema-string new-schema-map options)))) @@ -923,7 +950,7 @@ (let [new-schema-map (expression-persistence/candidate-schema (expression-resolver/validate-schema - schema-string expression-limits))] + schema-string expression-limits (select-keys opts [:allow-caveats?])))] (write-schema-candidate! conn schema-string new-schema-map opts known-schema-version)))))) diff --git a/modules/eacl-datomic/test/eacl/bench/managed_proof_cost_test.clj b/modules/eacl-datomic/test/eacl/bench/managed_proof_cost_test.clj index 3a52b575..04b47903 100644 --- a/modules/eacl-datomic/test/eacl/bench/managed_proof_cost_test.clj +++ b/modules/eacl-datomic/test/eacl/bench/managed_proof_cost_test.clj @@ -12,6 +12,7 @@ [datomic.api :as d] [eacl.backend.v8 :as backend] [eacl.core :as eacl] + [eacl.client.orchestration :as orchestration] [eacl.datahike.backend :as datahike-backend] [eacl.datahike.core :as datahike] [eacl.datascript.backend :as datascript-backend] @@ -264,7 +265,7 @@ :datahike (datahike/cache-stats client) :datascript (datascript/cache-stats client))) -(defn- managed-request-measurement +(defn- unrelated-request-measurement [label client demand transact-objects!] (let [backend-ops (atom {}) before-stats (backend-cache-stats label client) @@ -279,9 +280,10 @@ backend-ops 1 (fn [] (let [result (eacl/check-permission client demand)] - (when-not (and (:allowed? result) (:cached? result)) + (when-not (and (:allowed? result) + (= (not orchestration/*qualified-authorization-enabled?*) (:cached? result))) (throw - (ex-info "expected managed hit after unrelated commit" + (ex-info "unrelated commit violated the active epoch reuse contract" {:result result}))) result)))) (range request-measurement-samples)) @@ -330,7 +332,7 @@ dependency-counts) _ (eacl/check-permission client demand) exact (exact-request-measurement client demand) - managed (managed-request-measurement + managed (unrelated-request-measurement label client demand transact-objects!) relevant (relevant-miss-measurement client demand relationship) @@ -340,11 +342,12 @@ result (cond-> {:backend label + :answer-reuse (if orchestration/*qualified-authorization-enabled?* :exact-basis :managed) :create-committed-datom-events (count create-tx) :delete-committed-datom-events (count delete-tx) :proof-cardinalities proof-results :exact-hit exact - :managed-hit-after-unrelated-commit managed + :request-after-unrelated-commit managed :relevant-proof-miss relevant} relation-generation-history-size (assoc @@ -358,10 +361,11 @@ (is (pos? (:delete-committed-datom-events result))) (is (zero? (get-in exact [:backend-operations :proof-frame] 0)) (pr-str exact)) - (is (= request-measurement-samples (:managed-hits managed)) + (is (= (if orchestration/*qualified-authorization-enabled?* 0 request-measurement-samples) + (:managed-hits managed)) (pr-str managed)) (doseq [{:keys [dependency-count scalar full-vector - backend-operations]} + backend-operations]} proof-results] (is (= (* 2 proof-measurement-samples proof-calls-per-sample) (:proof-frame backend-operations)) @@ -514,10 +518,11 @@ (defn- compact-performance-result [{:keys [backend proof-cardinalities exact-hit - managed-hit-after-unrelated-commit relevant-proof-miss] + request-after-unrelated-commit relevant-proof-miss] :as result}] (cond-> {:backend backend + :answer-reuse (:answer-reuse result) :proof-cardinalities (mapv (fn [{:keys [dependency-count scalar full-vector backend-operations]}] @@ -527,7 +532,7 @@ :proof-frame-calls (:proof-frame backend-operations)}) proof-cardinalities) :exact-hit exact-hit - :managed-hit-after-unrelated-commit managed-hit-after-unrelated-commit + :request-after-unrelated-commit request-after-unrelated-commit :relevant-proof-miss relevant-proof-miss} (contains? result :relation-generation-history-datom-growth) (assoc diff --git a/modules/eacl-datomic/test/eacl/datomic/cache_model_test.clj b/modules/eacl-datomic/test/eacl/datomic/cache_model_test.clj index 6c048ecd..408fe806 100644 --- a/modules/eacl-datomic/test/eacl/datomic/cache_model_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/cache_model_test.clj @@ -36,7 +36,10 @@ (defn- without-cache-provenance [value] (if (map? value) - (dissoc value :cached? :cache-basis) + ;; Separate live requests may mint different time-scoped cursor bytes. + ;; Compare authorization and page bounds; cursor validity has its own suite. + (cond-> (dissoc value :cached? :cache-basis) + (:page-info value) (update :page-info dissoc :start-cursor :end-cursor)) value)) (defn- assert-same-answers! diff --git a/modules/eacl-datomic/test/eacl/datomic/cache_review_regressions_test.clj b/modules/eacl-datomic/test/eacl/datomic/cache_review_regressions_test.clj index 0e492607..ab9fdfca 100644 --- a/modules/eacl-datomic/test/eacl/datomic/cache_review_regressions_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/cache_review_regressions_test.clj @@ -385,10 +385,10 @@ alice (spice-object :user "alice") account (spice-object :account "acct0") calls (atom 0) - original engine/can?] - (with-redefs [engine/can? (fn [& args] - (swap! calls inc) - (apply original args))] + original engine/check-evidence] + (with-redefs [engine/check-evidence (fn [& args] + (swap! calls inc) + (apply original args))] (dotimes [_ 3] (is (true? (eacl/can? acl alice :admin account)))) (is (= 1 @calls) "identical fully-consistent reads at one basis compute once") diff --git a/modules/eacl-datomic/test/eacl/datomic/caveat_schema_test.clj b/modules/eacl-datomic/test/eacl/datomic/caveat_schema_test.clj index 68159b2a..6f03058b 100644 --- a/modules/eacl-datomic/test/eacl/datomic/caveat_schema_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/caveat_schema_test.clj @@ -1,5 +1,6 @@ (ns eacl.datomic.caveat-schema-test (:require [eacl.datomic.core :as api] + [eacl.datomic.backend :as backend] [eacl.cache :as cache] [eacl.caveats.hot-path-contract :as hot-path] [clojure.test :refer [deftest]] @@ -7,6 +8,8 @@ [eacl.datomic.schema :as schema] [eacl.caveats.persistence-contract :as contract] [eacl.caveats.publication-contract :as publication] + [eacl.relationships.edge-contract :as edge-contract] + [eacl.datomic.db :as scan-impl] [eacl.datomic.qualifiers :as qualifiers])) (defn interleave! [competitor outer] @@ -38,3 +41,15 @@ {:write-schema! #(schema/write-schema! conn %) :writer #(qualifiers/writer conn) :entid d/entid :strategy :inline :interleave! interleave!}) (finally (d/release conn) (d/delete-database uri))))) + +(deftest compact-qualified-scans + (let [uri (str "datomic:mem://edge-scan-" (random-uuid)) + _ (d/create-database uri) conn (d/connect uri)] + (try + (schema/install! conn) + (edge-contract/check! + {:write-schema! #(schema/write-schema! conn %) :writer #(qualifiers/writer conn) + :entid d/entid :forward scan-impl/subject->resources + :reverse scan-impl/resource->subjects :direct scan-impl/direct-edge + :adapter #(backend/basis-adapter % {})}) + (finally (d/release conn) (d/delete-database uri))))) diff --git a/modules/eacl-datomic/test/eacl/datomic/consistency_cache_test.clj b/modules/eacl-datomic/test/eacl/datomic/consistency_cache_test.clj index 71042e37..98cf9d17 100644 --- a/modules/eacl-datomic/test/eacl/datomic/consistency_cache_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/consistency_cache_test.clj @@ -6,6 +6,7 @@ [eacl.backend.v8 :as backend] [eacl.cache :as shared-cache] [eacl.causal-token :as causal-token] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl :refer [->Relationship spice-object]] [eacl.datomic.backend :as datomic-backend] [eacl.datomic.core :as core] @@ -142,7 +143,7 @@ :resource account :consistency (consistency/at-least-as-fresh token)})] (is (true? (:allowed? refreshed))) - (is (true? (:cached? refreshed)) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? refreshed)) "the reader Peer preserves its managed generation across tokens"))))) (deftest explicit-cache-expiry-installs-a-fresh-lifecycle-test @@ -188,8 +189,8 @@ alice (spice-object :user "alice") account (spice-object :account "acct") calls (atom 0) - original engine/can?] - (with-redefs [engine/can? + original engine/check-evidence] + (with-redefs [engine/check-evidence (fn [db subject permission resource] (swap! calls inc) (original db subject permission resource))] @@ -206,8 +207,8 @@ relationship (->Relationship alice :owner account) {created-token :zed/token} (seed! conn client) calls (atom 0) - original engine/can?] - (with-redefs [engine/can? + original engine/check-evidence] + (with-redefs [engine/check-evidence (fn [db subject permission resource] (swap! calls inc) (original db subject permission resource))] @@ -253,8 +254,8 @@ (let [client (cached-client conn) _ (seed! conn client) calls (atom 0) - original engine/can?] - (with-redefs [engine/can? + original engine/check-evidence] + (with-redefs [engine/check-evidence (fn [db subject permission resource] (swap! calls inc) (original db subject permission resource))] @@ -277,8 +278,8 @@ :permission :admin :resource (spice-object :account "acct")} calls (atom 0) - original engine/can?] - (with-redefs [engine/can? + original engine/check-evidence] + (with-redefs [engine/check-evidence (fn [db subject permission resource] (swap! calls inc) (original db subject permission resource))] diff --git a/modules/eacl-datomic/test/eacl/datomic/consistency_v3_test.clj b/modules/eacl-datomic/test/eacl/datomic/consistency_v3_test.clj index 1ea28306..01aa0ccd 100644 --- a/modules/eacl-datomic/test/eacl/datomic/consistency_v3_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/consistency_v3_test.clj @@ -3,6 +3,7 @@ [datomic.api :as d] [eacl.backend.source :as source] [eacl.causal-token :as causal-token] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl] [eacl.datomic.core :as datomic] [eacl.datomic.datomic-helpers @@ -309,9 +310,9 @@ [{:db/id (d/tempid :db.part/user) :db/doc "unrelated application data"}]) (testing "an unrelated basis advance lifts the authenticated answer" - (is (true? - (:cached? - (eacl/lookup-resources authorization query))))) + (is (= (not orchestration/*qualified-authorization-enabled?*) + (:cached? + (eacl/lookup-resources authorization query))))) (eacl/delete-relationship! authorization relationship) (let [after-revocation (eacl/lookup-resources authorization query)] diff --git a/modules/eacl-datomic/test/eacl/datomic/lookahead_observer_test.clj b/modules/eacl-datomic/test/eacl/datomic/lookahead_observer_test.clj index 7a5797d2..5dbb5624 100644 --- a/modules/eacl-datomic/test/eacl/datomic/lookahead_observer_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/lookahead_observer_test.clj @@ -59,7 +59,8 @@ (testing "the caller's continuation is an exact hit with identical content" (is (true? (:cached? continuation))) (is (= (:data oracle) (:data continuation))) - (is (= (:page-info oracle) (:page-info continuation))))) + (is (= (dissoc (:page-info oracle) :start-cursor :end-cursor) + (dissoc (:page-info continuation) :start-cursor :end-cursor))))) (testing "observer events carry provenance and exact meters" (let [foreground (first (filter #(= :request (:provenance %)) @events)) background (first (filter #(= :lookahead (:provenance %)) @events))] diff --git a/modules/eacl-datomic/test/eacl/datomic/lookup_cache_test.clj b/modules/eacl-datomic/test/eacl/datomic/lookup_cache_test.clj index d91fc975..0bf15540 100644 --- a/modules/eacl-datomic/test/eacl/datomic/lookup_cache_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/lookup_cache_test.clj @@ -2,6 +2,7 @@ (:require [clojure.test :refer [deftest is testing]] [datomic.api :as d] [eacl.cache :as shared-cache] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl :refer [->Relationship spice-object]] [eacl.datomic.core :as core] [eacl.datomic.datomic-helpers :refer [with-mem-conn @@ -57,6 +58,11 @@ [] {}) +(defn- calls-after-basis-change [legacy-count] + ;; Qualified reuse requires an exact basis until a proof also covers raw + ;; qualifier and Caveat mutations. An unrelated write is one extra miss. + (if orchestration/*qualified-authorization-enabled?* (inc legacy-count) legacy-count)) + (deftest live-non-recursive-pages-survive-unrelated-transactions-test (with-mem-conn [conn schema/v8-schema] (let [client (core/make-client conn {:cache (live-cache-context)}) @@ -89,8 +95,8 @@ (is (= ["a-1"] (mapv :id (:data (eacl/lookup-resources client forward-query))))) (is (= ["alice"] (mapv :id (:data (eacl/lookup-subjects client reverse-query))))) - (is (= 1 @forward-calls) "basis changes do not invalidate EACL results") - (is (= 1 @reverse-calls) "reverse lookup uses the same logical generation"))))) + (is (= (calls-after-basis-change 1) @forward-calls) "basis changes do not invalidate EACL results") + (is (= (calls-after-basis-change 1) @reverse-calls) "reverse lookup uses the same logical generation"))))) (deftest relationship-write-invalidates-live-pages-but-no-op-does-not-test (with-mem-conn [conn schema/v8-schema] @@ -118,13 +124,13 @@ :auditor (spice-object :account "a-2"))) (is (= ["a-1"] (mapv :id (:data (eacl/lookup-resources client query))))) - (is (= 1 @calls) + (is (= (calls-after-basis-change 1) @calls) "a relationship outside the permission dependency set keeps the page hot") (eacl/create-relationship! client second-rel) (is (= #{"a-1" "a-2"} (set (map :id (:data (eacl/lookup-resources client query)))))) - (is (= 2 @calls) "an actual relationship write selects a new generation") + (is (= (calls-after-basis-change 2) @calls) "an actual relationship write selects a new generation") (eacl/write-relationship! client @@ -134,7 +140,7 @@ :resource (:resource second-rel)}) (is (= #{"a-1" "a-2"} (set (map :id (:data (eacl/lookup-resources client query)))))) - (is (= 2 @calls) "a relationship no-op keeps the hot generation"))))) + (is (= (calls-after-basis-change 2) @calls) "a relationship no-op keeps the hot generation"))))) (deftest direct-relationship-writes-are-observed-test ;; This test used to assert the OPPOSITE: a raw d/transact of @@ -193,12 +199,12 @@ (->Relationship bob :auditor account)) (is (= ["server"] (mapv :id (:data (eacl/lookup-resources client query))))) - (is (= 1 @calls) + (is (= (calls-after-basis-change 1) @calls) "an unrelated relation leaves the arrow permission page hot") (eacl/delete-relationship! client owner-rel) (is (empty? (:data (eacl/lookup-resources client query)))) - (is (= 2 @calls) + (is (= (calls-after-basis-change 2) @calls) "a target-permission relation invalidates the arrow lookup") (eacl/create-relationship! client owner-rel) @@ -206,7 +212,7 @@ (mapv :id (:data (eacl/lookup-resources client query))))) (eacl/delete-relationship! client account-rel) (is (empty? (:data (eacl/lookup-resources client query)))) - (is (= 4 @calls) + (is (= (calls-after-basis-change 4) @calls) "the arrow's source relation is also a dependency"))))) (deftest cached-pages-store-eids-and-reapply-current-id-coercion-test @@ -319,8 +325,8 @@ (spice-object :account "a-2"))) (is (= 1 (:count (eacl/count-resources client forward-query)))) (is (= 1 (:count (eacl/count-subjects client reverse-query)))) - (is (= 1 @forward-calls)) - (is (= 1 @reverse-calls)) + (is (= (calls-after-basis-change 1) @forward-calls)) + (is (= (calls-after-basis-change 1) @reverse-calls)) (eacl/create-relationship! client @@ -329,8 +335,8 @@ (spice-object :account "a-2"))) (is (= 2 (:count (eacl/count-resources client forward-query)))) (is (= 1 (:count (eacl/count-subjects client reverse-query)))) - (is (= 2 @forward-calls)) - (is (= 2 @reverse-calls) + (is (= (calls-after-basis-change 2) @forward-calls)) + (is (= (calls-after-basis-change 2) @reverse-calls) "a relevant relation epoch invalidates both count directions"))))) (deftest recursive-cursors-replay-across-independent-client-proofs-test @@ -595,12 +601,12 @@ account (spice-object :account "a-1") query {:subject alice :permission :admin :resource/type :account} calls (atom 0) - original engine/can? + original engine/check-evidence lookups (atom 0) original-lookup engine/lookup-resources] - (with-redefs [engine/can? (fn [db s p r] - (swap! calls inc) - (original db s p r)) + (with-redefs [engine/check-evidence (fn [db s p r] + (swap! calls inc) + (original db s p r)) engine/lookup-resources (fn [db q cc] (swap! lookups inc) diff --git a/modules/eacl-datomic/test/eacl/datomic/qualified_write_test.clj b/modules/eacl-datomic/test/eacl/datomic/qualified_write_test.clj new file mode 100644 index 00000000..2ed4a935 --- /dev/null +++ b/modules/eacl-datomic/test/eacl/datomic/qualified_write_test.clj @@ -0,0 +1,80 @@ +(ns eacl.datomic.qualified-write-test + (:require [clojure.test :refer [deftest]] + [datomic.api :as d] + [eacl.caveats.publication-batch-contract :as batch] + [eacl.caveats.public-write-contract :as public] + [eacl.caveats.schema-allowance-contract :as allowance] + [eacl.caveats.inspection-contract :as inspection] + [eacl.caveats.deletion-contract :as deletion] + [eacl.caveats.cache-trace-contract :as cache-trace] + [eacl.authorization.qualification-test :as fixtures] + [eacl.datomic.core :as api] + [eacl.datomic.caveat-schema-test :as schema-races] + [eacl.datomic.schema :as schema] + [eacl.datomic.qualifiers :as qualifiers])) + +(deftest qualified-batches-publish-atomically + (let [uri (str "datomic:mem://qualified-batch-" (random-uuid)) + _ (d/create-database uri) conn (d/connect uri)] + (try + (schema/install! conn) + @(d/transact conn [{:db/ident :app/flag :db/valueType :db.type/long :db/cardinality :db.cardinality/one}]) + (batch/check! {:write-schema! #(schema/write-schema! conn %) + :writer #(qualifiers/writer conn) :entid d/entid :strategy :inline}) + (finally (d/release conn) (d/delete-database uri))))) + +(deftest public-qualified-writes-preserve-identity-and-commit-atomically + (let [uri (str "datomic:mem://public-qualified-" (random-uuid)) + _ (d/create-database uri) + conn (d/connect uri) + now (atom 99)] + (try + (schema/install! conn) + @(d/transact conn [{:db/ident :app/flag :db/valueType :db.type/long :db/cardinality :db.cardinality/one}]) + (public/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) :entid d/entid :now now}) + (finally (d/release conn) (d/delete-database uri))))) + +(deftest schema-alternatives-preserve-relation-identities-and-retained-data + (let [uri (str "datomic:mem://schema-allowance-" (random-uuid)) + _ (d/create-database uri) conn (d/connect uri)] + (try + (schema/install! conn) + (allowance/check! {:client (api/make-client conn {:caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) + :read-schema schema/read-schema :interleave! schema-races/interleave! :entid d/entid}) + (finally (d/release conn) (d/delete-database uri))))) + +(deftest stored-and-active-inspection-preserve-aligned-native-qualifiers + (let [uri (str "datomic:mem://public-qualified-" (random-uuid)) + _ (d/create-database uri) + conn (d/connect uri) + now (atom 99)] + (try + (schema/install! conn) + @(d/transact conn [{:db/ident :app/flag :db/valueType :db.type/long :db/cardinality :db.cardinality/one}]) + (inspection/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) :entid d/entid :now now}) + (finally (d/release conn) (d/delete-database uri))))) + +(deftest qualified-cache-traces-match-uncached-authorization + (let [uri (str "datomic:mem://qualified-cache-trace-" (random-uuid)) + _ (d/create-database uri) conn (d/connect uri) now (atom 99)] + (try + (schema/install! conn) + (cache-trace/check! {:client (api/make-client conn {:clock #(deref now) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn) :now now :expire-cache! api/expire-cache!}) + (finally (d/release conn) (d/delete-database uri))))) + +(deftest qualified-object-deletion-is-atomic-and-bounded + (let [uri (str "datomic:mem://qualified-deletion-" (random-uuid)) + _ (d/create-database uri) conn (d/connect uri)] + (try + (schema/install! conn) + (deletion/check! {:client (api/make-client conn {:clock (constantly 200) + :caveat-evaluator (fixtures/portable-evaluator (atom 0))}) + :writer #(qualifiers/writer conn)}) + (finally (d/release conn) (d/delete-database uri))))) diff --git a/modules/eacl-datomic/test/eacl/datomic/range_reuse_integration_test.clj b/modules/eacl-datomic/test/eacl/datomic/range_reuse_integration_test.clj index 6cde56b8..733a373c 100644 --- a/modules/eacl-datomic/test/eacl/datomic/range_reuse_integration_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/range_reuse_integration_test.clj @@ -30,7 +30,8 @@ (defn- public-shape [page] - (select-keys page [:data :page-info])) + (-> (select-keys page [:data :page-info]) + (update :page-info dissoc :start-cursor :end-cursor))) (deftest shorter-page-is-derived-from-the-longer-resident-page-test (with-mem-conn [conn datomic-schema/v8-schema] diff --git a/modules/eacl-datomic/test/eacl/datomic/recursive_cache_test.clj b/modules/eacl-datomic/test/eacl/datomic/recursive_cache_test.clj index 13b7fdc6..8f564d20 100644 --- a/modules/eacl-datomic/test/eacl/datomic/recursive_cache_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/recursive_cache_test.clj @@ -13,6 +13,7 @@ [eacl.cache.standard-lru :as lru] [eacl.backend.v8 :as backend] [eacl.continuation :as continuation] + [eacl.client.orchestration :as orchestration] [eacl.core :as eacl :refer [->Relationship spice-object]] [eacl.datomic.core :as core] [eacl.datomic.datomic-helpers :refer [with-mem-conn]] @@ -603,9 +604,9 @@ (binding [engine/*recursive-traversal-stats* fresh-stats] (eacl/lookup-resources client query))] (is (= (:data page1) (:data fresh-page1))) - (is (true? (:cached? fresh-page1)) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? fresh-page1)) "the completed demand answer is reusable under the unchanged scalar proof") - (is (zero? (get @fresh-stats :derived-grants 0)) + (is (= (not orchestration/*qualified-authorization-enabled?*) (zero? (get @fresh-stats :derived-grants 0))) "a managed answer hit performs no recursive traversal"))))))) (deftest recursive-denotations-are-client-private-across-clients-test diff --git a/modules/eacl-datomic/test/eacl/datomic/schema_basis_test.clj b/modules/eacl-datomic/test/eacl/datomic/schema_basis_test.clj index 3e8bd17b..a914a15c 100644 --- a/modules/eacl-datomic/test/eacl/datomic/schema_basis_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/schema_basis_test.clj @@ -278,14 +278,15 @@ before-version (idx/schema-version (d/db conn)) entered (promise) release-read (promise) - original-can? engine/can?] - (with-redefs [engine/can? + original-check engine/check-evidence] + (with-redefs [engine/check-evidence (fn [& args] (deliver entered true) @release-read - (apply original-can? args))] + (apply original-check args))] (let [read-work (future (eacl/can? acl user :admin account)) - _ @entered + _ (is (= true (deref entered 5000 ::timed-out)) + "the selected-snapshot read reaches the shared evidence check") write-work (future (eacl/write-schema! acl schema-v2)) write-result (deref write-work 2000 ::timed-out)] (try diff --git a/modules/eacl-datomic/test/eacl/datomic/speculative_admission_test.clj b/modules/eacl-datomic/test/eacl/datomic/speculative_admission_test.clj index ef5fb7a5..651b34c8 100644 --- a/modules/eacl-datomic/test/eacl/datomic/speculative_admission_test.clj +++ b/modules/eacl-datomic/test/eacl/datomic/speculative_admission_test.clj @@ -157,7 +157,7 @@ :after speculative-cursor}))] (is (= :eacl.pagination/invalid-cursor (:type cursor-error))) - (is (= :speculative-provenance + (is (= (if orchestration/*qualified-authorization-enabled?* :query-mismatch :speculative-provenance) (:reason cursor-error))) (is (not (nil? cursor-error)) "a speculative cursor cannot cross to a colliding commit")))) @@ -207,9 +207,9 @@ decision (eacl/check-permission prospective report-demand) after (datomic/cache-stats client)] (is (true? (:allowed? decision))) - (is (true? (:cached? decision)) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? decision)) "the disjoint proof reads through the committed managed tier") - (is (= (inc (:managed-hits before)) + (is (= (cond-> (:managed-hits before) (not orchestration/*qualified-authorization-enabled?*) inc) (:managed-hits after))) (is (= (:puts before) (:puts after)) "speculative read-through never promotes or publishes")))))))) @@ -276,13 +276,13 @@ (eacl/with-snapshot [prospective (eacl/with-schema - base schema-without-ban {:orphan-policy :retain-inert})] + base schema-without-ban {:orphan-policy :retain-inert})] (let [before (datomic/cache-stats client) decision (eacl/check-permission prospective report-demand) after (datomic/cache-stats client)] (is (true? (:allowed? decision))) - (is (true? (:cached? decision))) - (is (= (inc (:managed-hits before)) (:managed-hits after))) + (is (= (not orchestration/*qualified-authorization-enabled?*) (:cached? decision))) + (is (= (cond-> (:managed-hits before) (not orchestration/*qualified-authorization-enabled?*) inc) (:managed-hits after))) (is (= (:puts before) (:puts after))))))))) (deftest transaction-function-expanded-datoms-drive-effects-test diff --git a/modules/eacl/README.md b/modules/eacl/README.md index 298cc589..f9388bb4 100644 --- a/modules/eacl/README.md +++ b/modules/eacl/README.md @@ -15,12 +15,12 @@ This module must not depend on Datomic or a logging backend. ## Dependency and runtime ```clojure -{:deps {dev.eacl/eacl {:mvn/version "8.0.0-SNAPSHOT"}}} +{:deps {dev.eacl/eacl {:mvn/version "9.0.0-SNAPSHOT"}}} ``` The published JAR includes all generated JVM kernel/Dafny runtime classes, `deps.cljs`, and `EaclKernel.browser.js`. Generated kernel classes target Java -26 by default and an explicit source/custom build may compile those classes for +25 by default and an explicit source/custom build may compile those classes for Java 8 through 26. The complete JVM module requires Java 11 or newer because Caffeine 3.2.4 is its manual cache implementation; Java 17 is the supported production runtime floor. CLJS/DataScript uses the separate theronic diff --git a/modules/eacl/src/eacl/authorization/batch.cljc b/modules/eacl/src/eacl/authorization/batch.cljc index 59d9cdd0..58ebce2f 100644 --- a/modules/eacl/src/eacl/authorization/batch.cljc +++ b/modules/eacl/src/eacl/authorization/batch.cljc @@ -22,6 +22,7 @@ (def ^:private request-keys #{:checks + :caveat-context :consistency :timeout-ms :cancellation-token @@ -34,6 +35,7 @@ (def ^:private endpoint-keys #{:type :id :relation}) (def ^:private per-demand-control-keys #{:consistency + :caveat-context :timeout-ms :cancellation-token :cache? @@ -257,10 +259,12 @@ (defn aggregate-counters "Returns safe cumulative counters relative to the start of one batch." [work-before work-after ledger-before ledger-after output-units] - (let [commands (numeric-delta work-before work-after :advanced-datoms) + (let [ledger-delta (request-counters/delta ledger-before ledger-after) + ;; The mandatory ledger includes scans, exact probes, and qualifier + ;; data reads. Traversal observers contain only a subset of that work. + commands (:commands ledger-delta) + fetched-values (:fetched-values ledger-delta) transitions (numeric-delta work-before work-after :queued-work) - fetched-values (numeric-delta work-before work-after :fetched-values) - ledger-delta (request-counters/delta ledger-before ledger-after) candidates (:candidates-examined ledger-delta) probes (:probes ledger-delta) allocation-proxy diff --git a/modules/eacl/src/eacl/authorization/clock.cljc b/modules/eacl/src/eacl/authorization/clock.cljc new file mode 100644 index 00000000..b5422f4b --- /dev/null +++ b/modules/eacl/src/eacl/authorization/clock.cljc @@ -0,0 +1,17 @@ +(ns eacl.authorization.clock + "Trusted wall time with a process-local non-decreasing high-water mark." + (:require [eacl.caveats.values :as values])) + +(defn clock + "Wraps a trusted runtime clock, never request context data. Capture once per + selected operation and retain that value through all edges and batches." + [sample] + (when-not (fn? sample) (values/error! :clock-function)) + (let [high-water (atom nil)] + (fn [] + (let [time (sample)] + (when-not (values/valid-time? time) (values/error! :clock-time)) + (swap! high-water #(if (nil? %) time (max % time))))))) + +(defonce system-clock + (clock #?(:clj (fn [] (System/currentTimeMillis)) :cljs (fn [] (.now js/Date))))) diff --git a/modules/eacl/src/eacl/authorization/context.cljc b/modules/eacl/src/eacl/authorization/context.cljc new file mode 100644 index 00000000..6c4b1e65 --- /dev/null +++ b/modules/eacl/src/eacl/authorization/context.cljc @@ -0,0 +1,82 @@ +(ns eacl.authorization.context + "One bounded public context, independent of any particular Caveat schema." + (:refer-clojure :exclude [identity]) + (:require [eacl.caveats.values :as values] + [eacl.exact-integer :as integer])) + +(def ^:private encoding-options + {:maximum-size (:context-utf8-bytes values/limits) + :maximum-entries (:context-total-entries values/limits) + :maximum-depth 8}) + +(deftype ^:private PreparedContext [value identity]) + +(defn prepared? [context] (instance? PreparedContext context)) +(defn value [context] (.-value ^PreparedContext context)) +(defn identity [context] (.-identity ^PreparedContext context)) + +(defn- string! [s] + (when-not (string? s) (values/error! :context-type)) + (when (> (count s) (:string-utf8-bytes values/limits)) + (values/error! :resource-limit {:limit :string-utf8-bytes})) + (let [size (try (values/utf8-size s) + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) _ + (values/error! :context-type)))] + (when (> size (:string-utf8-bytes values/limits)) + (values/error! :resource-limit {:limit :string-utf8-bytes})))) + +(defn- scalar-type [v] + (cond + (boolean? v) :bool + (integer/exact? v) :int + (string? v) (do (string! v) :string) + (and (vector? v) (= 2 (count v)) (= :timestamp (first v)) + (values/valid-time? (second v))) :timestamp + :else nil)) + +(defn- value-size! [v] + (if (scalar-type v) + (if (vector? v) 3 1) + (do + (when-not (or (map? v) (vector? v)) (values/error! :context-type)) + (when (> (count v) (:container-entries values/limits)) + (values/error! :resource-limit {:limit :container-entries})) + (when (map? v) (doseq [key (keys v)] (string! key))) + (let [items (if (map? v) (vals v) v) + expected (when (seq items) (scalar-type (first items)))] + (reduce (fn [size item] + (when-not (and expected (= expected (scalar-type item))) + (values/error! :context-type)) + (+ size (if (= :timestamp expected) 3 1))) + (if (map? v) (inc (count v)) 1) items))))) + +(def ^:private empty-context (PreparedContext. {} (values/encode-bounded {} encoding-options))) + +(defn prepare + "Validates all supplied fields before I/O or reuse. Declared parameter types + are checked later by each demanded Caveat. The complete canonical identity + includes fields that no demanded Caveat uses." + [context] + (when-not (map? context) (values/error! :context-type)) + (when (> (count context) (:context-total-entries values/limits)) + (values/error! :resource-limit {:limit :context-size})) + (if (empty? context) + empty-context + (do + (reduce-kv + (fn [size key v] + (when-not (values/parameter-name? key) (values/error! :parameter-name)) + (let [size (+ size 1 (value-size! v))] + (when (> size (:context-total-entries values/limits)) + (values/error! :resource-limit {:limit :context-size})) + size)) + 1 context) + (PreparedContext. context (values/encode-bounded context encoding-options))))) + +(defn project + "Projects a validated request onto one admitted Caveat's parameter names." + [context parameters] + (let [source (value context)] + (reduce (fn [result [name _]] + (if (contains? source name) (assoc result name (get source name)) result)) + {} parameters))) diff --git a/modules/eacl/src/eacl/authorization/data.cljc b/modules/eacl/src/eacl/authorization/data.cljc new file mode 100644 index 00000000..74e11967 --- /dev/null +++ b/modules/eacl/src/eacl/authorization/data.cljc @@ -0,0 +1,38 @@ +(ns eacl.authorization.data + "Bounded native entity data for qualifier, Caveat, and Relation resolution. + Unknown fields remain visible to the strict semantic decoders." + (:require [eacl.caveats.definition :as definition] + [eacl.execution :as execution] + [eacl.relationships.qualifier :as qualifier] + [eacl.request.counters :as counters])) + +(def maximum-entity-facts 4096) +(def capability :bounded-snapshot-data-v1) +(def ^:private single-valued-attributes + (into (conj qualifier/attributes :eacl.relation/allows-unqualified?) definition/attributes)) + +(defn collect + "Consumes at most the fixed fact ceiling plus one overflow witness. Native + readers must also apply their index limit when their API realizes eagerly. + Attribute id resolution and assertion versions belong to the same basis." + [eid datoms attribute-key assertion-version?] + (when-not (qualifier/concrete-eid? eid) (qualifier/error! :qualification-entity-id)) + (loop [rows (seq datoms) entity nil version nil n 0] + (if-let [datom (first rows)] + (do + (counters/add-fetched-values!) + (when (= n maximum-entity-facts) (qualifier/error! :qualification-entity-limit)) + (execution/check! :qualification-data/fact) + (when-not (= eid (:e datom)) (qualifier/error! :qualification-entity-bound)) + (let [attribute (attribute-key (:a datom)) + value (:v datom)] + (when-not (keyword? attribute) (qualifier/error! :qualification-attribute)) + (when (and (contains? single-valued-attributes attribute) (contains? entity attribute)) + (qualifier/error! :nonfunctional-qualification-entity)) + (recur (next rows) + (if (= :eacl.relation/caveats attribute) + (update (or entity {:db/id eid}) attribute (fnil conj #{}) value) + (assoc (or entity {:db/id eid}) attribute value)) + (if (and assertion-version? (= qualifier/marker-attribute attribute)) (:tx datom) version) + (inc n)))) + {:entity entity :version version :fact-count n}))) diff --git a/modules/eacl/src/eacl/authorization/evidence.cljc b/modules/eacl/src/eacl/authorization/evidence.cljc new file mode 100644 index 00000000..991c54dc --- /dev/null +++ b/modules/eacl/src/eacl/authorization/evidence.cljc @@ -0,0 +1,238 @@ +(ns eacl.authorization.evidence + "Bounded conditional permission evidence and temporal witness certificates. + Timeless definite values are plain booleans. Only qualified/conditional + values allocate evidence; no evaluator or database is invoked here." + (:require [clojure.set :as set] + [eacl.caveats.values :as values])) + +(def format-version 1) +(def limits {:nodes 256 :depth 32 :work 65536 :missing-fields 128 :faults 32}) +(def ^:private wire-limits + {:maximum-size 4194304 :maximum-entries 16384 :maximum-depth 80}) +(def ^:private atom-limits + {:maximum-size 4194304 :maximum-entries 16384 :maximum-depth 40}) + +(defrecord Evidence [value valid-until-ms complete?]) + +(defn error! [reason] + (throw (ex-info "Invalid or excessive authorization evidence." + {:type :eacl.authorization/invalid-evidence + :eacl/error :eacl.authorization/invalid-evidence :reason reason}))) + +(defn value [e] (if (boolean? e) e (:value e))) +(defn valid-until [e] (when-not (boolean? e) (:valid-until-ms e))) +(defn complete? [e] (if (boolean? e) true (:complete? e))) +(defn fault? [e] (let [v (value e)] (and (vector? v) (= :fault (first v))))) +(defn has? [e] (true? (value e))) +(defn no? [e] (false? (value e))) + +(defn permissionship [e] + (cond (has? e) :has-permission + (no? e) :no-permission + (fault? e) :evaluation-failure + :else :conditional-permission)) + +(defn- wrap [v end complete] + (if (and (boolean? v) (nil? end) complete) v (->Evidence v end complete))) + +(defn with-certificate [e end complete] + (when-not (or (boolean? e) (instance? Evidence e)) (error! :evidence-shape)) + (when-not (and (or (nil? end) (values/valid-time? end)) (boolean? complete)) + (error! :certificate)) + (wrap (value e) end complete)) + +(defn before? [time end] (or (nil? end) (< time end))) +(defn reusable? [e start time] + (and (complete? e) (not (fault? e)) (<= start time) (before? time (valid-until e)))) +(defn meet [a b] (cond (nil? a) b (nil? b) a :else (min a b))) + +(defn- missing-vector [missing] + (when-not (and (coll? missing) (seq missing) + (<= (count missing) (:missing-fields limits)) + (every? values/parameter-name? missing)) + (error! :missing-fields)) + (vec (sort (set missing)))) + +(defn conditional + "Creates one residual atom. Identity is complete canonical data containing + the Caveat/profile, parameter types, and already-bound residual plan; it + must not be a digest alone. Missing fields are part of the atom identity." + [identity missing] + (when-not (and (vector? identity) (seq identity)) (error! :atom-identity)) + (let [missing (missing-vector missing) + key (values/encode-bounded [identity missing] atom-limits)] + (->Evidence [[key missing] false true] nil true))) + +(defn fault + "Preserves a sanitized typed failure. Exception messages and arbitrary + backend data are deliberately excluded from reusable evidence." + [type reason] + (when-not (and (keyword? type) (keyword? reason)) (error! :fault-shape)) + (->Evidence [:fault [[type reason]]] nil true)) + +(defn throw-if-fault! + "Page traversal cannot publish partial results after an authoritative + evaluation fault. Preserve only the bounded, sanitized fault reasons." + [e] + (when (fault? e) + (throw (ex-info "Qualified page evaluation failed." + {:type :eacl.authorization/evaluation-failure + :eacl/error :eacl.authorization/evaluation-failure + :faults (second (value e))}))) + e) + +(defn- charge! [budget] + (when (> (vswap! budget inc) (:work limits)) (error! :work-limit))) + +(defn- branch [node atom high?] + (if (and (vector? node) (= atom (first node))) + (nth node (if high? 2 1)) node)) + +(defn- boolean-result [op a b] + (case op :union (or a b) :intersection (and a b) + :arrow (and a b) :exclusion (and a (not b)))) + +(defn- apply-node [op a b depth budget] + (charge! budget) + (when (> depth (:depth limits)) (error! :depth-limit)) + (cond + (and (boolean? a) (boolean? b)) (boolean-result op a b) + (= a b) (if (= op :exclusion) false a) + (and (= op :union) (or (true? a) (true? b))) true + (and (#{:intersection :arrow} op) (or (false? a) (false? b))) false + (and (= op :exclusion) (or (false? a) (true? b))) false + :else + (let [aa (when (vector? a) (first a)) ba (when (vector? b) (first b)) + atom (cond (nil? aa) ba (nil? ba) aa + (neg? (compare (first aa) (first ba))) aa :else ba) + low (apply-node op (branch a atom false) (branch b atom false) (inc depth) budget) + high (apply-node op (branch a atom true) (branch b atom true) (inc depth) budget)] + (if (= low high) low [atom low high])))) + +(defn- check-node-budget! [node] + (loop [pending [[node 0]] n 0] + (when-let [[v depth] (peek pending)] + (when (or (>= n (:nodes limits)) (> depth (:depth limits))) (error! :node-limit)) + (recur (if (boolean? v) (pop pending) + (conj (pop pending) [(nth v 1) (inc depth)] [(nth v 2) (inc depth)])) + (inc n))))) + +(defn- faults [e] (if (fault? e) (second (value e)) [])) + +(defn- combine-value [op a b] + (cond + (and (boolean? (value a)) (boolean? (value b))) + (boolean-result op (value a) (value b)) + (or (fault? a) (fault? b)) + (let [reasons (vec (sort-by pr-str (set/union (set (faults a)) (set (faults b)))))] + (when (> (count reasons) (:faults limits)) (error! :fault-limit)) + [:fault reasons]) + :else + (let [result (apply-node op (value a) (value b) 0 (volatile! 0))] + (check-node-budget! result) + result))) + +(defn combine + "Composes exactly the evidence already demanded by an operator. A decisive + complete witness can retain its own deadline; non-decisive and faulty + outcomes need both child certificates. Faults precede Boolean absorbers." + [op a b] + (when-not (contains? #{:union :intersection :exclusion :arrow} op) (error! :operator)) + (if (and (boolean? a) (boolean? b)) + (boolean-result op a b) + (let [v (combine-value op a b) + faulted? (or (fault? a) (fault? b)) + left? (and (not faulted?) (complete? a) (if (= op :union) (has? a) (no? a))) + right? (and (not faulted?) (complete? b) (if (#{:union :exclusion} op) (has? b) (no? b))) + end (cond left? (valid-until a) right? (valid-until b) + :else (meet (valid-until a) (valid-until b))) + complete (cond left? true right? true :else (and (complete? a) (complete? b)))] + (wrap v end complete)))) + +(defn missing-fields [e] + (if (or (boolean? (value e)) (fault? e)) + [] + (loop [pending [(value e)] missing #{}] + (if (seq pending) + (let [node (peek pending)] + (if (boolean? node) + (recur (pop pending) missing) + (let [next-missing (into missing (second (first node)))] + (when (> (count next-missing) (:missing-fields limits)) (error! :missing-fields)) + (recur (conj (pop pending) (nth node 1) (nth node 2)) next-missing)))) + (vec (sort missing)))))) + +(defn- validate-atom! [atom] + (when-not (and (vector? atom) (= 2 (count atom))) (error! :atom-shape)) + (let [[key missing] atom] + (when-not (and (string? key) + (= missing (missing-vector missing))) + (error! :atom-shape)) + (let [decoded (values/decode-bounded key atom-limits)] + (when-not (and (vector? decoded) (= 2 (count decoded)) + (vector? (first decoded)) (seq (first decoded)) + (= missing (second decoded)) + (= key (values/encode-bounded decoded atom-limits))) + (error! :atom-identity))) + atom)) + +(defn- validate-value! [v] + (if (and (vector? v) (= :fault (first v))) + (let [reasons (second v)] + (when-not (and (= 2 (count v)) (vector? reasons) (seq reasons) + (<= (count reasons) (:faults limits)) + (every? #(and (vector? %) (= 2 (count %)) (every? keyword? %)) reasons) + (= reasons (vec (sort-by pr-str (set reasons))))) + (error! :fault-shape))) + (loop [pending [[v nil 0]] n 0] + (when-let [[node previous depth] (peek pending)] + (when (or (>= n (:nodes limits)) (> depth (:depth limits))) (error! :node-limit)) + (if (boolean? node) + (recur (pop pending) (inc n)) + (do + (when-not (and (vector? node) (= 3 (count node))) (error! :node-shape)) + (let [[atom low high] node + key (first (validate-atom! atom))] + (when (or (= low high) (and previous (not (neg? (compare previous key))))) + (error! :noncanonical-node)) + (recur (conj (pop pending) [low key (inc depth)] [high key (inc depth)]) (inc n)))))))) + v) + +(def ^:private true-wire + (values/encode-bounded [:eacl.authorization/evidence format-version true nil true] wire-limits)) +(def ^:private false-wire + (values/encode-bounded [:eacl.authorization/evidence format-version false nil true] wire-limits)) + +(defn encode [e] + (cond + (true? e) true-wire + (false? e) false-wire + :else + (do + (when-not (or (boolean? e) (instance? Evidence e)) (error! :evidence-shape)) + (when-not (boolean? (value e)) (validate-value! (value e))) + (with-certificate e (valid-until e) (complete? e)) + (if (boolean? (value e)) + ;; This closed scalar envelope has a fixed small wire bound. It is + ;; byte-identical to the generic encoder without a BDD walk or a + ;; reconstructed canonical collection for every witnessed edge. + (str "[:eacl.authorization/evidence " format-version " " (value e) " " + (if-some [end (valid-until e)] end "nil") " " (complete? e) "]") + (do + (missing-fields e) + (values/encode-bounded [:eacl.authorization/evidence format-version (value e) + (valid-until e) (complete? e)] wire-limits)))))) + +(defn decode [payload] + (cond + (= true-wire payload) true + (= false-wire payload) false + :else + (let [wire (values/decode-bounded payload wire-limits)] + (when-not (and (vector? wire) (= 5 (count wire)) + (= :eacl.authorization/evidence (first wire)) (= format-version (second wire))) + (error! :wire-shape)) + (let [[_ _ v end complete] wire + result (with-certificate (->Evidence (validate-value! v) nil true) end complete)] + (when-not (= payload (encode result)) (error! :noncanonical-wire)) + result)))) diff --git a/modules/eacl/src/eacl/authorization/evidence_index.cljc b/modules/eacl/src/eacl/authorization/evidence_index.cljc new file mode 100644 index 00000000..d545c762 --- /dev/null +++ b/modules/eacl/src/eacl/authorization/evidence_index.cljc @@ -0,0 +1,57 @@ +(ns eacl.authorization.evidence-index + "Bounded positional evidence aggregation for incremental operator joins. + A changed child updates only its ancestor path; unchanged roots still + retain changed leaves for later transitions." + (:require [eacl.authorization.evidence :as evidence])) + +(def maximum-width 1048576) +(defrecord EvidenceIndex [operation width offset tree]) + +(defn value [index] (nth (:tree index) 1)) +(defn storage-weight [index] (count (:tree index))) + +(defn build + "Builds a deterministic balanced tree. `step!` charges each composition to + the owning evaluator's existing work and cancellation contract." + [operation values step!] + (when-not (and (contains? #{:union :intersection} operation) (vector? values) + (<= (count values) maximum-width) (fn? step!)) + (evidence/error! :index-shape)) + (let [width (count values) + offset (loop [n 1] (if (>= n width) n (recur (* 2 n)))) + identity (= operation :intersection) + tree (loop [i 0 tree (transient (vec (repeat (* 2 offset) identity)))] + (if (= i width) + tree + (recur (inc i) (assoc! tree (+ offset i) (nth values i))))) + tree (loop [i (dec offset) tree tree] + (if (zero? i) + (persistent! tree) + (do + (step!) + (recur (dec i) (assoc! tree i (evidence/combine operation + (nth tree (* 2 i)) + (nth tree (inc (* 2 i)))))))))] + (->EvidenceIndex operation width offset tree))) + +(defn replace-slot + "Replaces one already validated child value, preserving canonical evidence. + The tree shape and therefore witness selection stay fixed for its lifetime." + [index slot replacement step!] + (when-not (and (integer? slot) (<= 0 slot) (< slot (:width index)) (fn? step!)) + (evidence/error! :index-slot)) + (let [leaf (+ (:offset index) slot)] + (if (= replacement (nth (:tree index) leaf)) + index + (assoc index :tree + (loop [i (quot leaf 2) tree (assoc (:tree index) leaf replacement)] + (if (zero? i) + tree + (do + (step!) + (let [next-value (evidence/combine (:operation index) + (nth tree (* 2 i)) + (nth tree (inc (* 2 i))))] + (if (= next-value (nth tree i)) + tree + (recur (quot i 2) (assoc tree i next-value))))))))))) diff --git a/modules/eacl/src/eacl/authorization/filters.cljc b/modules/eacl/src/eacl/authorization/filters.cljc index ee711772..3b51281b 100644 --- a/modules/eacl/src/eacl/authorization/filters.cljc +++ b/modules/eacl/src/eacl/authorization/filters.cljc @@ -5,7 +5,7 @@ (def ^:private page-control-keys #{:first :last :after :before :cursor :limit :page/basis :consistency :cache? :populate-cache? :evaluation - :timeout-ms :cancellation-token + :timeout-ms :cancellation-token :caveat-context :result-policy :aggregate-limits}) (def ^:private lookup-resource-keys (into page-control-keys diff --git a/modules/eacl/src/eacl/authorization/qualification.cljc b/modules/eacl/src/eacl/authorization/qualification.cljc new file mode 100644 index 00000000..79ea6b0e --- /dev/null +++ b/modules/eacl/src/eacl/authorization/qualification.cljc @@ -0,0 +1,249 @@ +(ns eacl.authorization.qualification + "One edge qualification seam for a selected immutable request basis. + This layer resolves data only; traversal remains in the existing engine." + (:require [eacl.authorization.context :as context] + [eacl.authorization.data :as data] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualifier-cache :as qualifier-cache] + [eacl.authorization.temporal :as temporal] + [eacl.backend.v8 :as backend] + [eacl.cache.standard-lru :as lru] + [eacl.caveats.definition :as definition] + [eacl.caveats.evaluator :as evaluator] + [eacl.caveats.values :as values] + [eacl.execution :as execution] + [eacl.relationships.edge :as edge] + [eacl.relationships.qualifier :as qualifier] + [eacl.request.counters :as counters])) + +(def maximum-request-entries 100000) +(defrecord Qualification [time context evaluator entity version basis cache memos lookup prepared-context populate-cache? temporal-state]) + +(defn request + "Captures trusted time and exact source/basis identity supplied by request + orchestration. Entity/version callbacks close over that same native basis. + Decode retention is optional and requires exact basis or complete content identity." + [{:keys [time context evaluator entity version basis cache lookup prepared-context] :as options}] + (when-not (and (values/valid-time? time) (or (nil? context) (map? context)) + (or (and (fn? entity) (fn? version) (nil? lookup)) + (and (fn? lookup) (nil? entity) (nil? version))) + (map? basis) (seq basis) + (or (nil? cache) (lru/store? cache) (qualifier-cache/cache? cache)) + (or (nil? prepared-context) + (context/prepared? prepared-context))) + (qualifier/error! :qualification-context)) + (let [prepared (if (and prepared-context + (or (not (contains? options :context)) + (identical? context (context/value prepared-context)))) + prepared-context + (context/prepare (or context {})))] + (->Qualification time (context/value prepared) evaluator entity version basis cache + (delay (volatile! {})) lookup prepared (not (false? (:populate-cache? options))) + (delay (volatile! nil))))) + +(defn require-publication! [adapter] + ;; Check before any answer/cache route, including empty or ordinary edges. + ;; Request context construction releases its selected snapshot on failure. + (when-not (some backend/qualified-publication-capabilities + (:qualified-publication (backend/capabilities adapter))) + (throw (ex-info "Qualified authorization requires a certified native publication strategy." + {:type :eacl/unsupported-capability :eacl/error :eacl/unsupported-capability + :capability :qualified-relationship-publication})))) + +(defn request-from-adapter + "Uses only the selected immutable adapter's bounded, metered data operation. + Entity contents and their assertion version arrive in the same read." + [adapter options] + (backend/require-capability! adapter :qualification data/capability) + (require-publication! adapter) + (request + (assoc (dissoc options :entity :version) + :lookup (fn [eid] + (execution/check! :qualification-data/before) + (counters/add-commands!) + (let [result (backend/invoke adapter :qualification-data eid)] + (execution/check! :qualification-data/after) + result))))) + +(defn- memo! [request key build] + (let [memos (force (:memos request)) current @memos + result + (if (contains? current key) + (get current key) + (do + (when (>= (count current) maximum-request-entries) (qualifier/error! :request-qualifier-limit)) + (let [result (try {:value (build)} + (catch #?(:clj Exception :cljs :default) error {:error error}))] + (vswap! memos assoc key result) + result)))] + (if-let [error (:error result)] (throw error) (:value result)))) + +(defn- entity-data [request eid] + (if-let [lookup (:lookup request)] + (memo! request [:entity-data eid] + #(let [result (lookup eid)] + (when-not (and (map? result) (= #{:entity :version :fact-count} (set (keys result))) + (or (nil? (:entity result)) (map? (:entity result))) + (or (nil? (:entity result)) (= eid (get-in result [:entity :db/id]))) + (or (nil? (:version result)) (qualifier/concrete-eid? (:version result))) + (integer? (:fact-count result)) + (<= 0 (:fact-count result) data/maximum-entity-facts)) + (qualifier/error! :qualification-data)) + result)) + (memo! request [:entity-data eid] #(hash-map :entity ((:entity request) eid))))) + +(defn- named-definition [request eid] + (memo! request [:caveat eid] + #(let [entity (:entity (entity-data request eid))] + {:entity entity :header (definition/decode-header entity)}))) + +(defn exact-reuse-identity + "Complete collision-checked semantic scope for same-basis, same-time reuse. + Cross-time acceptance additionally requires a certified evidence interval." + [request] + (memo! request [:reuse-identity] + #(vector qualifier/format-version (:basis request) (:time request) + (context/identity (:prepared-context request)) + (when-let [engine (:evaluator request)] + (select-keys (evaluator/descriptor engine) + [:profile :profile-fingerprint :fingerprint :capability-version]))))) + +(defn- allowed! [request relation-id caveat-id] + (let [allowed (memo! request [:relation relation-id] + #(let [relation (:entity (entity-data request relation-id))] + (when-not (and (map? relation) (seq relation)) + (qualifier/error! :missing-relation)) + (qualifier/relation-allowance relation)))] + (when-not (contains? allowed caveat-id) (qualifier/error! :caveat-not-allowed)))) + +(defn- qualifier-input [request qid] + (let [data (entity-data request qid) + entity (:entity data) + caveat-id (get entity qualifier/caveat-attribute) + _ (when (and (some? caveat-id) (not (qualifier/concrete-eid? caveat-id))) + (qualifier/error! :qualifier-ref)) + named (when caveat-id (named-definition request caveat-id)) + version (if (:lookup request) (:version data) ((:version request) qid))] + (when (and (some? version) (not (qualifier/concrete-eid? version))) + (qualifier/error! :qualifier-version)) + {:entity entity :named named :version version})) + +(defn- decode-input [{:keys [entity named version]}] + {:qualifier (qualifier/decode entity (get-in named [:header :parameters] [])) + :definition named :version version}) + +(defn- cached-decoded [request relation-id qid cache] + (let [exact (qualifier-cache/exact-key (:basis request) relation-id qid) + cached (qualifier-cache/lookup! cache exact)] + (if (:found? cached) + (:value cached) + (let [{:keys [entity named version] :as input} (qualifier-input request qid) + relation (:entity (entity-data request relation-id)) + content (qualifier-cache/content-key (:basis request) relation-id qid version entity (:entity named) relation) + cached (qualifier-cache/lookup! cache content) + result (if (:found? cached) (:value cached) (decode-input input))] + (if (:populate-cache? request) + (qualifier-cache/publish! cache exact content result) + result))))) + +(defn observe-interval! + "Imports a certificate for already accepted retained work. Ordinary timeless + evidence leaves the sparse request ledger unrealized." + [request interval] + (when request + (when-not (and (temporal/interval-valid? interval) + (temporal/reusable? interval (:time request) true)) + (evidence/error! :retained-temporal-certificate)) + (when (or (:valid-until-ms interval) (not (:complete? interval))) + (let [state (force (:temporal-state request)) + previous @state + end (evidence/meet (:valid-until-ms previous) (:valid-until-ms interval)) + complete? (and (:complete? interval) (get previous :complete? true))] + (when (or (not= end (:valid-until-ms previous)) + (not= complete? (get previous :complete? true))) + (vreset! state {:valid-until-ms end :complete? complete?}))))) + interval) + +(defn observe-evidence! [request value] + (when request + (observe-interval! request (temporal/interval (:time request) (evidence/valid-until value) + (and (not (evidence/fault? value)) (evidence/complete? value))))) + value) + +(defn certificate [request] + (let [state (when (realized? (:temporal-state request)) @(force (:temporal-state request)))] + (temporal/interval (:time request) (:valid-until-ms state) (get state :complete? true)))) + +(defn- decoded [request relation-id qid] + (memo! request [:qualifier qid] + #(let [cache (:cache request) + result + (if (qualifier-cache/cache? cache) + (cached-decoded request relation-id qid cache) + (let [key [(:basis request) qid qualifier/format-version] + cached (when cache (lru/lookup! cache key))] + (if (:found? cached) + (:value cached) + (let [result (decode-input (qualifier-input request qid))] + (when (and cache (:populate-cache? request)) (lru/put-if-absent! cache key result)) + result)))) + end (get-in result [:qualifier :valid-until-ms])] + (when (and end (< (:time request) end)) + (observe-interval! request (temporal/interval (:time request) end true))) + result))) + +(defn inspect + "Returns decoded public qualifier metadata without running a Caveat program. + The request memo and exact qualifier validation are shared with authorization." + [request relation-id qualifier-id] + (if-not qualifier-id + {} + (let [{:keys [qualifier definition]} (decoded request relation-id qualifier-id) + {:keys [caveat caveat-context valid-until-ms]} qualifier] + (allowed! request relation-id caveat) + (cond-> {} + caveat (assoc :caveat (get-in definition [:header :name])) + (seq caveat-context) (assoc :caveat-context caveat-context) + valid-until-ms (assoc :valid-until-ms valid-until-ms))))) + +(defn- caveat-evidence [request named bound] + (let [projected (context/project (:prepared-context request) (get-in named [:header :parameters])) + result (evaluator/evaluate (:evaluator request) (:entity named) projected bound)] + (case (:outcome result) + :true true + :false false + :conditional + (evidence/conditional + [values/profile-id evaluator/profile-fingerprint (:entity named) + (get-in named [:header :parameters]) (:residual result)] + (:missing-fields result)) + :error (evidence/fault :eacl.caveat/evaluation + (if (keyword? (:reason result)) (:reason result) :invalid-outcome)) + (evidence/fault :eacl.caveat/evaluation :invalid-outcome)))) + +(defn qualify + "Returns evidence for one compact native edge. The ordinary integer branch + allocates nothing and never dereferences request state. All authoritative + faults remain faults, including faults on subtracting edges." + [request relation-id compact-edge] + (if-not (vector? compact-edge) + (some? compact-edge) + (do + (execution/check! :qualifier-resolution) + (try + (when-not (edge/valid? compact-edge) (qualifier/error! :qualifier-ref)) + (let [{:keys [qualifier definition]} (decoded request relation-id (edge/qualifier-id compact-edge)) + {:keys [caveat caveat-context valid-until-ms]} qualifier] + (allowed! request relation-id caveat) + (if (and valid-until-ms (>= (:time request) valid-until-ms)) + false + (evidence/with-certificate + (if caveat (caveat-evidence request definition caveat-context) true) + valid-until-ms true))) + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (let [{:keys [type reason]} (ex-data error)] + (if (contains? #{:eacl.qualifier/invalid :eacl.caveat/invalid + :eacl.caveat/evaluator-unavailable + :eacl.authorization/invalid-evidence} type) + (evidence/fault type (if (keyword? reason) reason :unavailable)) + (throw error)))))))) diff --git a/modules/eacl/src/eacl/authorization/qualifier_cache.cljc b/modules/eacl/src/eacl/authorization/qualifier_cache.cljc new file mode 100644 index 00000000..98f7e333 --- /dev/null +++ b/modules/eacl/src/eacl/authorization/qualifier_cache.cljc @@ -0,0 +1,45 @@ +(ns eacl.authorization.qualifier-cache + "Bounded decoded-data reuse under exact basis or complete native content. + Publication capability alone never certifies an arbitrary source's writers." + (:require [eacl.cache.standard-lru :as lru] + [eacl.relationships.qualifier :as qualifier])) + +(def default-max-entries 256) +(defrecord DecodeCache [entries]) + +(defn cache? [value] (instance? DecodeCache value)) + +(defn normalize-option [option] + (cond + (false? option) false + (nil? option) {:max-entries default-max-entries} + (and (map? option) (= #{:max-entries} (set (keys option))) + (integer? (:max-entries option)) (<= 1 (:max-entries option) 100000)) option + :else (throw (ex-info "Qualifier cache must be false or a bounded :max-entries map." + {:type :eacl/invalid-config :eacl/error :eacl/invalid-config + :key :qualifier-cache :value option})))) + +(defn cache [options] + (when-let [options (normalize-option options)] + (->DecodeCache (lru/store (:max-entries options))))) + +(defn exact-key [basis relation-id qid] + [:exact basis relation-id qid qualifier/format-version]) + +(defn content-key + "Every native field remains in the collision-checked identity. In particular, + a marker assertion version alone does not prove in-place immutability. + Speculative identity and source lifecycle remain after removing basis facts." + [basis relation-id qid version entity named-entity relation] + [:content (dissoc basis :revision :exact-locator :backend-snapshot-id) + relation-id qid version qualifier/format-version entity named-entity relation]) + +(defn lookup! [cache key] + (lru/lookup! (:entries cache) key)) + +(defn publish! [cache exact content decoded] + ;; Two indices share one capacity and immutable value. Only successful data + ;; decoding reaches this point; no contextual evidence enters either index. + (lru/put-if-absent! (:entries cache) content decoded) + (lru/put-if-absent! (:entries cache) exact decoded) + decoded) diff --git a/modules/eacl/src/eacl/authorization/result.cljc b/modules/eacl/src/eacl/authorization/result.cljc new file mode 100644 index 00000000..e6b438fb --- /dev/null +++ b/modules/eacl/src/eacl/authorization/result.cljc @@ -0,0 +1,62 @@ +(ns eacl.authorization.result + "Public permissionship projection of completed bounded evidence." + (:require [eacl.authorization.evidence :as evidence])) + +(defn result-policy + "Absent policy preserves definite-only lookup/count compatibility." + [request] + (let [policy (get request :result-policy :definite)] + (when-not (#{:definite :detailed} policy) + (throw (ex-info "Result policy must be :definite or :detailed." + {:type :eacl.authorization/invalid-result-policy + :eacl/error :eacl.authorization/invalid-result-policy}))) + policy)) + +(defn count-result + "Projects count categories only when requested, including empty results." + [{:keys [count truncated? definite-count conditional-count]} limit policy] + (cond-> {:count count :limit (or limit -1)} + (some? limit) (assoc :truncated? truncated?) + (= :detailed policy) + (assoc :definite-count (or definite-count count) + :conditional-count (or conditional-count 0)))) + +(defn check-result + "Produces a detailed result without losing conditional evidence or faults. + Residuals use the bounded canonical evidence encoding, including its format + marker; callers should supply missing fields in a new check request." + [value] + (evidence/throw-if-fault! value) + (let [membership (evidence/permissionship value)] + (cond-> {:allowed? (evidence/has? value) :permissionship membership} + (= :conditional-permission membership) + (assoc :missing-fields (evidence/missing-fields value) + :residual (evidence/encode value))))) + +(defn cache-value? + "Cache ingress accepts only canonical completed evidence without faults." + [value] + (try + (and (string? value) (not (evidence/fault? (evidence/decode value)))) + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) _ false))) + +(defn lookup-result + "Keeps the reusable object identity separate from its detailed decision." + [object value] + (assoc (check-result value) :object object)) + +(defn lookup-result-valid? + "Closed detailed lookup transport; no denials, faults, or inconsistent residuals." + [object? item] + (try + (and (map? item) + (object? (:object item)) + (case (:permissionship item) + :has-permission + (= item (lookup-result (:object item) true)) + :conditional-permission + (let [value (evidence/decode (:residual item))] + (and (= :conditional-permission (evidence/permissionship value)) + (= item (lookup-result (:object item) value)))) + false)) + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) _ false))) diff --git a/modules/eacl/src/eacl/authorization/temporal.cljc b/modules/eacl/src/eacl/authorization/temporal.cljc new file mode 100644 index 00000000..6280beb6 --- /dev/null +++ b/modules/eacl/src/eacl/authorization/temporal.cljc @@ -0,0 +1,102 @@ +(ns eacl.authorization.temporal + "Completed point-answer intervals, independent of retention and wall timers." + (:require [eacl.authorization.evidence :as evidence] + [eacl.caveats.values :as values])) + +(def point-format :eacl.authorization/temporal-point-v1) +(def collection-format :eacl.authorization/temporal-collection-v2) + +(defn point-answer [time proof] + (evidence/throw-if-fault! proof) + {:format point-format :start-ms time + :valid-until-ms (evidence/valid-until proof) + :complete? (evidence/complete? proof) + :kind (evidence/permissionship proof) + :value (evidence/encode proof)}) + +(defn point-answer-valid? [answer] + (try + (and (map? answer) + (= #{:format :start-ms :valid-until-ms :complete? :kind :value} (set (keys answer))) + (= point-format (:format answer)) + (values/valid-time? (:start-ms answer)) + (evidence/before? (:start-ms answer) (:valid-until-ms answer)) + (let [proof (evidence/decode (:value answer))] + (and (not (evidence/fault? proof)) + (= (:kind answer) (evidence/permissionship proof)) + (= (:valid-until-ms answer) (evidence/valid-until proof)) + (= (:complete? answer) (evidence/complete? proof))))) + (catch #?(:clj Exception :cljs :default) _ false))) + +(defn reusable? + "Checks a validated resident entry at the captured time. Missing completeness + permits only its original time and exact basis, never a temporal promotion." + [answer time exact-basis?] + (and (values/valid-time? time) + (<= (:start-ms answer) time) + (evidence/before? time (:valid-until-ms answer)) + (or (:complete? answer) (and exact-basis? (= time (:start-ms answer)))))) + +(defn answer-interval + "Extracts the interval of an admitted point, collection, or rendered answer." + [answer] + (if (= point-format (:format answer)) answer (:qualification-certificate answer))) + +(defn answer-reusable? [answer time exact-basis?] + (when-let [certificate (answer-interval answer)] + (reusable? certificate time exact-basis?))) + +(defn supersedes? + "A later computation can replace a resident interval it could not reuse. + Older pinned computations never evict a newer answer under the same key." + [prior next] + (let [before (answer-interval prior) after (answer-interval next)] + (and (= (:format prior) (:format next)) before after + (< (:start-ms before) (:start-ms after)) + (not (reusable? before (:start-ms after) true))))) + +(defn interval [time end complete?] + {:start-ms time :valid-until-ms end :complete? complete?}) + +(defn interval-valid? [value] + (and (map? value) + (= #{:start-ms :valid-until-ms :complete?} (set (keys value))) + (values/valid-time? (:start-ms value)) + (or (nil? (:valid-until-ms value)) (values/valid-time? (:valid-until-ms value))) + (boolean? (:complete? value)) + (evidence/before? (:start-ms value) (:valid-until-ms value)))) + +(defn intersect-intervals + "Combines certificates for work used together. A missing certificate cannot + certify retained work; callers keep that path at its original time." + [left right] + (when (and (interval-valid? left) (interval-valid? right)) + (let [result (interval (max (:start-ms left) (:start-ms right)) + (evidence/meet (:valid-until-ms left) (:valid-until-ms right)) + (and (:complete? left) (:complete? right)))] + (when (and (interval-valid? result) + (reusable? left (:start-ms result) true) + (reusable? right (:start-ms result) true)) result)))) + +(defn cursor-certificate + "A resumed prefix starts at its new evaluation time while retaining the + series' original time and every prior exclusive deadline. Missing retained + evidence explicitly prevents cross-time reuse; no graph walk repairs it." + [mode time prior current] + (assoc (interval time (evidence/meet (:valid-until-ms prior) (:valid-until-ms current)) + (and (:complete? current) (or (nil? prior) (:complete? prior)))) + :mode mode :original-time-ms (get prior :original-time-ms time))) + +(defn cursor-certificate-valid? [value] + (and (map? value) + (= #{:mode :original-time-ms :start-ms :valid-until-ms :complete?} (set (keys value))) + (contains? #{:pinned :live} (:mode value)) + (values/valid-time? (:original-time-ms value)) + (interval-valid? (dissoc value :mode :original-time-ms)) + (<= (:original-time-ms value) (:start-ms value)) + (or (= :live (:mode value)) (= (:original-time-ms value) (:start-ms value))))) + +(defn cursor-time-valid? [certificate time] + (if (= :pinned (:mode certificate)) + (= time (:original-time-ms certificate)) + (reusable? certificate time true))) diff --git a/modules/eacl/src/eacl/backend/direct_membership.cljc b/modules/eacl/src/eacl/backend/direct_membership.cljc index fe8031bc..36ca12c8 100644 --- a/modules/eacl/src/eacl/backend/direct_membership.cljc +++ b/modules/eacl/src/eacl/backend/direct_membership.cljc @@ -7,7 +7,8 @@ (:require [eacl.backend.v8 :as backend] [eacl.execution :as execution] [eacl.exact-integer :as exact-integer] - [eacl.request.counters :as request-counters])) + [eacl.request.counters :as request-counters] + [eacl.relationships.edge :as edge])) (def cache-miss ::cache-miss) @@ -186,20 +187,21 @@ (direct-match subject-type eid relation-eid resource-type resource-eid))))) -(defn ^:no-doc direct-match-many-checked? - [adapter request] +(defn- match-many-checked + [adapter request edges?] (let [{:keys [candidates]} request] (execution/check! execution/*contract* :direct-membership-batch/before {:candidate-count 0}) (if (empty? candidates) [] - (let [native? (native-batch? adapter) + (let [native? (and (not edges?) (native-batch? adapter)) result (if native? (vec (backend/invoke adapter :direct-match-many? request)) (let [match (scalar-matcher - (backend/direct-match-invoker adapter) request)] + (if edges? (backend/direct-edge-invoker adapter) + (backend/direct-match-invoker adapter)) request)] (loop [index 0 result (transient [])] (if (= index (count candidates)) @@ -216,7 +218,7 @@ matched-count (when-not native? (reduce (fn [total matched?] - (if (true? matched?) (inc total) total)) + (if (if edges? (some? matched?) (true? matched?)) (inc total) total)) 0 result))] (when *physical-stats* @@ -240,10 +242,13 @@ (contract-violation! adapter :aligned-cardinality {:expected (count candidates) :actual (count result)})) - (when-not (every? boolean? result) - (contract-violation! adapter :aligned-boolean-vector :redacted)) + (when-not (every? (if edges? #(or (nil? %) (edge/valid? %)) boolean?) result) + (contract-violation! adapter (if edges? :aligned-compact-edges :aligned-boolean-vector) :redacted)) result)))) +(defn ^:no-doc direct-match-many-checked? [adapter request] + (match-many-checked adapter request false)) + (defn direct-match-many? "Returns one Boolean per input candidate, or throws without returning a partial vector. Native and scalar execution have the same normalized input, @@ -260,17 +265,8 @@ [1 (str (:resource-type descriptor)) (:resource-eid descriptor) (:relation-eid descriptor) (str (:subject-type descriptor))])) -(defn dispatch - "Evaluates an ordered vector of physical leaf probes. - - `cache-lookup` must return a Boolean only for a proof-compatible completed - hit and `cache-miss` otherwise. Misses are deterministically grouped by - normalized descriptor, sorted and deduplicated for locality, split at the - physical cap, then scattered back to the original generator order. No - partial result escapes if any group fails." - ([adapter probes] - (dispatch adapter probes (constantly cache-miss))) - ([adapter probes cache-lookup] +(defn- dispatch-impl + [adapter probes cache-lookup edges?] (when-not (vector? probes) (invalid-request! "Direct-membership probes must be a vector." {:value-type (some-> probes type str)})) @@ -297,7 +293,7 @@ (valid-probe! probe) (let [cached (cache-lookup probe)] (cond - (boolean? cached) + (if edges? (or (nil? cached) (edge/valid? cached)) (boolean? cached)) (recur (inc index) (assoc! results index cached) misses (inc cache-hits)) @@ -339,7 +335,8 @@ (let [request {:direction direction :descriptor descriptor :candidates (vec candidate-chunk)} - decisions (direct-match-many-checked? adapter request)] + decisions (if edges? (match-many-checked adapter request true) + (direct-match-many-checked? adapter request))] (reduce (fn [results [candidate decision]] (reduce #(assoc %1 %2 decision) @@ -355,4 +352,22 @@ (add-stat! :cache-hits cache-hits) (when (some #{::unresolved} completed) (contract-violation! adapter :complete-scatter :redacted)) - completed))) + completed)) + +(defn dispatch + "Groups, sorts, deduplicates, and scatters bounded physical Boolean probes. + Cached values must be proof-compatible Booleans; misses use cache-miss." + ([adapter probes] (dispatch-impl adapter probes (constantly cache-miss) false)) + ([adapter probes cache-lookup] (dispatch-impl adapter probes cache-lookup false))) + +(defn dispatch-edges + "Runs the same bounded grouping/scatter schedule for compact stored edges. + Missing rows remain nil. Permission qualification happens at the consumer, + after physical work has been metered, on the same selected request basis." + [adapter probes] + (dispatch-impl adapter probes (constantly cache-miss) true)) + +(defn dispatch-cached-edges + "Reuses compact physical results only from the owning immutable request." + [adapter probes cache-lookup] + (dispatch-impl adapter probes cache-lookup true)) diff --git a/modules/eacl/src/eacl/backend/v8.cljc b/modules/eacl/src/eacl/backend/v8.cljc index 2881957a..b722ea80 100644 --- a/modules/eacl/src/eacl/backend/v8.cljc +++ b/modules/eacl/src/eacl/backend/v8.cljc @@ -3,7 +3,9 @@ This is the sole production backend boundary for recursive traversal, Relay pagination, deletion, consistency selection, and ordered-generation proofs." - (:require [eacl.exact-integer :as exact-integer] + (:require [eacl.authorization.data :as qualification-data] + [eacl.exact-integer :as exact-integer] + [eacl.relationships.edge :as edge] [eacl.request.counters :as request-counters] [eacl.spicedb.consistency :as consistency])) @@ -17,6 +19,8 @@ optional capability declaration." :canonical-expression-v1) +(def qualified-publication-capabilities #{:atomic-inline-v1 :atomic-prepared-v1}) + (def direct-membership-batch-capability :bounded-aligned-v1) (def maximum-direct-membership-batch-width 256) @@ -61,7 +65,7 @@ (def optional-snapshot-operations "Snapshot operations with fail-closed defaults. They remain visible to the certification boundary without making an uncertified snapshot invalid." - #{:schema-generation :direct-match-many?}) + #{:schema-generation :direct-match-many? :direct-edge :qualification-data}) (def ^:private source-authority-operation-keys #{:select-current :select-authoritative :select-at-least :select-exact @@ -105,12 +109,19 @@ :direct-match? #{:iff-forward-scan-membership :iff-reverse-scan-membership :snapshot-bound} + :direct-edge + #{:optional-capability-paired :stored-eid-or-qualified-pair-or-nil + :iff-forward-scan-membership :iff-reverse-scan-membership + :no-authorization-before-qualification :snapshot-bound} :direct-match-many? #{:optional-capability-paired :immutable-basis :normalized-direct-relation-descriptor :distinct-typed-input :maximum-width-256 :aligned-boolean-result :scalar-equivalent :cooperative-cancellation :atomic-failure :snapshot-bound} + :qualification-data + #{:optional-capability-paired :bounded-entity-facts :unknown-fields-preserved + :snapshot-bound :same-basis-assertion-version-or-nil :physical-facts-metered} :all-permission-nodes #{:finite :exact-schema-coverage :snapshot-bound} :schema-generation @@ -154,7 +165,7 @@ :runtime #{}}) (def ^:private known-capability-groups - (conj (set (keys empty-capabilities)) :direct-membership-batch)) + (conj (set (keys empty-capabilities)) :direct-membership-batch :qualification :qualified-publication)) (def ^:private scan-contract-keys #{:strict-order? :unique? :replayable? :strict-progress? :atomic-chunk?}) @@ -301,6 +312,11 @@ (vec unknown-batch-contracts) :known-direct-membership-batch-contracts #{direct-membership-batch-capability}})) + (when (seq (remove #{qualification-data/capability} (:qualification normalized))) + (invalid-adapter! "Backend declares an unknown qualification data contract." {:backend backend-id})) + (when (or (> (count (:qualified-publication normalized)) 1) + (seq (remove qualified-publication-capabilities (:qualified-publication normalized)))) + (invalid-adapter! "Backend declares an unknown or ambiguous qualified publication contract." {:backend backend-id})) normalized)) (defn normalize-traversal-execution @@ -439,6 +455,10 @@ (invalid-adapter! "Backend advertises ordered generations without a proof-frame operation." {:backend id :capability :ordered-generations})) + (when-not (= (contains? (:qualification normalized) qualification-data/capability) + (fn? (:qualification-data operations))) + (invalid-adapter! "Qualification data capability and operation must be declared together." + {:backend id :operation :qualification-data})) (let [batch-capability? (contains? (:direct-membership-batch normalized) direct-membership-batch-capability) @@ -663,7 +683,8 @@ (let [backend-id (::id adapter) direction (or (:direction options) :asc) bound (:bound-eid options) - inclusive? (true? (:inclusive-bound? options))] + inclusive? (true? (:inclusive-bound? options)) + compact? (true? (:include-qualifier? options))] (when-not (sequential? value) (contract-violation! backend-id operation-key :finite-sequential-result value)) @@ -675,10 +696,12 @@ first? true] (if-not remaining value - (let [item (first remaining)] + (let [raw-item (first remaining) + valid-item? (if compact? (edge/valid? raw-item) (exact-integer/natural? raw-item)) + item (if (and compact? valid-item?) (edge/endpoint raw-item) raw-item)] ;; One combined predicate on the hot path; the failed obligation ;; is classified only on the cold violation branch. - (when-not (exact-integer/natural? item) + (when-not valid-item? (contract-violation! backend-id operation-key (if (exact-integer/exact? item) :nonnegative :exact-integer) @@ -770,6 +793,16 @@ backend-id operation-key :finite-node-set value)) value) + :qualification-data + (do + (when-not (and (map? value) (= #{:entity :version :fact-count} (set (keys value))) + (or (nil? (:entity value)) (map? (:entity value))) + (or (nil? (:version value)) (exact-integer/natural? (:version value))) + (exact-integer/natural? (:fact-count value)) + (<= (:fact-count value) qualification-data/maximum-entity-facts)) + (contract-violation! backend-id operation-key :bounded-qualification-data :redacted)) + value) + :direct-match? (do (when-not (boolean? value) @@ -777,6 +810,12 @@ backend-id operation-key :boolean-result value)) value) + :direct-edge + (do + (when-not (or (nil? value) (edge/valid? value)) + (contract-violation! backend-id operation-key :compact-edge-or-nil value)) + value) + :direct-match-many? (do (when-not (and (sequential? value) (every? boolean? value)) @@ -832,11 +871,11 @@ (observe-invocation! :failed adapter operation-key) (throw error))))))) -(defn ^:no-doc direct-match-invoker - "Captures the immutable scalar direct-membership implementation while - preserving the complete adapter invocation boundary for every candidate." - [adapter] - (let [operation-key :direct-match?] +(defn- direct-invoker + [adapter operation-key] + (when-not (contains? #{:direct-match? :direct-edge} operation-key) + (invalid-adapter! "A direct invoker requires a direct membership operation." + {:operation operation-key})) (if-not (adapter? adapter) (fn [subject-type subject-eid relation-eid resource-type resource-eid] (invoke adapter operation-key subject-type subject-eid relation-eid @@ -852,13 +891,18 @@ (let [value (implementation subject-type subject-eid relation-eid resource-type resource-eid)] (observe-invocation! :after adapter operation-key) - (if (and guarded? (not (boolean? value))) - (contract-violation! - (::id adapter) operation-key :boolean-result value) - value)) + (if guarded? (guard-output! adapter operation-key nil value) value)) (catch #?(:clj Throwable :cljs :default) error (observe-invocation! :failed adapter operation-key) - (throw error)))))))) + (throw error))))))) + +(defn ^:no-doc direct-match-invoker + "Captures immutable direct membership with complete metering and guards." + [adapter] (direct-invoker adapter :direct-match?)) + +(defn ^:no-doc direct-edge-invoker + "Captures compact direct edges with complete metering and guards." + [adapter] (direct-invoker adapter :direct-edge)) (defn- invoke-completed [adapter operation-key guarded? options value] @@ -1022,12 +1066,14 @@ options (or (last args) {}) direction (or (:direction options) :asc) bound (:bound-eid options) - inclusive? (true? (:inclusive-bound? options))] + inclusive? (true? (:inclusive-bound? options)) + compact? (true? (:include-qualifier? options)) + guarded? (runtime-guards? adapter)] (observe-invocation! :after adapter operation-key) (when-not (sequential? value) (contract-violation! (::id adapter) operation-key :finite-sequential-result :redacted)) - (when (and (runtime-guards? adapter) + (when (and guarded? (not (contains? #{:asc :desc} direction))) (contract-violation! (::id adapter) operation-key :known-direction :redacted)) @@ -1039,23 +1085,26 @@ (after-realize!) (if-not items accumulator - (let [item (first items)] - (when (runtime-guards? adapter) - (when-not (exact-integer/natural? item) + (let [item (first items) + valid-item? (or (not guarded?) + (if compact? (edge/valid? item) (exact-integer/natural? item))) + eid (if (and compact? valid-item?) (edge/endpoint item) item)] + (when guarded? + (when-not valid-item? (contract-violation! (::id adapter) operation-key :exact-natural :redacted)) (when (and (some? previous) (not ((if (= :desc direction) > <) - previous item))) + previous eid))) (contract-violation! (::id adapter) operation-key :strict-order :redacted)) (when (and (some? bound) (not (within-bound? - direction bound inclusive? item))) + direction bound inclusive? eid))) (contract-violation! (::id adapter) operation-key :inclusive-exclusive-bound :redacted))) - (recur (rest items) item (step accumulator item))))))) + (recur (rest items) eid (step accumulator item))))))) (catch #?(:clj Throwable :cljs :default) error (observe-invocation! :failed adapter operation-key) (throw error)))) diff --git a/modules/eacl/src/eacl/cache.cljc b/modules/eacl/src/eacl/cache.cljc index 2cc7c6ed..19492a57 100644 --- a/modules/eacl/src/eacl/cache.cljc +++ b/modules/eacl/src/eacl/cache.cljc @@ -4,7 +4,9 @@ Exact and managed reuse are semantic key construction concerns. Storage is only a bounded partial map of opaque keys to immutable completed values; misses compute independently and publication never owns computation." - (:require [eacl.backend.v8 :as backend] + (:require [eacl.authorization.result :as authorization-result] + [eacl.authorization.temporal :as temporal] + [eacl.backend.v8 :as backend] [eacl.cache-identity :as cache-identity] [eacl.cache.key :as cache-key] [eacl.cache.standard-lru :as lru] @@ -13,6 +15,7 @@ [eacl.exact-integer :as exact-integer] [eacl.execution :as execution] [eacl.proof-frame :as proof-frame] + [eacl.relationships.mutations :as relationship-mutations] [eacl.secure-format :as secure] [eacl.subproblem-cache :as subproblem]) #?(:clj (:import [java.util.concurrent.atomic LongAdder]))) @@ -82,7 +85,7 @@ (def ^:private answer-entry-format :eacl.cache/completed-answer-v2) (def rendered-page-entry-format "Version identifier for exact public transport-page values." - :eacl.cache/rendered-page-v4) + :eacl.cache/rendered-page-v5) (defn- metadata-free-portable-data? [value allow-records? {:keys [maximum-depth maximum-entries @@ -888,15 +891,21 @@ expected-limit (if limited? (:count-limit answer-query) -1) - expected-fields (if limited? - #{:count :limit :truncated?} - #{:count :limit}) + detailed? (= :detailed (:result-policy answer-query)) + expected-fields (cond-> #{:count :limit} + limited? (conj :truncated?) + detailed? (conj :definite-count :conditional-count)) count-value (:count value)] (and (map? query) (map? answer-query) (map? value) (= expected-fields (set (keys value))) (portable-natural? count-value) + (or (not detailed?) + (and (portable-natural? (:definite-count value)) + (portable-natural? (:conditional-count value)) + (= count-value (+ (:definite-count value) + (:conditional-count value))))) (if limited? (and (portable-natural? expected-limit) (= expected-limit (:limit value)) @@ -926,11 +935,24 @@ (defn- rendered-relationship-shape? [value] (and (map? value) - (= #{:subject :relation :resource} (set (keys value))) + (every? (into #{:subject :relation :resource} relationship-mutations/qualifier-keys) (keys value)) + (relationship-mutations/canonical-qualifier-metadata? value) (rendered-spice-object-shape? (:subject value)) (unqualified-keyword? (:relation value)) (rendered-spice-object-shape? (:resource value)))) +(defn- internal-relationship-shape? + "Native eids may be Integer or Long. Their representation is an adapter + detail; public transport IDs retain the stricter canonical shape check." + [value] + (and (every? #(let [id (get-in value [% :id])] + (and (integer? id) (pos? id) + (<= id #?(:clj Long/MAX_VALUE :cljs js/Number.MAX_SAFE_INTEGER)))) + [:subject :resource]) + (rendered-relationship-shape? + #?(:clj (-> value (update-in [:subject :id] long) (update-in [:resource :id] long)) + :cljs value)))) + (defn rendered-page-entry-valid? "True for one exact public page with its already-authenticated cursor tokens. @@ -942,40 +964,50 @@ (rendered-page-entry-valid? nil value)) ([semantic-key value] (let [page (:page value) - page-info (:page-info page) - page-info-fields (when (map? page-info) (set (keys page-info))) - operation (:operation semantic-key) - rendered-item? - (case operation - :read-relationships rendered-relationship-shape? - (:lookup-resources :lookup-subjects) - rendered-spice-object-shape? + page-info (:page-info page) + page-info-fields (when (map? page-info) (set (keys page-info))) + operation (:operation semantic-key) + qualified? (if semantic-key (some? (:qualification semantic-key)) + (contains? value :qualification-certificate)) + detailed? (= :detailed (get-in semantic-key [:query :public :result-policy])) + rendered-item? + (case operation + :read-relationships rendered-relationship-shape? + (:lookup-resources :lookup-subjects) + (if detailed? + #(authorization-result/lookup-result-valid? rendered-spice-object-shape? %) + rendered-spice-object-shape?) ;; The one-argument public predicate accepts either supported page ;; shape; publication always supplies the operation-specific key. - (fn [item] - (or (rendered-spice-object-shape? item) - (rendered-relationship-shape? item)))) - token? (fn [candidate] - (or (nil? candidate) - (and (string? candidate) - (pos? (count candidate)))))] - (and (map? value) - (= rendered-page-entry-fields (set (keys value))) - (= rendered-page-entry-format (:format value)) - (map? page) - (= page-answer-fields (set (keys page))) - (vector? (:data page)) - (<= (count (:data page)) 1000) - (every? rendered-item? (:data page)) - (map? page-info) - (every? page-info-fields required-page-info-fields) - (every? allowed-page-info-fields page-info-fields) - (token? (:start-cursor page-info)) - (token? (:end-cursor page-info)) - (boolean? (:has-next-page? page-info)) - (boolean? (:has-previous-page? page-info)) - (or (not (contains? page-info :bounded?)) - (boolean? (:bounded? page-info))))))) + (fn [item] + (or (rendered-spice-object-shape? item) + (rendered-relationship-shape? item)))) + token? (fn [candidate] + (or (nil? candidate) + (and (string? candidate) + (pos? (count candidate)))))] + (and (map? value) + (= (cond-> rendered-page-entry-fields qualified? (conj :qualification-certificate)) + (set (keys value))) + (or (not qualified?) + (and (or (nil? semantic-key) + (= temporal/collection-format (:qualification-certificate-format semantic-key))) + (temporal/interval-valid? (:qualification-certificate value)))) + (= rendered-page-entry-format (:format value)) + (map? page) + (= page-answer-fields (set (keys page))) + (vector? (:data page)) + (<= (count (:data page)) 1000) + (every? rendered-item? (:data page)) + (map? page-info) + (every? page-info-fields required-page-info-fields) + (every? allowed-page-info-fields page-info-fields) + (token? (:start-cursor page-info)) + (token? (:end-cursor page-info)) + (boolean? (:has-next-page? page-info)) + (boolean? (:has-previous-page? page-info)) + (or (not (contains? page-info :bounded?)) + (boolean? (:bounded? page-info))))))) (defn- permission-tree-answer? [value] @@ -1013,6 +1045,16 @@ :else false)) true))) +(defn- lookup-page-answer? + [semantic-key value] + (let [query (:query semantic-key) + detailed? (= :detailed (:result-policy (or (:internal query) (:public query))))] + (and (page-answer? value) + (if detailed? + (every? #(authorization-result/lookup-result-valid? rendered-spice-object-shape? %) + (:data value)) + (not-any? #(and (map? %) (contains? % :object)) (:data value)))))) + (defn completed-answer-value-valid? "Validates one completed authorization answer against its semantic key. @@ -1020,17 +1062,30 @@ snapshot restore. Exact resident lookup is ordinary membership by a complete semantic key, so an already accepted value is not validated again per hit." [operation semantic-key value] - (and (map? semantic-key) - (= operation (:operation semantic-key)) - (case operation - :can? (boolean? value) - :read-relationships (page-answer? value) - :lookup-resources (page-answer? value) - :lookup-subjects (page-answer? value) - :count-resources (count-answer? semantic-key value) - :count-subjects (count-answer? semantic-key value) - :expand-permission-tree (permission-tree-answer? value) - false))) + (let [qualified-result? (and (:qualification semantic-key) + (contains? #{:lookup-resources :lookup-subjects :count-resources :count-subjects :read-relationships} operation)) + certificate (:qualification-certificate value) + value (if qualified-result? (dissoc value :qualification-certificate) value)] + (and (or (not qualified-result?) + (and (= temporal/collection-format (:qualification-certificate-format semantic-key)) + (temporal/interval-valid? certificate))) + (map? semantic-key) + (= operation (:operation semantic-key)) + (case operation + :can? (cond + (= temporal/point-format (:temporal-answer-format semantic-key)) + (and (:qualification semantic-key) (temporal/point-answer-valid? value)) + (:qualification semantic-key) (authorization-result/cache-value? value) + :else (boolean? value)) + :read-relationships (and (page-answer? value) + (or (nil? (:qualification semantic-key)) + (every? internal-relationship-shape? (:data value)))) + :lookup-resources (lookup-page-answer? semantic-key value) + :lookup-subjects (lookup-page-answer? semantic-key value) + :count-resources (count-answer? semantic-key value) + :count-subjects (count-answer? semantic-key value) + :expand-permission-tree (permission-tree-answer? value) + false)))) (defn- answer-snapshot-entry-valid? [key entry] @@ -1206,17 +1261,18 @@ nil)))) (defn- lookup-answer - [subproblem-store key] - (some-> (subproblem/lookup! - subproblem-store :answer key) + [subproblem-store key eligible?] + (some-> (if eligible? + (subproblem/lookup-eligible! subproblem-store :answer key eligible?) + (subproblem/lookup! subproblem-store :answer key)) :value)) (defn- lookup-managed-answer - [subproblem-store key requested-revision] + [subproblem-store key requested-revision temporal? evaluation-time-ms] (some-> (subproblem/lookup-eligible! subproblem-store :answer key - #(managed-answer-causally-eligible? - requested-revision %)) + #(and (managed-answer-causally-eligible? requested-revision %) + (or (not temporal?) (temporal/answer-reusable? (:value %) evaluation-time-ms false)))) :value)) (defn- record-publication! @@ -1234,7 +1290,12 @@ store (subproblem/publish! subproblem-store :answer key - {:valid? answer-entry-valid?} + (cond-> {:valid? answer-entry-valid?} + (some? (temporal/answer-interval (:value entry))) + (assoc :replace? (fn [prior next] + (and (= (:computed-revision prior) (:computed-revision next)) + (= (:computed-exact-locator prior) (:computed-exact-locator next)) + (temporal/supersedes? (:value prior) (:value next)))))) entry))) (defn- cache-hit-result @@ -1251,7 +1312,7 @@ The caller supplies the captured lifecycle so an expiry racing this request cannot mix an old answer store with a new rendered store. Cache failures are ordinary misses and never affect authorization." - [store {:keys [exact-basis-key cache-lifecycle]} semantic-key] + [store {:keys [exact-basis-key cache-lifecycle evaluation-time-ms]} semantic-key] (when (and (basis-cache? store) (valid-exact-basis-key? exact-basis-key) (map? semantic-key) @@ -1265,7 +1326,9 @@ (catch #?(:clj Throwable :cljs :default) _ (record-metrics! store update :rendered-page-store-errors inc) nil))] - (if (:found? resident) + (if (and (:found? resident) + (or (nil? (:qualification semantic-key)) + (temporal/answer-reusable? (:value resident) evaluation-time-ms true))) (do ;; JVM hits use a striped LongAdder instead of serializing on the ;; ordinary metrics atom. CLJS remains single-threaded. @@ -1315,8 +1378,10 @@ (let [lifecycle (or cache-lifecycle @(:lifecycle store)) storage-key (exact-rendered-page-key exact-basis-key semantic-key)] (try - (if (lru/put-if-absent! - (:rendered-pages lifecycle) storage-key value) + (if (or (lru/put-if-absent! (:rendered-pages lifecycle) storage-key value) + (let [prior (lru/lookup! (:rendered-pages lifecycle) storage-key)] + (and (:found? prior) (temporal/supersedes? (:value prior) value) + (lru/replace-if! (:rendered-pages lifecycle) storage-key (:value prior) value)))) (do (record-metrics! store update :rendered-page-puts inc) {:published? true :reason :published}) @@ -1355,7 +1420,7 @@ "Resolves an ordinary request exact-first, then by one complete managed key." [store {:keys [exact-basis-key cache-lifecycle managed-key-fn populate-cache? - populate-exact?] + populate-exact? evaluation-time-ms] :or {populate-cache? true populate-exact? true}} semantic-key compute] @@ -1370,7 +1435,11 @@ lifecycle (or cache-lifecycle @(:lifecycle store)) subproblem-store (:subproblems lifecycle) exact-key (exact-answer-key exact-basis-key semantic-key) - exact-entry (lookup-answer subproblem-store exact-key)] + temporal? (or (= temporal/point-format (:temporal-answer-format semantic-key)) + (= temporal/collection-format (:qualification-certificate-format semantic-key))) + exact-entry (lookup-answer subproblem-store exact-key + (when temporal? + #(temporal/answer-reusable? (:value %) evaluation-time-ms true)))] (if exact-entry (cache-hit-result store :exact-basis exact-entry) (if-not (execution/cache-stage-available?) @@ -1384,7 +1453,7 @@ managed-entry (when managed-key (lookup-managed-answer - subproblem-store managed-key revision))] + subproblem-store managed-key revision temporal? evaluation-time-ms))] (if managed-entry (do (when (and populate-cache? populate-exact?) @@ -1425,7 +1494,7 @@ (defn resolve-managed-read-only! "Consults only a causally valid managed key; a miss computes without writes." [store - {:keys [cache-lifecycle snapshot-order managed-source managed-key-fn]} + {:keys [cache-lifecycle snapshot-order managed-source managed-key-fn evaluation-time-ms]} semantic-key compute] (if-not (and (basis-cache? store) (proof-frame/generation? snapshot-order) @@ -1442,7 +1511,8 @@ entry (when managed-key (lookup-managed-answer - subproblem-store managed-key snapshot-order))] + subproblem-store managed-key snapshot-order + (= temporal/point-format (:temporal-answer-format semantic-key)) evaluation-time-ms))] (if entry (cache-hit-result store :managed-current entry) (uncached-result store compute))))))) diff --git a/modules/eacl/src/eacl/caveats/definition.cljc b/modules/eacl/src/eacl/caveats/definition.cljc index 606632b9..e7d71aed 100644 --- a/modules/eacl/src/eacl/caveats/definition.cljc +++ b/modules/eacl/src/eacl/caveats/definition.cljc @@ -15,17 +15,42 @@ :eacl.caveat/expression-source (:source compiled) :eacl.caveat/profile-version values/profile-id})) -(defn decode-entity [entity] - (when-not (and (map? entity) (= (set attributes) (set (keys (dissoc entity :db/id))))) +(defn content-identity + "Bounded, complete native content for a validated-definition cache. A miss + must decode the header/plan before publication. No database id or digest + substitutes for these exact bytes; changed or malformed content cannot hit." + [entity] + (when-not (and (map? entity) + (= (count entity) (+ (count attributes) (if (contains? entity :db/id) 1 0))) + (every? #(contains? entity %) attributes)) (values/error! :definition-shape)) (when-not (= values/profile-id (:eacl.caveat/profile-version entity)) (values/error! :unsupported-profile)) (let [name (:eacl.caveat/name entity) - parameters (values/decode-parameters (:eacl.caveat/parameters-payload entity)) - compiled (plan/compile-plan (:eacl.caveat/expression-source entity) parameters)] + payload (:eacl.caveat/parameters-payload entity) + source (:eacl.caveat/expression-source entity)] + (when-not (and (string? payload) (<= (count payload) (:context-utf8-bytes values/limits))) + (values/error! :resource-limit {:limit :payload-bytes})) (when-not (and (values/parameter-name? name) - (= (:source compiled) (:eacl.caveat/expression-source entity))) + (string? source) (seq source) + (<= (count source) (:source-utf8-bytes values/limits)) + (<= (values/utf8-size source) (:source-utf8-bytes values/limits))) (values/error! :definition-shape)) + (dissoc entity :db/id))) + +(defn decode-header + "Validates the named definition's structural envelope and parameter types + without compiling a program. Expired relationships need no program work." + [entity] + (let [content (content-identity entity)] + {:name (:eacl.caveat/name content) + :parameters (values/decode-parameters (:eacl.caveat/parameters-payload content)) + :source (:eacl.caveat/expression-source content)})) + +(defn decode-entity [entity] + (let [{:keys [name parameters source]} (decode-header entity) + compiled (plan/compile-plan source parameters)] + (when-not (= (:source compiled) source) (values/error! :definition-shape)) (assoc compiled :name name))) (defn entity-deletions [{:keys [additions retractions]}] diff --git a/modules/eacl/src/eacl/caveats/values.cljc b/modules/eacl/src/eacl/caveats/values.cljc index 421c7010..435ae3ff 100644 --- a/modules/eacl/src/eacl/caveats/values.cljc +++ b/modules/eacl/src/eacl/caveats/values.cljc @@ -28,7 +28,7 @@ (merge data {:type :eacl.caveat/invalid :eacl/error :eacl.caveat/invalid :reason reason}))))) -(defn utf8-size [s] (count (secure/utf8-bytes s))) +(defn utf8-size [s] (secure/utf8-size s)) (defn parameter-name? [s] (and (string? s) (<= (count s) (:identifier-ascii-bytes limits)) @@ -201,7 +201,11 @@ context))) (defn normalize-context [parameters context] - (decode-context parameters (encode-context parameters context))) + ;; Host values already have their portable representation. The encoder + ;; validates every type and aggregate wire bound; reading that freshly + ;; produced payload adds no admission check and needlessly rebuilds it. + (encode-context parameters context) + context) (defn normalize-value [type value] (get (normalize-context [["value" type]] {"value" value}) "value")) @@ -219,5 +223,12 @@ value)) (defn merge-context [parameters request bound] - (normalize-context parameters (merge (normalize-context parameters request) - (normalize-context parameters bound)))) + (let [request (normalize-context parameters request) + bound (normalize-context parameters bound) + merged (merge request bound)] + ;; Both supplied inputs must be admitted, even when bound values win. + ;; An unchanged admitted map already satisfies the aggregate bounds. + (cond + (= merged request) request + (= merged bound) bound + :else (normalize-context parameters merged)))) diff --git a/modules/eacl/src/eacl/client/orchestration.cljc b/modules/eacl/src/eacl/client/orchestration.cljc index 30091f39..8b84ad55 100644 --- a/modules/eacl/src/eacl/client/orchestration.cljc +++ b/modules/eacl/src/eacl/client/orchestration.cljc @@ -19,6 +19,7 @@ :prepared-native-source-id-key optional private config key populated by a backend bootstrap wrapper :relationship-retraction-count (fn [db tx-data] n) + :qualified-plan (fn [db entries app-datoms source-scope] plan) :schema {:read-schema (fn [db] ...) :write-schema! (fn [conn schema-string opts] ...)} :transact! (fn [conn native-tx] tx-report) @@ -33,6 +34,13 @@ (:require [clojure.set :as set] [com.rpl.specter :as S] [eacl.authorization.batch :as batch] + [eacl.authorization.clock :as authorization-clock] + [eacl.authorization.context :as caveat-context] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualifier-cache :as qualifier-cache] + [eacl.authorization.temporal :as temporal] + [eacl.authorization.result :as authorization-result] [eacl.authorization.filters :as authorization-filters] [eacl.backend.source :as source] [eacl.backend.v8 :as backend] @@ -41,11 +49,17 @@ [eacl.cache.derived-schema :as derived-schema] [eacl.cache-identity :as cache-identity] [eacl.causal-token :as causal-token] + [eacl.caveats.evaluator :as caveat-evaluator] + [eacl.schema.qualification-admission :as qualification-admission] + [eacl.relationships.inspection :as inspection] + [eacl.schema.expression-resolver :as expression-resolver] [eacl.consistency :as consistency-v3] [eacl.continuation :as continuation] [eacl.cursor :as cursor] [eacl.core :as eacl :refer [IAuthorizationReader IAuthorizationWriter + IRelationshipPreparation + IRelationshipPlanning IBatchedAuthorization ISnapshotSource IAuthorizationSnapshot @@ -69,6 +83,7 @@ [eacl.relay :as relay] [eacl.relationships.filters :as relationship-filters] [eacl.relationships.mutations :as relationship-mutations] + [eacl.relationships.staged :as qualified-writes] [eacl.relationships.storage :as relationship-storage] [eacl.request.context :as request-context] [eacl.request.counters :as request-counters] @@ -84,8 +99,22 @@ speculative-with-snapshot speculative-with-schema-snapshot snapshot-tx-relationship + snapshot-tx-relationships attach-runtime-cache-lifecycle) +(def ^:dynamic *qualified-authorization-enabled?* + "V9 qualified serving. The legacy binding is retained for compatibility + qualification; public clients use the activated semantic epoch." + true) + +(defn- qualification-options [opts] + (when *qualified-authorization-enabled?* + {:time (:evaluation-time-ms opts) + :prepared-context (::caveat-context opts) + :evaluator (:caveat-evaluator opts) + :cache (when (not (false? (:completed-cache-request? opts))) (:qualifier-decode-cache opts)) + :populate-cache? (:populate-cache-request? opts true)})) + (def ^:dynamic *operator-expression-writes-enabled?* "Public schema-write gate for intersection or exclusion expressions. Union-only schemas never consult this value. Dynamic binding remains @@ -128,10 +157,19 @@ (defn- ensure-execution-contract [opts operation request] - (let [opts + (when (#{:lookup-resources :lookup-subjects :count-resources :count-subjects} operation) + (authorization-result/result-policy request)) + (let [context (get request :caveat-context {}) + prepared (::caveat-context opts) + opts (if (and prepared (identical? context (caveat-context/value prepared))) + opts + (assoc opts ::caveat-context (caveat-context/prepare context))) + opts ;; The public operation name for observation only: `:request-operation` ;; is reserved for batch endpoints and changes scalar decision keys. (cond-> (assoc opts ::observed-operation operation) + (not (contains? opts :evaluation-time-ms)) + (assoc :evaluation-time-ms ((or (::evaluation-clock opts) authorization-clock/system-clock))) (nil? (:cache-lifecycle opts)) (assoc :cache-lifecycle (cache/capture-cache-lifecycle @@ -323,6 +361,8 @@ :selected-snapshot selected-snapshot :basis-identity semantic-identity :contract (:execution-contract runtime) + :caveat-context (::caveat-context runtime) + :qualification-options (qualification-options runtime) :derived-registry (:derived-schema-caches runtime) :counter-ledger (:request-counter-ledger runtime) :proof-diagnostic-fn @@ -401,14 +441,16 @@ scopes (volatile! {})] (fn [relation-eid] (or (get @scopes relation-eid) - (when-let [resolved - (proof-frame/resolved-generation - (force (:proof-frame-delay state)) - relation-eid)] - (let [scope (conj @base - (:schema-generation resolved) - relation-eid - (:generation resolved))] + (let [resolved (when-not (:qualification state) + (proof-frame/resolved-generation + (force (:proof-frame-delay state)) relation-eid))] + ;; Compact rows contain data, never a qualification result. + ;; Exact basis scope remains safe when no managed qualifier + ;; proof is available, including raw qualifier mutations. + (let [scope (if resolved + (conj @base (:schema-generation resolved) + relation-eid (:generation resolved)) + (conj @base :exact identity relation-eid))] (vswap! scopes assoc relation-eid scope) scope))))))] {:memo (scan-cache/memo) @@ -454,6 +496,8 @@ :selected-snapshot nil :basis-identity identity :contract (:execution-contract opts) + :caveat-context (::caveat-context opts) + :qualification-options (qualification-options opts) :derived-registry (:derived-schema-caches opts) :counter-ledger ledger :proof-diagnostic-fn @@ -495,6 +539,7 @@ historical-basis? (::historical-basis? runtime)] (assoc runtime ::request-context-state context-state + :qualification (:qualification context-state) ;; Nested operations (notably one scalar decision inside a batch) ;; refine the selected request's contract without selecting a new ;; basis. Preserve that operation-local semantic demand instead of @@ -834,6 +879,7 @@ resource-type permission relationship-dependency) :snapshot-semantic-identity page-semantic-identity :cursor-dependency-context continuation-context + :cursor-qualification-certificate (:qualification-certificate prepared) :accepted-cursor-frame accepted-cursor-frame :transport-page-input-expiring? (:expiring-cursor-input? prepared) @@ -860,7 +906,18 @@ request-context opts operation query resource-type permission relationship-dependency)] (try - (f page) + (binding [engine/*qualification* + (if (and engine/*qualification* + (not (identical? (:adapter page) (request-context/adapter request-context)))) + (qualification/request-from-adapter + (:adapter page) + (assoc (qualification-options (:opts page)) + :basis (:snapshot-semantic-identity page))) + engine/*qualification*)] + (when (and engine/*qualification* (get-in page [:opts :cursor-qualification-certificate])) + (qualification/observe-interval! engine/*qualification* + (select-keys (get-in page [:opts :cursor-qualification-certificate]) [:start-ms :valid-until-ms :complete?]))) + (f (assoc-in page [:opts :qualification] engine/*qualification*))) (finally (when-let [selected (:selected-snapshot page)] (source/release! selected)))))) @@ -868,7 +925,7 @@ (defn- completed-answer-semantic-key [opts operation query] (let [contract (:execution-contract opts)] - {:operation operation + (cond-> {:operation operation :query query :evaluation (:evaluation contract) :demand (:demand contract) @@ -883,7 +940,9 @@ :identity-contract (:identity-contract opts) :recursive-traversal-limits (:recursive-traversal-limits opts) :expression-limits (:expression-limits opts) - :permission-tree-limits (:permission-tree-limits opts)})) + :permission-tree-limits (:permission-tree-limits opts)} + engine/*qualification* + (assoc :qualification (qualification/exact-reuse-identity engine/*qualification*))))) (defn- continuation-compute-fn "The engine call for a range-composition remainder or a segment-end @@ -906,6 +965,9 @@ (let [contract (:execution-contract opts) managed-reuse? (and (:managed-cache-enabled? opts) + ;; Qualified managed proofs are a separate release obligation. + ;; The current exact scope fixes source, basis, context, and time. + (nil? engine/*qualification*) (not (:historical-basis? opts))) context-state (or (::request-context-state opts) (request-context/active-state request-context)) @@ -956,30 +1018,40 @@ @request-proof-frame-delay relation-ids))))) evaluate-with (fn [thunk] - (execution/check! contract :schema-plan) - (let [value - (binding [engine/*schema-cache* @schema-cache - expression-persistence/*structural-cache* - (:expression-metrics @schema-cache) - expression-persistence/*expression-limits* - (:expression-limits opts) - engine/*proof-frame* @request-proof-frame-delay - engine/*request-lineage* - (:request-lineage opts) - engine/*request-frame* - request-frame-descriptor - engine/*recursive-traversal-limits* - (:recursive-traversal-limits opts) - engine/*service-admission* - (:service-admission opts) - engine/*evaluation-mode* - (:evaluation contract) - execution/*contract* contract - subproblem/*decision-kernel* - (:decision-kernel opts)] - (thunk))] - (execution/check! contract :semantic-evaluation) - value)) + (execution/check! contract :schema-plan) + (let [value + (binding [engine/*schema-cache* @schema-cache + expression-persistence/*structural-cache* + (:expression-metrics @schema-cache) + expression-persistence/*expression-limits* + (:expression-limits opts) + engine/*proof-frame* @request-proof-frame-delay + engine/*request-lineage* + (:request-lineage opts) + engine/*request-frame* + request-frame-descriptor + engine/*recursive-traversal-limits* + (:recursive-traversal-limits opts) + engine/*service-admission* + (:service-admission opts) + engine/*evaluation-mode* + (:evaluation contract) + execution/*contract* contract + subproblem/*decision-kernel* + (:decision-kernel opts)] + (thunk))] + (execution/check! contract :semantic-evaluation) + (cond + (nil? engine/*qualification*) value + (= :can? operation) (temporal/point-answer (:time engine/*qualification*) value) + (contains? #{:lookup-resources :lookup-subjects :count-resources :count-subjects :read-relationships} operation) + (assoc value :qualification-certificate + (if (and (= :read-relationships operation) + (nil? (get-in query [:public :authorization])) + (not= :expiry-active (get-in query [:public :relationship-state]))) + (temporal/interval (:time engine/*qualification*) nil true) + (qualification/certificate engine/*qualification*))) + :else value))) evaluate #(evaluate-with compute) cacheable? (and (:basis-cache-store opts) @@ -1085,7 +1157,19 @@ (proof-frame/resolve! frame (:relation-ids @dependencies)))) semantic-key - (completed-answer-semantic-key opts operation query) + (cond-> (completed-answer-semantic-key opts operation query) + (and engine/*qualification* (contains? #{:lookup-resources :lookup-subjects :count-resources :count-subjects :read-relationships} operation)) + (-> (assoc :qualification-certificate-format temporal/collection-format) + (update :qualification (fn [[format basis _ context evaluator]] + [format basis context evaluator]))) + (= :expand-permission-tree operation) + (dissoc :qualification) + (and engine/*qualification* (= :can? operation)) + ;; Keep exact source/basis, context and evaluator while removing + ;; time from this answer key. Its value now certifies time reuse. + (-> (assoc :temporal-answer-format temporal/point-format) + (update :qualification (fn [[format basis _ context evaluator]] + [format basis context evaluator])))) ;; Range reuse: on an exact and managed miss, any window of the ;; same walk is served from the retained segments; a window that ;; runs past a segment composes its tail with one continuation; @@ -1123,6 +1207,12 @@ evaluate (fn [] (let [hit (range-reuse/lookup! range-tier walk-key window) + hit (if engine/*qualification* + (when (and (temporal/interval-valid? (:qualification-certificate hit)) + (temporal/reusable? (:qualification-certificate hit) (:time engine/*qualification*) true)) + (qualification/observe-interval! engine/*qualification* (:qualification-certificate hit)) + hit) + hit) compute-and-publish (fn [] (let [page (evaluate)] @@ -1181,6 +1271,7 @@ (:basis-cache-store opts) {:cache-lifecycle (:cache-lifecycle opts) :exact-basis-key exact-basis-key + :evaluation-time-ms (:time engine/*qualification*) :populate-cache? (:populate-cache-request? opts true) :populate-exact? @@ -1191,6 +1282,8 @@ #(proof-frame/descriptor @complete-proof))} semantic-key evaluate))] (execution/check! contract :cache-publication) + (when (and engine/*qualification* (get-in answer [:value :qualification-certificate])) + (qualification/observe-interval! engine/*qualification* (get-in answer [:value :qualification-certificate]))) (cond @derived? (assoc answer :cached? true :cache-tier :range) @composed? (assoc answer :cache-tier :range-composition) @@ -1199,7 +1292,7 @@ (defn- with-cache-info [value {:keys [cached? cache-basis]}] (if (map? value) - (assoc value :cached? cached? :cache-basis cache-basis) + (assoc (dissoc value :qualification-certificate) :cached? cached? :cache-basis cache-basis) value)) (def ^:private rendered-page-render-abi @@ -1209,14 +1302,20 @@ (defn- rendered-page-semantic-key [opts operation normalized-public-query consistency-key] - (completed-answer-semantic-key - opts operation - {:public normalized-public-query - :consistency consistency-key - :cursor-policy - (or (:cursor-cache-policy-identity opts) - (cursor/cache-policy-identity opts)) - :render-abi rendered-page-render-abi})) + (cond-> (completed-answer-semantic-key + opts operation + {:public normalized-public-query + :consistency consistency-key + :cursor-policy + (or (:cursor-cache-policy-identity opts) + (cursor/cache-policy-identity opts)) + :render-abi rendered-page-render-abi}) + engine/*qualification* + (assoc :temporal-mode (relay/temporal-mode opts) + :qualification-certificate-format temporal/collection-format) + (and engine/*qualification* (= :live (relay/temporal-mode opts))) + (update :qualification (fn [[format basis _ context evaluator]] + [format basis context evaluator])))) (defn- rendered-page-cache-context [adapter opts operation public-query] @@ -1250,6 +1349,7 @@ {:store (:basis-cache-store opts) :lookup-options {:cache-lifecycle (:cache-lifecycle opts) + :evaluation-time-ms (:time engine/*qualification*) :exact-basis-key exact-basis-key} :publication-options {:cache-lifecycle (:cache-lifecycle opts) @@ -1271,6 +1371,8 @@ (defn- public-page-from-rendered [opts rendered provenance] (execution/check! (:execution-contract opts) :rendered-page-return) + (when (and engine/*qualification* (:qualification-certificate rendered)) + (qualification/observe-interval! engine/*qualification* (:qualification-certificate rendered))) (with-cache-info (:page rendered) provenance)) (defn- selected-rendered-page-hit @@ -1294,8 +1396,15 @@ (relay/externalize-page adapter opts operation query (:value answer))) rendered - {:format cache/rendered-page-entry-format - :page public-page}] + (cond-> {:format cache/rendered-page-entry-format + :page public-page} + engine/*qualification* + (assoc :qualification-certificate + (let [current (:qualification-certificate (:value answer)) + prior (:cursor-qualification-certificate opts)] + (if prior + (temporal/intersect-intervals current (select-keys prior [:start-ms :valid-until-ms :complete?])) + current))))] (when cache-context (cache/publish-rendered-page! (:store cache-context) @@ -1343,6 +1452,12 @@ :request-lineage (:request-lineage opts) :populate-cache? (:populate-cache-request? opts true)}))) +(defn- qualified-schema! [api db schema evaluator] + (qualification-admission/schema! + schema evaluator + (when-let [capability (:qualified-publication-capability api)] + (capability db)))) + (defn- request-schema "The parsed public schema visible in `db`, for validating one request. @@ -1354,27 +1469,34 @@ tier keys by an identity that fixes the schema generation). An unstamped database (no schema generation) or an unbound generation reads the schema directly." - [api db] - (let [cache engine/*schema-cache* - slot (:parsed-schema cache) - catalog-slot (:validation-catalog cache) - read-schema (get-in api [:schema :read-schema])] - (if (and slot - (or (some? (:schema-version cache)) - (true? (:request-local? cache)))) - (let [schema - (engine/memoized-derived! slot #(read-schema db)) - names - (if catalog-slot - (engine/memoized-derived! - catalog-slot #(schema-errors/catalog schema)) - (schema-errors/catalog schema))] - (schema-errors/with-catalog schema names)) - (read-schema db)))) + ([api db] (request-schema api db true)) + ([api db authorize?] + (let [cache engine/*schema-cache* + slot (:parsed-schema cache) + catalog-slot (:validation-catalog cache) + read-schema (if engine/*qualification* + (or (get-in api [:schema :read-authorization-schema]) + (throw (ex-info "Qualified authorization requires structural schema reads." + {:type :eacl/unsupported-capability + :eacl/error :eacl/unsupported-capability + :capability :qualified-authorization-schema}))) + (get-in api [:schema :read-schema])) + schema + (if (and slot (or (some? (:schema-version cache)) (true? (:request-local? cache)))) + (let [schema (engine/memoized-derived! slot #(read-schema db)) + names (if catalog-slot + (engine/memoized-derived! catalog-slot #(schema-errors/catalog schema)) + (schema-errors/catalog schema))] + (schema-errors/with-catalog schema names)) + (read-schema db))] + ;; Parsed structure is shared; evaluator availability belongs to this client. + (when (and authorize? engine/*qualification*) + (qualified-schema! api db schema (:evaluator engine/*qualification*))) + schema))) (defn- authorization-scan-page [api opts request-context adapter selected-db cursor-opts filters - internal-query validate!] + internal-query validate!] (let [{:keys [subject permission on]} (:authorization filters) internal-subject ((:spice-object->internal opts) selected-db subject) schema-cache (:request-schema-cache cursor-opts) @@ -1436,20 +1558,20 @@ ;; Root/schema validation is selected-snapshot work but precedes the first ;; physical relationship candidate. (binding [engine/*schema-cache* @schema-cache - expression-persistence/*expression-limits* - (:expression-limits opts) - engine/*proof-frame* request-proof-frame] + expression-persistence/*expression-limits* + (:expression-limits opts) + engine/*proof-frame* request-proof-frame] (validate!)) (let [internal-page (binding [engine/*aggregate-work-stats* work-stats relationship-filters/*validated-request?* true] ((get-in api [:impl :read-relationships]) selected-db internal-query (:decision-kernel cursor-opts) - {:candidate-window candidate-window - :accept? accept?}))] + (inspection/window-options engine/*qualification* filters + {:candidate-window candidate-window :accept? accept?})))] (batch/check-aggregate-limits! limits (counters (count (:data internal-page))) nil) - internal-page))) + (inspection/decode-page engine/*qualification* internal-page)))) (defn read-relationships [api source {:as opts :keys [object-id->entid]} filters] @@ -1459,6 +1581,10 @@ (relationship-filters/validate! filters)) (when-not authorization-filters/*validated-request?* (authorization-filters/validate-scan-authorization! filters)) + (when (and (not *qualified-authorization-enabled?*) (contains? filters :relationship-state)) + (throw (ex-info "Qualified Relationship inspection is not enabled." + {:type :eacl/unsupported-capability :eacl/error :eacl/unsupported-capability + :capability :qualified-relationship-inspection}))) (let [opts (ensure-execution-contract opts :read-relationships filters) authorization (:authorization filters) authorization-resource-type @@ -1470,107 +1596,116 @@ (with-selected-context api source opts (:consistency filters) (fn [request-context] - (if-let [rendered-hit - (selected-rendered-page-hit - request-context opts :read-relationships filters)] - rendered-hit - (with-page-context - request-context opts :read-relationships filters - authorization-resource-type authorization-permission - (when authorization - {:resource-type (:resource/type filters) - :relation (:resource/relation filters) - :subject-type (:subject/type filters)}) - (fn [{request-context :request-context - adapter :adapter page-db :db cursor-opts :opts - page-query :query - deferred-boundary? - :deferred-cursor-edge-internalization?}] - (let [rendered-cache - (rendered-page-cache-context - adapter cursor-opts :read-relationships filters) - rendered-hit - (lookup-rendered-page cursor-opts rendered-cache)] - (if rendered-hit - (public-page-from-rendered - cursor-opts (:value rendered-hit) rendered-hit) - (let [page-query - (if deferred-boundary? - (relay/internalize-prepared-page-query - adapter page-query) - page-query) + (inspection/page-info engine/*qualification* filters + (if-let [rendered-hit + (selected-rendered-page-hit + request-context opts :read-relationships filters)] + rendered-hit + (with-page-context + request-context opts :read-relationships filters + authorization-resource-type authorization-permission + (when authorization + {:resource-type (:resource/type filters) + :relation (:resource/relation filters) + :subject-type (:subject/type filters)}) + (fn [{request-context :request-context + adapter :adapter page-db :db cursor-opts :opts + page-query :query + deferred-boundary? + :deferred-cursor-edge-internalization?}] + (let [rendered-cache + (rendered-page-cache-context + adapter cursor-opts :read-relationships filters) + rendered-hit + (lookup-rendered-page cursor-opts rendered-cache)] + (if rendered-hit + (public-page-from-rendered + cursor-opts (:value rendered-hit) rendered-hit) + (let [page-query + (if deferred-boundary? + (relay/internalize-prepared-page-query + adapter page-query) + page-query) ;; Schema validation runs on the miss path (inside the ;; bound generation) and on unknown-object short-cuts. - validate! - (fn [] - (schema-errors/validate-authorized-relationship-read! - (request-schema api page-db) - filters)) - subject-id (:subject/id filters) - resource-id (:resource/id filters) - subject-eid - (when subject-id - (object-id->entid page-db subject-id)) - resource-eid - (when resource-id - (object-id->entid page-db resource-id)) - internal-query - (-> page-query - (dissoc :consistency :cache? :populate-cache? - :evaluation :timeout-ms - :cancellation-token :aggregate-limits - :authorization) - (cond-> - subject-id (assoc :subject/id subject-eid) - resource-id (assoc :resource/id resource-eid)))] - (if (or (and subject-id (nil? subject-eid)) - (and resource-id (nil? resource-eid))) - (do - (call-with-request-schema-cache cursor-opts validate!) - (if (cursor-request? filters) - (stale-cursor-anchor! :read-relationships) - (cond-> - (assoc relay/empty-page - :cached? false :cache-basis nil) - (:authorization filters) - (assoc-in [:page-info :bounded?] false)))) - (let [answer-opts - (cond-> cursor-opts - rendered-cache - (assoc ::populate-exact-answer? false)) - answer - (if authorization - (cached-engine-result - request-context adapter answer-opts - :read-relationships - (cache/lookup-page-query-identity - filters internal-query) - authorization-resource-type - authorization-permission - #(authorization-scan-page - api opts request-context adapter page-db - cursor-opts filters internal-query validate!)) - (cached-engine-result - request-context adapter answer-opts - :read-relationships - (cache/lookup-page-query-identity - filters internal-query) - nil nil - #(do - (validate!) - (binding - [relationship-filters/*validated-request?* - true] - ((get-in api [:impl :read-relationships]) - page-db internal-query - (:decision-kernel cursor-opts))))))] - (render-and-cache-page - adapter cursor-opts :read-relationships filters - rendered-cache answer))))))))))))) + validate! + (fn [] + (schema-errors/validate-authorized-relationship-read! + (request-schema api page-db (some? authorization)) + filters)) + subject-id (:subject/id filters) + resource-id (:resource/id filters) + subject-eid + (when subject-id + (object-id->entid page-db subject-id)) + resource-eid + (when resource-id + (object-id->entid page-db resource-id)) + internal-query + (-> page-query + (dissoc :consistency :cache? :populate-cache? + :evaluation :timeout-ms + :cancellation-token :aggregate-limits + :authorization :relationship-state) + (cond-> + subject-id (assoc :subject/id subject-eid) + resource-id (assoc :resource/id resource-eid)))] + (if (or (and subject-id (nil? subject-eid)) + (and resource-id (nil? resource-eid))) + (do + (call-with-request-schema-cache cursor-opts validate!) + (if (cursor-request? filters) + (stale-cursor-anchor! :read-relationships) + (cond-> + (assoc relay/empty-page + :cached? false :cache-basis nil) + (:authorization filters) + (assoc-in [:page-info :bounded?] false)))) + (let [answer-opts + (cond-> cursor-opts + rendered-cache + (assoc ::populate-exact-answer? false)) + answer + (if authorization + (cached-engine-result + request-context adapter answer-opts + :read-relationships + (cache/lookup-page-query-identity + filters internal-query) + authorization-resource-type + authorization-permission + #(authorization-scan-page + api opts request-context adapter page-db + cursor-opts filters internal-query validate!)) + (cached-engine-result + request-context adapter answer-opts + :read-relationships + (cache/lookup-page-query-identity + filters internal-query) + nil nil + #(do + (validate!) + (binding + [relationship-filters/*validated-request?* + true] + (if engine/*qualification* + (inspection/decode-page + engine/*qualification* + ((get-in api [:impl :read-relationships]) + page-db internal-query (:decision-kernel cursor-opts) + (inspection/window-options + engine/*qualification* filters + (when (= :expiry-active (:relationship-state filters)) + {:candidate-window (get-in opts [:execution-contract :aggregate-limits :candidate-window])})))) + ((get-in api [:impl :read-relationships]) + page-db internal-query (:decision-kernel cursor-opts)))))))] + (render-and-cache-page + adapter cursor-opts :read-relationships filters + rendered-cache answer)))))))))))))) (defn spice-relationship->internal [db {:keys [spice-object->internal object-id->lookup-ref]} - {:keys [subject relation resource]}] + {:keys [subject relation resource] :as relationship}] (let [internalize (fn [object] (assoc (spice-object->internal db object) @@ -1583,9 +1718,10 @@ ;; transaction data or a cache key. :eacl.relationship/public-object (select-keys object [:type :id])))] - {:subject (internalize subject) - :relation relation - :resource (internalize resource)})) + (merge (select-keys relationship relationship-mutations/qualifier-keys) + {:subject (internalize subject) + :relation relation + :resource (internalize resource)}))) (defn- response-token-for-revision [_api native-revision opts] @@ -1688,11 +1824,13 @@ (public-answer-key-eligible? adapter)) response (fn [answer] - {:allowed? (:value answer) - :cached? (:cached? answer) - :cache-basis (:cache-basis answer) - :evaluation - (get-in opts [:execution-contract :evaluation])})] + (cond-> {:allowed? (:value answer) + :cached? (:cached? answer) + :cache-basis (:cache-basis answer) + :evaluation (get-in opts [:execution-contract :evaluation])} + engine/*qualification* + (merge (authorization-result/check-result (evidence/decode (get-in answer [:value :value])))))) + check-point (if engine/*qualification* engine/check-evidence engine/can?)] (if public-key? ;; Exact lookup precedes the compute closure, so a warm point decision ;; performs no Datomic/Dynamo identity lookup. Managed reuse is safe only @@ -1709,7 +1847,7 @@ (spice-object->internal selected-db resource)] (validate!) (if (and (:id internal-subject) (:id internal-resource)) - (engine/can? + (check-point adapter internal-subject permission internal-resource) false)))) (let [internal-subject @@ -1719,11 +1857,11 @@ (if-not (and (:id internal-subject) (:id internal-resource)) (do (call-with-request-schema-cache opts validate!) - {:allowed? false - :cached? false - :cache-basis nil - :evaluation - (get-in opts [:execution-contract :evaluation])}) + (cond-> {:allowed? false + :cached? false + :cache-basis nil + :evaluation (get-in opts [:execution-contract :evaluation])} + engine/*qualification* (merge (authorization-result/check-result false)))) (response (cached-engine-result request-context adapter opts :can? @@ -1734,7 +1872,7 @@ permission #(do (validate!) - (engine/can? + (check-point adapter internal-subject permission internal-resource))))))))) (defn check-permission @@ -1768,6 +1906,7 @@ (do ;; Validate every request-wide control without capturing cache state or ;; acquiring a snapshot. + (caveat-context/prepare (get request :caveat-context {})) (execution/normalize opts :check-permissions request) []) (let [opts @@ -1910,7 +2049,8 @@ engine/*proof-frame* request-proof-frame] (required-direct-relation-id adapter relation-resource-type relation relation-subject-type)) - direct-match! (backend/direct-match-invoker adapter) + direct-match! ((if engine/*qualification* + backend/direct-edge-invoker backend/direct-match-invoker) adapter) accept? (fn [candidate] (execution/check! @@ -1934,15 +2074,17 @@ (execution/check! contract :authorization-probe-complete consumed) (batch/check-aggregate-limits! limits consumed nil)) - matches?)) + (if engine/*qualification* + (if matches? (qualification/qualify engine/*qualification* relation-id matches?) false) + matches?))) engine-options {:continuation-cache-fn (fn [] (continuation-context adapter cursor-opts operation query)) :candidate-filter - {:candidate-window candidate-window - :accept? accept?}} + (assoc {:candidate-window candidate-window} + (if engine/*qualification* :accept-evidence :accept?) accept?)} internal-page (binding [engine/*schema-cache* @schema-cache expression-persistence/*expression-limits* @@ -2098,9 +2240,9 @@ public-key? (and public-subject (public-answer-key-eligible? adapter)) empty-answer - #(cond-> {:count 0 :limit (or (:count-limit query) -1)} - (contains? query :count-limit) - (assoc :truncated? false)) + #(authorization-result/count-result + {:count 0 :truncated? false} (:count-limit query) + (authorization-result/result-policy query)) compute (fn [internal-subject] (validate!) @@ -2285,9 +2427,9 @@ public-key? (and public-resource (public-answer-key-eligible? adapter)) empty-answer - #(cond-> {:count 0 :limit (or (:count-limit query) -1)} - (contains? query :count-limit) - (assoc :truncated? false)) + #(authorization-result/count-result + {:count 0 :truncated? false} (:count-limit query) + (authorization-result/result-policy query)) compute (fn [internal-resource] (validate!) @@ -2409,16 +2551,16 @@ [token source-incarnation source-lifecycle basis-cache-store continuation-cache-store cursor-codec-cache cursor-construction-cache derived-schema-caches scan-tier - range-tier content-revision]) + range-tier qualifier-decode-cache content-revision]) (defrecord Basis [adapter selected-snapshot identity selection basis-kind historical-basis? execution-constraints release-state owner-thread acquired-at-ms maximum-retention-ms - source-incarnation speculative]) + source-incarnation speculative evaluation-time-ms]) (def ^:private runtime-lifecycle-option-keys #{:source-lifecycle :basis-cache-store :continuation-cache-store :cursor-codec-cache :cursor-construction-cache :derived-schema-caches - :scan-tier :range-tier}) + :scan-tier :range-tier :qualifier-decode-cache}) ;; A transient Acl read has already captured and attached one immutable ;; lifecycle options map before selecting its basis. Keep that map opaque @@ -2479,6 +2621,10 @@ (when (and basis-cache-store (not (false? range-reuse-option))) (range-reuse/tier (if (map? range-reuse-option) range-reuse-option {})))) +(defn- qualifier-tier-for + [{:keys [qualifier-cache-option]} basis-cache-store] + (when basis-cache-store (qualifier-cache/cache qualifier-cache-option))) + (defn- fresh-runtime-cache-lifecycle [{:keys [cache-option derived-schema-store-factory] :as config} @@ -2504,6 +2650,7 @@ (derived-schema-store-factory) (scan-tier-for-config config basis-cache-store) (range-tier-for config basis-cache-store) + (qualifier-tier-for config basis-cache-store) content-revision))) (defn- narrow-runtime-cache-lifecycle @@ -2535,6 +2682,7 @@ (:derived-schema-caches current) (scan-tier-for-config config basis-cache-store) (range-tier-for config basis-cache-store) + (qualifier-tier-for config basis-cache-store) content-revision))) (defn- lifecycle-content-revision @@ -2594,6 +2742,7 @@ (def ^:private runtime-option-keys #{:adapter-fingerprint :adapter-deterministic? :aggregate-limits + ::evaluation-clock :caveat-evaluator :qualifier-decode-cache :continuation-cache-store :basis-cache-store :cursor-codec-cache :cursor-construction-cache :decision-kernel :derived-schema-caches :expression-limits @@ -2613,6 +2762,9 @@ (defn- reader-api [api] {:backend-id (:backend-id api) + :entid (:entid api) + :qualified-plan (:qualified-plan api) + :qualified-publication-capability (:qualified-publication-capability api) :basis-adapter (:basis-adapter api) :basis-adapter-config-keys (:basis-adapter-config-keys api) :native-with (:native-with api) @@ -2622,10 +2774,13 @@ :relation-version-attribute (:relation-version-attribute api) :prepare-relationship-tx (:prepare-relationship-tx api) :schema {:read-schema (get-in api [:schema :read-schema]) + :read-authorization-schema (get-in api [:schema :read-authorization-schema]) :generation (get-in api [:schema :generation]) :plan-replacement (get-in api [:schema :plan-replacement])} :impl {:validate-relationship-operation! (get-in api [:impl :validate-relationship-operation!]) + :relationship-publication-input + (get-in api [:impl :relationship-publication-input]) :relationship-relation-id (get-in api [:impl :relationship-relation-id]) :relation-coordinate (get-in api [:impl :relation-coordinate]) @@ -2883,6 +3038,7 @@ opts)] (cond-> (assoc opts ::retained-basis basis + :evaluation-time-ms (:evaluation-time-ms basis) :authorization-target-kind (if (::transient-acl-selection? opts) :acl @@ -2905,6 +3061,7 @@ ;; it must not repopulate the cleared runtime under its old lineage. (assoc :basis-cache-store nil :cache-lifecycle nil + :qualifier-decode-cache nil :continuation-cache-store nil :cursor-codec-cache nil :cursor-construction-cache nil @@ -2933,7 +3090,7 @@ [{:keys [adapter selected-snapshot semantic-identity selection historical-basis? execution-constraints maximum-snapshot-retention-ms runtime-options - source-incarnation speculative]}] + source-incarnation speculative evaluation-time-ms]}] (->Basis adapter selected-snapshot semantic-identity selection (if speculative :speculative (:basis-kind semantic-identity)) historical-basis? @@ -2946,7 +3103,8 @@ (or source-incarnation (get-in runtime-options [:runtime-cache-lifecycle :source-incarnation])) - speculative)) + speculative + (or evaluation-time-ms (:evaluation-time-ms runtime-options)))) (defn- snapshot-populate-cache? [basis requested?] @@ -3069,6 +3227,10 @@ (:diagnostics speculative) (typed-capability-error! :speculative-diagnostics :ordinary-snapshot))) + IRelationshipPlanning + (-tx-relationships [this request] + (snapshot-tx-relationships this request)) + IAuthorizationSnapshot (-basis [_] (public-basis basis)) (-basis-token [_] (basis-token* runtime basis)) @@ -3084,7 +3246,8 @@ (backend-writer/make-writer {:id (:backend-id api) :state {:conn conn :source source :options options - :runtime runtime :api api} + :runtime runtime :api api + :qualified-writer (delay (when-let [factory (:qualified-writer api)] (factory conn)))} :max-attempts (or (:writer-max-attempts api) 1) :max-transaction-size (or (:writer-max-transaction-size api) @@ -3096,8 +3259,11 @@ :plan-relationship-update (get-in api [:impl :tx-update-relationship]) :plan-delete-object - (or (get-in api [:impl :tx-delete-object-stream]) - (get-in api [:impl :tx-delete-object])) + (fn [db object-eid] + ((or (when *qualified-authorization-enabled?* + (get-in api [:impl :object-relationship-retractions])) + (get-in api [:impl :tx-delete-object-stream]) + (get-in api [:impl :tx-delete-object])) db object-eid)) :prepare-relationship-tx (or (:prepare-relationship-tx api) (fn [_db tx-data] @@ -3150,8 +3316,8 @@ ((backend-writer/operation writer :transact!) conn {:tx-data (vec tx-data)}))) -(defn- writer-write-relationships! - [writer updates] +(defn- writer-write-ordinary-relationships! + [writer updates app-datoms] (let [{:keys [api]} (backend-writer/state writer) options (current-writer-options writer) validate-operation! @@ -3201,8 +3367,8 @@ distinct vec) tx-data - (when (seq raw-tx) - (vec (prepare db raw-tx)))] + (when (or (seq raw-tx) (seq app-datoms)) + (vec (prepare db (into raw-tx app-datoms))))] (if (seq tx-data) {:tx-data tx-data} {:no-op-response @@ -3234,15 +3400,114 @@ (throw error)) (:value outcome)))))) +(defn- qualified-publication-entries + [api db options updates] + (let [schema (qualified-schema! api db ((get-in api [:schema :read-authorization-schema]) db) + (:caveat-evaluator options)) + resolve-input (get-in api [:impl :relationship-publication-input]) + generation ((get-in api [:schema :generation]) db) + internal-updates + (relationship-mutations/coalesce-updates + (mapv (fn [update] + (update-in update [:relationship] #(spice-relationship->internal db options %))) updates))] + (when-not (ifn? resolve-input) + (typed-capability-error! :qualified-relationship-publication (:backend-id api))) + (mapv + (fn [{:keys [operation relationship] :as update}] + (schema-errors/validate-relationship-write! + schema :write-relationships + {:resource-type (:type (:resource relationship)) + :subject-type (:type (:subject relationship)) :relation (:relation relationship)}) + (let [input (resolve-input db relationship) + name (:caveat relationship) + caveat (when (and name (not= :delete operation)) + (or ((:entid api) db [:eacl.caveat/name name]) + (throw (ex-info "Relationship names an unknown Caveat." + {:type :eacl/unknown-caveat :eacl/error :eacl/unknown-caveat + :caveat name})))) + value (when-not (= :delete operation) + (cond-> (select-keys relationship [:caveat-context :valid-until-ms]) + caveat (assoc :caveat caveat)))] + (cond-> (assoc input :operation operation :value (not-empty value) :schema-generation generation) + (contains? update :prepared-qualifier) + (assoc :prepared-qualifier (:prepared-qualifier update))))) + internal-updates))) + +(defn- writer-write-qualified-relationships! + [writer updates app-datoms] + (let [{:keys [api qualified-writer]} (backend-writer/state writer) + native-writer (or (some-> qualified-writer deref) + (typed-capability-error! :qualified-relationship-publication (:backend-id api))) + options (current-writer-options writer) + contention? (backend-writer/operation writer :contention?)] + (loop [attempt 1] + (let [outcome + (try + (let [entries (call-with-writer-basis + writer #(qualified-publication-entries api (:db %) options updates)) + prepared (qualified-writes/prepare-batch! native-writer entries) + plan (qualified-writes/plan-batch-current native-writer prepared app-datoms) + tx-data (:tx-data plan)] + {:value (if (seq tx-data) + (let [report (submit-writer-tx! writer tx-data)] + (committed-write-response api (:db-after report) options)) + (call-with-writer-basis + writer #(write-response-for-revision api (get-in % [:selection :native-revision]) options)))}) + (catch #?(:clj Throwable :cljs :default) error {:error error}))] + (if-let [error (:error outcome)] + (if (or (contention? error) + (= :prepared-schema-changed (:reason (ex-data error)))) + (if (< attempt (backend-writer/max-attempts writer)) + (recur (inc attempt)) + (relationship-contention! writer attempt error)) + (if (= :relationship-conflict (:reason (ex-data error))) + (relationship-mutations/conflict! nil) + (throw error))) + (:value outcome)))))) + +(defn- writer-write-relationships! + [writer {:keys [updates tx-data] :or {tx-data []}}] + (let [updates (relationship-mutations/normalize-updates (vec updates)) + app-datoms (qualified-writes/application-datoms tx-data #{})] + (when (and (not *qualified-authorization-enabled?*) + (some #(seq (select-keys (:relationship %) relationship-mutations/qualifier-keys)) updates)) + (typed-capability-error! :qualified-relationship-publication (backend-writer/backend-id writer))) + (if *qualified-authorization-enabled?* + (writer-write-qualified-relationships! writer updates app-datoms) + (writer-write-ordinary-relationships! writer updates app-datoms)))) + +(defn- writer-prepare-relationship! [writer relationship] + (let [relationship (relationship-mutations/normalize-relationship relationship) + {:keys [api qualified-writer]} (backend-writer/state writer)] + (when-not *qualified-authorization-enabled?* + (typed-capability-error! :qualified-relationship-publication (:backend-id api))) + (let [native-writer (or (some-> qualified-writer deref) + (typed-capability-error! :qualified-relationship-publication (:backend-id api))) + options (current-writer-options writer) + entries (call-with-writer-basis + writer #(qualified-publication-entries api (:db %) options + [{:operation :touch :relationship relationship}]))] + (:value (first (qualified-writes/prepare-batch! native-writer entries true)))))) + +(defn- writer-discard-prepared-relationship! [writer prepared] + (when prepared + (let [{:keys [api qualified-writer]} (backend-writer/state writer) + native-writer (or (some-> qualified-writer deref) + (typed-capability-error! :qualified-relationship-publication (:backend-id api)))] + (qualified-writes/cleanup! native-writer prepared)))) + (defn- largest-fitting-prepared-batch "Returns a non-empty final transaction no larger than the writer limit. `raw-ops` may be a lazy native scan. At most limit+1 raw operations are realized, and preparation is binary-searched because schema/relation guards can make the final transaction larger than its raw slice." - [writer db raw-ops] - (let [limit (backend-writer/max-transaction-size writer) - prepare (backend-writer/operation writer :prepare-relationship-tx) + [writer db raw-ops qualified-writer] + (let [raw-ops (if qualified-writer (filter qualified-writes/relationship-retraction? raw-ops) raw-ops) + limit (backend-writer/max-transaction-size writer) + prepare (if qualified-writer + #(qualified-writes/plan-retraction-batch qualified-writer %1 %2) + (backend-writer/operation writer :prepare-relationship-tx)) sampled (if (counted? raw-ops) (let [raw-count (count raw-ops)] (vec (if (<= raw-count limit) @@ -3279,7 +3544,10 @@ "Removes every relationship touching object in final-transaction-bounded batches. Each contention retry reacquires and replans from a fresh basis." [writer object] - (let [{:keys [api]} (backend-writer/state writer) + (let [{:keys [api qualified-writer]} (backend-writer/state writer) + native-writer (when *qualified-authorization-enabled?* + (or (some-> qualified-writer deref) + (typed-capability-error! :qualified-relationship-publication (:backend-id api)))) options (current-writer-options writer) plan-delete (backend-writer/operation writer :plan-delete-object) @@ -3310,7 +3578,7 @@ fitted (largest-fitting-prepared-batch writer db - (plan-delete db object-eid))] + (plan-delete db object-eid) native-writer)] (if-not fitted {:done? true :response @@ -3347,6 +3615,16 @@ (assoc response :retracted-datoms retracted) (recur retracted response)))))) +(defn- qualified-schema-options [api db source options] + (if *qualified-authorization-enabled?* + (do + (qualified-schema! + api db + (expression-resolver/validate-schema source (:expression-limits options) {:allow-caveats? true}) + (:caveat-evaluator options)) + {:allow-caveats? true}) + {:allow-caveats? false})) + (defn- write-schema-through! [writer {:keys [schema] :as request}] (when (= :retain-inert (:orphan-policy request)) @@ -3358,50 +3636,49 @@ :orphan-policy :retain-inert :operation :write-schema!}))) (let [schema-string schema] - (require-operator-expression-writes-enabled! schema-string) - (let [{:keys [conn api]} (backend-writer/state writer) - options (current-writer-options writer) - write-schema! (backend-writer/operation writer :write-schema!) - contention? (backend-writer/operation writer :contention?) - result - (loop [attempt 1] - (let [outcome - (try - (let [expected-generation - (call-with-writer-basis - writer - (fn [{:keys [db]}] - ((backend-writer/operation - writer :schema-generation) - db)))] - {:value - (write-schema! - conn schema-string - (merge - (select-keys options - [:token-ttl-seconds :expression-limits]) - (select-keys request - [:allow-empty-schema? :orphan-policy])) - expected-generation)}) - (catch #?(:clj Throwable :cljs :default) error - {:error error}))] - (if-let [error (:error outcome)] - (if (contention? error) - (if (< attempt (backend-writer/max-attempts writer)) - (recur (inc attempt)) - (relationship-contention! writer attempt error)) - (throw error)) - (:value outcome))))] - (when-not (:eacl.schema/no-op? result) + (require-operator-expression-writes-enabled! schema-string) + (let [{:keys [conn api]} (backend-writer/state writer) + options (current-writer-options writer) + write-schema! (backend-writer/operation writer :write-schema!) + contention? (backend-writer/operation writer :contention?) + result + (loop [attempt 1] + (let [outcome + (try + (let [{:keys [expected-generation admission]} + (call-with-writer-basis + writer + (fn [{:keys [db]}] + {:expected-generation ((backend-writer/operation writer :schema-generation) db) + :admission (qualified-schema-options api db schema-string options)}))] + {:value + (write-schema! + conn schema-string + (merge + (select-keys options + [:token-ttl-seconds :expression-limits]) + (select-keys request + [:allow-empty-schema? :orphan-policy]) admission) + expected-generation)}) + (catch #?(:clj Throwable :cljs :default) error + {:error error}))] + (if-let [error (:error outcome)] + (if (contention? error) + (if (< attempt (backend-writer/max-attempts writer)) + (recur (inc attempt)) + (relationship-contention! writer attempt error)) + (throw error)) + (:value outcome))))] + (when-not (:eacl.schema/no-op? result) ;; Exact keys already include the new immutable basis and managed keys ;; include the certified schema generation. Retaining bounded historical ;; entries is safe and avoids an obsolete whole-cache flush. - (request-counters/add! :writer-submissions)) - (merge result - (if (:eacl.schema/no-op? result) - (write-response api (:eacl.schema/db-after result) options) - (committed-write-response - api (:eacl.schema/db-after result) options)))))) + (request-counters/add! :writer-submissions)) + (merge result + (if (:eacl.schema/no-op? result) + (write-response api (:eacl.schema/db-after result) options) + (committed-write-response + api (:eacl.schema/db-after result) options)))))) (defn- speculative-capability! [api capability] @@ -3584,6 +3861,7 @@ :maximum-snapshot-retention-ms (:maximum-retention-ms basis) :historical-basis? false :source-incarnation (:source-incarnation basis) + :evaluation-time-ms (:evaluation-time-ms basis) :speculative speculative}) api))) @@ -3618,7 +3896,8 @@ db-before schema (merge (select-keys (runtime-options runtime) [:expression-limits]) - (or options {}))) + (or options {}) + (qualified-schema-options api db-before schema (runtime-options runtime)))) tx-data (:speculative-tx-data plan) _ (when-not (and (map? plan) @@ -3646,46 +3925,64 @@ (speculative-snapshot-from-report snapshot report effects (:diagnostics plan))))) -(defn snapshot-tx-relationship - [snapshot update] - (let [{:keys [basis api]} snapshot +(defn- snapshot-tx-ordinary-relationships + [snapshot updates app-datoms] + (let [{:keys [basis api runtime]} snapshot _ (basis-open! basis) db (:db (backend/state (:adapter basis))) - {:keys [operation relationship]} - (if (and (map? update) (contains? update :relationship)) - update - {:operation (:operation update) - :relationship - (->Relationship (:subject update) - (:relation update) - (:resource update))}) - validate-operation! - (get-in api [:impl :validate-relationship-operation!]) plan-update (get-in api [:impl :tx-update-relationship]) prepare (or (:prepare-relationship-tx api) - (fn [_db tx] - (relationship-commit-preconditions-first tx)))] - (when-not (and (ifn? validate-operation!) - (ifn? plan-update)) - (speculative-capability! api :tx-relationship)) - (validate-operation! operation) + (fn [_db tx] (relationship-commit-preconditions-first tx)))] + (when-not (ifn? plan-update) (speculative-capability! api :tx-relationship)) (let [schema ((get-in api [:schema :read-schema]) db) - _ - (schema-errors/validate-relationship-write! - schema :write-relationships - {:resource-type (:type (:resource relationship)) - :subject-type (:type (:subject relationship)) - :relation (:relation relationship)}) - internal-relationship - (spice-relationship->internal - db (runtime-options (:runtime snapshot)) relationship) - internal-update - (->RelationshipUpdate operation internal-relationship) - _ (relationship-mutations/validate-batch! [internal-update]) - raw (some-> (plan-update db internal-update) vec)] - (if (seq raw) - (vec (prepare db raw)) - [])))) + options (runtime-options runtime) + _ (doseq [{:keys [relationship]} updates] + (schema-errors/validate-relationship-write! + schema :write-relationships + {:resource-type (:type (:resource relationship)) + :subject-type (:type (:subject relationship)) :relation (:relation relationship)})) + internal-updates + (relationship-mutations/coalesce-updates + (mapv (fn [update] + (update-in update [:relationship] #(spice-relationship->internal db options %))) updates)) + raw (into (vec (distinct (mapcat #(plan-update db %) internal-updates))) app-datoms)] + (if (seq raw) (vec (prepare db raw)) [])))) + +(defn snapshot-tx-relationships + [snapshot {:keys [updates tx-data] :or {tx-data []}}] + (let [{:keys [basis api runtime]} snapshot + _ (basis-open! basis) + updates (relationship-mutations/normalize-updates (vec updates)) + app-datoms (qualified-writes/application-datoms tx-data #{})] + (if *qualified-authorization-enabled?* + (let [plan (or (:qualified-plan api) + (typed-capability-error! :qualified-relationship-publication (:backend-id api))) + db (:db (backend/state (:adapter basis))) + entries (qualified-publication-entries api db (runtime-options runtime) updates) + entries (mapv (fn [entry] + (if (contains? entry :prepared-qualifier) + (let [prepared (:prepared-qualifier entry)] + (when-not (or (nil? prepared) (qualified-writes/prepared? prepared)) + (qualified-writes/error! :prepared-qualifier-required)) + (assoc entry :value prepared :expected-value (:value entry))) + entry)) entries)] + (:tx-data (plan db entries app-datoms + (select-keys (:identity basis) [:source-id :branch])))) + (do + (when (some #(or (contains? % :prepared-qualifier) + (seq (select-keys (:relationship %) relationship-mutations/qualifier-keys))) updates) + (typed-capability-error! :qualified-relationship-publication (:backend-id api))) + (snapshot-tx-ordinary-relationships snapshot updates app-datoms))))) + +(defn snapshot-tx-relationship + [snapshot update] + (snapshot-tx-relationships + snapshot {:updates [(if (contains? update :relationship) + update + (cond-> {:operation (:operation update) + :relationship (dissoc update :operation :prepared-qualifier)} + (contains? update :prepared-qualifier) + (assoc :prepared-qualifier (:prepared-qualifier update))))]})) (defn- with-page-lookahead "Runs the client-level page operation `thunk` and, when the client has @@ -3693,15 +3990,17 @@ same public operation on `client` after the response is complete." [client runtime operation request thunk] (let [page (thunk) - options (runtime-options runtime)] + options (runtime-options runtime) + qualified? *qualified-authorization-enabled?*] (when-let [state (:lookahead-state options)] (lookahead/submit! state operation request page (fn [continuation] - (case operation - :lookup-resources (eacl/lookup-resources client continuation) - :lookup-subjects (eacl/lookup-subjects client continuation) - :read-relationships (eacl/read-relationships client continuation))) + (binding [*qualified-authorization-enabled?* qualified?] + (case operation + :lookup-resources (eacl/lookup-resources client continuation) + :lookup-subjects (eacl/lookup-subjects client continuation) + :read-relationships (eacl/read-relationships client continuation)))) (:io-observer options))) page)) @@ -3794,6 +4093,7 @@ _ (request-cache-controls request)] (if (empty? (:checks request)) (do + (caveat-context/prepare (get request :caveat-context {})) (execution/normalize (runtime-options runtime) :check-permissions request) []) @@ -3844,11 +4144,17 @@ (-speculative-diagnostics [_] (typed-capability-error! :speculative-diagnostics :acl)) + IRelationshipPreparation + (-prepare-relationship! [_ relationship] + (writer-prepare-relationship! (writable! writer) relationship)) + (-discard-prepared-relationship! [_ prepared] + (writer-discard-prepared-relationship! (writable! writer) prepared)) + IAuthorizationWriter (-write-schema! [_ request] (write-schema-through! (writable! writer) request)) - (-write-relationships! [_ {:keys [updates]}] - (writer-write-relationships! (writable! writer) updates)) + (-write-relationships! [_ request] + (writer-write-relationships! (writable! writer) request)) (-delete-object! [_ {:keys [object]}] (writer-delete-object! (writable! writer) object))) @@ -4124,6 +4430,9 @@ (backend-unification, D-7). Per-backend extensions are declared via the api's :extra-client-opt-keys and documented on the backend's make-client." #{:entid->object-id + :clock + :caveat-evaluator + :qualifier-cache :object-id->lookup-ref :internal-cursor->spice :spice-cursor->internal @@ -4278,7 +4587,14 @@ {:type :eacl/invalid-config :eacl/error :eacl/invalid-config :key :io-observer :value (:io-observer config-opts)}))) + (when (and (contains? config-opts :clock) (not (fn? (:clock config-opts)))) + (throw (ex-info "EACL Config Error: :clock must be a zero-argument millisecond clock function." + {:type :eacl/invalid-config :eacl/error :eacl/invalid-config :key :clock}))) (lookahead/validate-option! (:lookahead config-opts)) + (when (some? (:caveat-evaluator config-opts)) + (caveat-evaluator/require-matching! + (:caveat-evaluator config-opts) caveat-evaluator/profile-fingerprint)) + (qualifier-cache/normalize-option (:qualifier-cache config-opts)) (when-let [scan-cache-option (:scan-cache config-opts)] (when-not (or (false? scan-cache-option) (and (map? scan-cache-option) @@ -4436,6 +4752,7 @@ runtime-cache-lifecycle-config {:cache-option cache :scan-cache-option (:scan-cache config-opts) + :qualifier-cache-option (:qualifier-cache config-opts) :range-reuse-option (:range-reuse config-opts) :proof-contract-reporter proof-contract-reporter :derived-schema-store-factory derived-schema/store @@ -4458,6 +4775,12 @@ (select-keys config-opts (:extra-client-opt-keys api)) {:object-id->lookup-ref object-id->lookup-ref + :caveat-evaluator (if (contains? config-opts :caveat-evaluator) + (:caveat-evaluator config-opts) + (caveat-evaluator/default-evaluator)) + ::evaluation-clock (if (contains? config-opts :clock) + (authorization-clock/clock (:clock config-opts)) + authorization-clock/system-clock) :io-observer (:io-observer config-opts) :lookahead-state (lookahead/state (lookahead/validate-option! (:lookahead config-opts))) diff --git a/modules/eacl/src/eacl/client/range_reuse.cljc b/modules/eacl/src/eacl/client/range_reuse.cljc index 1e3fd3b3..6285d7a9 100644 --- a/modules/eacl/src/eacl/client/range_reuse.cljc +++ b/modules/eacl/src/eacl/client/range_reuse.cljc @@ -17,7 +17,8 @@ Derived and composed pages are returned to the ordinary path, which renders and publishes them under their exact keys like computed pages. Bounded candidate-window routes carry no marker and never participate." - (:require [eacl.cache.standard-lru :as lru])) + (:require [eacl.cache.standard-lru :as lru] + [eacl.authorization.temporal :as temporal])) (def default-max-entries 512) (def default-max-results-per-walk 4096) @@ -148,6 +149,17 @@ (transient {}) (range (count edges))))) +(defn- retain-certificate [value source] + (cond-> value + (and value (contains? source :qualification-certificate)) + (assoc :qualification-certificate (:qualification-certificate source)))) + +(defn- combine-certificates [value left right] + (cond-> value + (or (contains? left :qualification-certificate) (contains? right :qualification-certificate)) + (assoc :qualification-certificate + (temporal/intersect-intervals (:qualification-certificate left) (:qualification-certificate right))))) + (defn- segment-of "A segment from a computed page and the window that produced it. The page's data and edges are in canonical forward order for both kinds. The @@ -156,14 +168,14 @@ [{:keys [kind boundary size]} page] (let [info (:page-info page) edges (:edges page)] - {:data (:data page) - :edges edges - :index (index-edges edges) - :series-size size - :start-boundary (when (= :first kind) (edge-key boundary)) - :end-boundary (when (= :last kind) (edge-key boundary)) - :has-previous? (boolean (:has-previous-page? info)) - :has-next? (boolean (:has-next-page? info))})) + (retain-certificate {:data (:data page) + :edges edges + :index (index-edges edges) + :series-size size + :start-boundary (when (= :first kind) (edge-key boundary)) + :end-boundary (when (= :last kind) (edge-key boundary)) + :has-previous? (boolean (:has-previous-page? info)) + :has-next? (boolean (:has-next-page? info))} page))) (defn- slice-page "The internal page for results `[from, to)` of `segment`." @@ -171,16 +183,16 @@ (let [data (subvec (:data segment) from to) edges (subvec (:edges segment) from to) n (count (:data segment))] - {:data data - :edges edges - :range-reusable? true - :page-info {:start-cursor (when (seq edges) (nth edges 0)) - :end-cursor (when (seq edges) (peek edges)) - :has-next-page? (boolean (and (seq data) - (or (< to n) (:has-next? segment)))) - :has-previous-page? (boolean (and (seq data) - (or (pos? from) - (:has-previous? segment))))}})) + (retain-certificate {:data data + :edges edges + :range-reusable? true + :page-info {:start-cursor (when (seq edges) (nth edges 0)) + :end-cursor (when (seq edges) (peek edges)) + :has-next-page? (boolean (and (seq data) + (or (< to n) (:has-next? segment)))) + :has-previous-page? (boolean (and (seq data) + (or (pos? from) + (:has-previous? segment))))}} segment))) (defn ^:no-doc forward-position "The index in `segment` of the first result after `boundary`, or nil @@ -211,34 +223,35 @@ nil." [{:keys [kind size boundary]} segment] (let [n (count (:data segment))] - (case kind - :first - (when-let [from (forward-position segment boundary)] - (let [held (- n from) - continuation (fn [remaining] - (cond-> {:kind :first - :size remaining - :boundary (peek (:edges segment))} - (:series-size segment) - (assoc :checkpoint-size (:series-size segment))))] - (cond - (>= held size) {:page (slice-page segment from (+ from size))} - (not (:has-next? segment)) {:page (slice-page segment from n)} - (pos? held) {:partial (slice-page segment from n) - :continuation (continuation (- size held))} - (and (pos? n) (:series-size segment)) {:continuation (continuation size)} - :else nil))) - :last - (when-let [to (backward-position segment boundary)] - (cond - (>= to size) {:page (slice-page segment (- to size) to)} - (not (:has-previous? segment)) {:page (slice-page segment 0 to)} - (pos? to) {:partial (slice-page segment 0 to) - :continuation {:kind :last - :size (- size to) - :boundary (nth (:edges segment) 0)}} - :else nil)) - nil))) + (retain-certificate + (case kind + :first + (when-let [from (forward-position segment boundary)] + (let [held (- n from) + continuation (fn [remaining] + (cond-> {:kind :first + :size remaining + :boundary (peek (:edges segment))} + (:series-size segment) + (assoc :checkpoint-size (:series-size segment))))] + (cond + (>= held size) {:page (slice-page segment from (+ from size))} + (not (:has-next? segment)) {:page (slice-page segment from n)} + (pos? held) {:partial (slice-page segment from n) + :continuation (continuation (- size held))} + (and (pos? n) (:series-size segment)) {:continuation (continuation size)} + :else nil))) + :last + (when-let [to (backward-position segment boundary)] + (cond + (>= to size) {:page (slice-page segment (- to size) to)} + (not (:has-previous? segment)) {:page (slice-page segment 0 to)} + (pos? to) {:partial (slice-page segment 0 to) + :continuation {:kind :last + :size (- size to) + :boundary (nth (:edges segment) 0)}} + :else nil)) + nil) segment))) (defn- best [candidates] @@ -286,19 +299,19 @@ edges (into (:edges left) (:edges right)) left-info (:page-info left) right-info (:page-info right)] - {:data data - :edges edges - :range-reusable? true - :page-info {:start-cursor (when (seq edges) (nth edges 0)) - :end-cursor (when (seq edges) (peek edges)) - :has-next-page? (boolean - (if (seq (:data right)) - (:has-next-page? right-info) - (:has-next-page? left-info))) - :has-previous-page? (boolean - (if (seq (:data left)) - (:has-previous-page? left-info) - (:has-previous-page? right-info)))}}))) + (combine-certificates {:data data + :edges edges + :range-reusable? true + :page-info {:start-cursor (when (seq edges) (nth edges 0)) + :end-cursor (when (seq edges) (peek edges)) + :has-next-page? (boolean + (if (seq (:data right)) + (:has-next-page? right-info) + (:has-next-page? left-info))) + :has-previous-page? (boolean + (if (seq (:data left)) + (:has-previous-page? left-info) + (:has-previous-page? right-info)))}} left right)))) ;; --------------------------------------------------------------------------- ;; Publication: merging and bounds @@ -308,14 +321,14 @@ "The series at the merged segment's end is the right part's series." [left right] (let [edges (into (:edges left) (:edges right))] - {:data (into (:data left) (:data right)) - :edges edges - :index (index-edges edges) - :series-size (:series-size right) - :start-boundary (:start-boundary left) - :end-boundary (:end-boundary right) - :has-previous? (:has-previous? left) - :has-next? (:has-next? right)})) + (combine-certificates {:data (into (:data left) (:data right)) + :edges edges + :index (index-edges edges) + :series-size (:series-size right) + :start-boundary (:start-boundary left) + :end-boundary (:end-boundary right) + :has-previous? (:has-previous? left) + :has-next? (:has-next? right)} left right))) (defn- covers? "True when `segment` already holds every result of `candidate` at the diff --git a/modules/eacl/src/eacl/core.cljc b/modules/eacl/src/eacl/core.cljc index 8ebe5900..9608d0c0 100644 --- a/modules/eacl/src/eacl/core.cljc +++ b/modules/eacl/src/eacl/core.cljc @@ -41,6 +41,16 @@ (-write-relationships! [this request]) (-delete-object! [this request])) +(defprotocol IRelationshipPreparation + "Explicit writer-owned preparation for caller-composed qualified transactions." + (-prepare-relationship! [this relationship]) + (-discard-prepared-relationship! [this prepared])) + +(defprotocol IRelationshipPlanning + "Atomic batch planning on one immutable snapshot." + (-tx-relationships [this request])) + + (defprotocol ISnapshotSource "Selects one immutable authorization snapshot." (-snapshot [this consistency options])) @@ -128,14 +138,24 @@ :consistency consistency}))) (defn can? - "Returns the `:allowed?` projection of `check-permission`." + "Returns true only for a definite grant. Authoritative qualified evaluation + failures become false here; check-permission preserves their typed error. + Cancellation, resource limits, invalid requests, and backend errors propagate." ([target request] - (:allowed? (check-permission target request))) + (try + (let [decision (check-permission target request)] + (and (true? (:allowed? decision)) + (or (not (contains? decision :permissionship)) + (= :has-permission (:permissionship decision))))) + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (if (= :eacl.authorization/evaluation-failure (:type (ex-data error))) + false + (throw error))))) ([target subject permission resource] - (:allowed? (check-permission target subject permission resource))) + (can? target {:subject subject :permission permission :resource resource})) ([target subject permission resource consistency] - (:allowed? - (check-permission target subject permission resource consistency)))) + (can? target {:subject subject :permission permission :resource resource + :consistency consistency}))) (defn read-schema ([target] @@ -181,6 +201,27 @@ updates {:updates updates}))) +(defn prepare-relationship! + "Creates an inert qualifier for a Relationship and returns an opaque handle + (nil for an ordinary Relationship). Pass it as :prepared-qualifier on the + update supplied to tx-relationship. Preparation never grants access." + [target relationship] + (if (satisfies? IRelationshipPreparation target) + (-prepare-relationship! target relationship) + (throw (typed-error :eacl/unsupported-capability + "Target cannot prepare a qualified Relationship." + {:capability :prepare-relationship :target (target-kind target)})))) + +(defn discard-prepared-relationship! + "Removes an unchanged, unattached preparation through its original writer. + Attached or altered qualifiers are rejected." + [target prepared] + (if (satisfies? IRelationshipPreparation target) + (-discard-prepared-relationship! target prepared) + (throw (typed-error :eacl/unsupported-capability + "Target cannot discard a Relationship preparation." + {:capability :discard-prepared-relationship :target (target-kind target)})))) + (defn delete-object! [target object] (-delete-object! (writer! target) @@ -195,12 +236,13 @@ :subject subject :relation relation :resource resource})) - ([target {:keys [operation subject relation resource]}] + ([target {:keys [operation subject relation resource] :as update}] (write-relationships! target [(->RelationshipUpdate operation - (->Relationship subject relation resource))]))) + (merge (->Relationship subject relation resource) + (select-keys update [:caveat :caveat-context :valid-until-ms])))]))) (defn with "Applies native transaction data in memory and returns an immutable, @@ -230,6 +272,17 @@ {:capability :with-schema :target (target-kind target)}))))) +(defn tx-relationships + "Plans an atomic batch on one snapshot. Accepts updates or + {:updates [...] :tx-data [...]} for application composition. Prepared + backends require :prepared-qualifier handles on qualified updates." + [snapshot request] + (if (and (snapshot? snapshot) (satisfies? IRelationshipPlanning snapshot)) + (-tx-relationships snapshot (if (map? request) request {:updates request})) + (throw (typed-error :eacl/unsupported-capability + "Target cannot plan a Relationship batch." + {:capability :tx-relationships :target (target-kind snapshot)})))) + (defn tx-relationship "Plans one relationship mutation against an immutable EACL snapshot. diff --git a/modules/eacl/src/eacl/engine/least_path.cljc b/modules/eacl/src/eacl/engine/least_path.cljc index 5b944453..855aa51b 100644 --- a/modules/eacl/src/eacl/engine/least_path.cljc +++ b/modules/eacl/src/eacl/engine/least_path.cljc @@ -28,9 +28,13 @@ telemetry); the caller's cut-point runs before every adapter command; `:max-commands`/`:max-values` budgets mirror the probe check's and fail typed as `:eacl.reducer/limit-exceeded`." - (:require [eacl.backend.v8 :as backend] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.result :as authorization-result] + [eacl.authorization.qualification :as qualification] + [eacl.backend.v8 :as backend] [eacl.engine.stable-reducer :as reducer] - [eacl.engine.stable-route :as route])) + [eacl.engine.stable-route :as route] + [eacl.relationships.edge :as edge])) (defn adapter-fetch-fn "Direction-aware width-one read seam: realizes one read-demand @@ -43,6 +47,7 @@ resource->subjects (backend/scan-invoker adapter :resource->subjects)] (fn [{:keys [operation bound-eid direction] :as descriptor}] (let [options (cond-> {:direction (or direction :asc)} + (:include-qualifier? descriptor) (assoc :include-qualifier? true) (:limit descriptor) (assoc :limit (:limit descriptor)) bound-eid (assoc :bound-eid bound-eid :inclusive-bound? false))] @@ -77,7 +82,7 @@ (or `:adapter` for the direct path); budgets and the cut-point mirror the probe check's semantics." [{:keys [adapter fetch-fn cut-point! physical-chunk-size - max-commands max-values] + max-commands max-values qualification] :or {physical-chunk-size reducer/default-physical-chunk-size max-commands reducer/default-max-commands max-values reducer/default-max-values}}] @@ -85,6 +90,7 @@ counters (volatile! {:commands 0 :fetched-values 0 :stream-opens 0 :emissions 0})] {:counters counters + :qualification qualification ;; Request-local witness-child prefixes (task 3.2's memoization): ;; every arrow-permission witness on this request alternates against ;; the SAME ascending child enumeration, so its emission prefix is @@ -99,7 +105,8 @@ (when (>= (:commands @counters) max-commands) (limit-failure! :max-commands @counters {:max-commands max-commands})) - (let [values (reducer/bounded-vector (fetch-fn descriptor) + (let [descriptor (cond-> descriptor qualification (assoc :include-qualifier? true)) + values (reducer/bounded-vector (fetch-fn descriptor) (:limit descriptor))] (when (> (+ (:fetched-values @counters) (count values)) max-values) @@ -140,11 +147,19 @@ {:mk mk-desc :buf [] :idx 0 :bound bound :done? false}) (defn- stream-next - "[value stream'] or [nil stream'] on exhaustion." + "[value stream'] or [nil stream'] on exhaustion. Qualified false rows + advance physical bounds and consume the same command/value budgets." [ctx {:keys [mk buf idx bound done?] :as s}] (cond (< idx (count buf)) - [(nth buf idx) (assoc s :idx (inc idx) :bound (nth buf idx))] + (let [value (nth buf idx) eid (edge/endpoint value) + result (if (and (:qualification ctx) (vector? value)) + (evidence/throw-if-fault! + (qualification/qualify (:qualification ctx) (:relation-eid (mk bound 1)) value)) + true) + next-stream (cond-> (assoc s :idx (inc idx) :bound eid) + (:qualification ctx) (assoc :evidence result))] + [eid next-stream]) done? [nil s] @@ -152,67 +167,94 @@ (let [chunk ((:fetch! ctx) (mk bound (:chunk ctx)))] (if (empty? chunk) [nil (assoc s :done? true)] - [(nth chunk 0) - (assoc s :buf chunk :idx 1 :bound (nth chunk 0) - :done? (< (count chunk) (:chunk ctx)))])))) + (recur ctx (assoc s :buf chunk :idx 0 + :done? (< (count chunk) (:chunk ctx)))))))) + +(defn- stream-next-active + "Only enumeration skips inactive prefixes here. Witness intersections + retain one physical row per side per step to preserve shorter-side work." + [ctx s] + (loop [s s] + (let [[value next-stream] (stream-next ctx s)] + (if (and value (evidence/no? (get next-stream :evidence true))) + (recur next-stream) + [value next-stream])))) ;; --------------------------------------------------------------------------- ;; Exact-bound probes and bounded intersections (witness primitives) ;; --------------------------------------------------------------------------- -(defn- probe-fwd? - "Does (subject, relation, candidate) exist? One exact-bound forward scan." +(defn- probe-fwd-evidence [ctx subject-type subject-eid relation-eid resource-type candidate] - (= candidate - (first ((:fetch! ctx) - (fwd-scan subject-type subject-eid relation-eid resource-type - (dec candidate) 1 false))))) - -(defn- probe-rev? - "Does (candidate, relation, resource) exist? One exact-bound reverse scan." + (let [value (first ((:fetch! ctx) + (fwd-scan subject-type subject-eid relation-eid resource-type + (dec candidate) 1 false)))] + (if (= candidate (edge/endpoint value)) + (if (:qualification ctx) + (evidence/throw-if-fault! + (qualification/qualify (:qualification ctx) relation-eid value)) + true) + false))) + +(defn- probe-rev-evidence [ctx resource-type resource-eid relation-eid subject-type candidate] - (= candidate - (first ((:fetch! ctx) - (rev-scan resource-type resource-eid relation-eid subject-type - (dec candidate) 1 false))))) + (let [value (first ((:fetch! ctx) + (rev-scan resource-type resource-eid relation-eid subject-type + (dec candidate) 1 false)))] + (if (= candidate (edge/endpoint value)) + (if (:qualification ctx) + (evidence/throw-if-fault! + (qualification/qualify (:qualification ctx) relation-eid value)) + true) + false))) + +(defn- probe-fwd? [ctx subject-type subject-eid relation-eid resource-type candidate] + (not (evidence/no? (probe-fwd-evidence ctx subject-type subject-eid relation-eid resource-type candidate)))) + +(defn- probe-rev? [ctx resource-type resource-eid relation-eid subject-type candidate] + (not (evidence/no? (probe-rev-evidence ctx resource-type resource-eid relation-eid subject-type candidate)))) + +(defn- path-active? [ctx state] + (or (nil? (:qualification ctx)) (not (evidence/no? (get state :evidence true))))) + +(defn- path-hit? [ctx state other] + (if (:qualification ctx) + (not (evidence/no? (evidence/throw-if-fault! + (evidence/combine :arrow (get state :evidence true) + (if (nil? other) false other))))) + (boolean other))) (defn- isect2? - "Interleaved min-side intersection of two ascending streams, each side - decided by the opposite side's exact probe - (BidirectionalArrowIntersection.dfy `Decide`): stops at the first hit - or the first exhausted side. `below` (exclusive) bounds both sides." + "Shorter-side intersection witness. A qualified hit requires the two + endpoint conditions to be jointly possible, including their residuals." [ctx a-stream a-member? b-stream b-member? below] (let [beyond? (fn [v] (and below (>= v below)))] (loop [a a-stream b b-stream] (let [[av a'] (stream-next ctx a)] (cond (or (nil? av) (beyond? av)) false - (a-member? av) true + (and (path-active? ctx a') (path-hit? ctx a' (a-member? av))) true :else (let [[bv b'] (stream-next ctx b)] (cond (or (nil? bv) (beyond? bv)) false - (b-member? bv) true + (and (path-active? ctx b') (path-hit? ctx b' (b-member? bv))) true :else (recur a' b')))))))) (defn- least-common - "The LEAST element of the intersection of two ascending streams, or nil - when it is empty — strict alternation with the opposite side's exact - membership probe. Both streams are ascending, so the first common - element either side reaches IS the least common element; cost is - bounded by the SMALLER side's prefix, never one side's total fan-in - (the same min-side property `isect2?` has for the decision form)." + "Least jointly possible intermediate, decided by alternating exact probes + on the two ascending sides. Exhaustion of either side ends the search." [ctx a-stream a-member? b-stream b-member?] (loop [a a-stream b b-stream] (let [[av a'] (stream-next ctx a)] (cond (nil? av) nil - (a-member? av) av + (and (path-active? ctx a') (path-hit? ctx a' (a-member? av))) av :else (let [[bv b'] (stream-next ctx b)] (cond (nil? bv) nil - (b-member? bv) bv + (and (path-active? ctx b') (path-hit? ctx b' (b-member? bv))) bv :else (recur a' b'))))))) (defn- shared-child-pull @@ -256,7 +298,7 @@ (loop [es entity-stream pos 0 child-done? false] (let [[cand es'] (stream-next ctx es)] (cond - (and cand (entity-hit? cand)) true + (and cand (path-active? ctx es') (path-hit? ctx es' (entity-hit? cand))) true (nil? cand) false :else (if child-done? @@ -265,7 +307,7 @@ (cond (nil? emission) (recur es' pos true) (and child-stop? (child-stop? emission)) (recur es' pos true) - (child-hit? emission) true + (path-hit? ctx emission (child-hit? emission)) true :else (recur es' pos' false)))))))) (defn- candidate-accepted? @@ -273,26 +315,72 @@ a zero-work true branch so existing union-only plans retain their trace." [env node subject-eid resource-eid] (if-let [accept? (:candidate-accept? env)] - (boolean - (accept? {:node node - :direction (:traversal env) - :subject-type (:subject-type env) - :subject-eid subject-eid - :resource-eid resource-eid})) + (let [result (accept? {:node node + :direction (:traversal env) + :subject-type (:subject-type env) + :subject-eid subject-eid + :resource-eid resource-eid})] + (if (:qualification env) + (not (evidence/no? (evidence/throw-if-fault! result))) + (boolean result))) true)) -(defn- derives? +(defn- accepted-emission + "Complete only an active generated path. Legacy union paths that already + prove Has need no extra point proof or qualification context work." + [env node rule subject-eid resource-eid value coords path-evidence] + (if (:qualification env) + (when-not (evidence/no? path-evidence) + (let [result (if (and (:legacy-qualified? env) (evidence/has? path-evidence)) + path-evidence + ((:candidate-accept? env) + {:node node :direction (:traversal env) + :subject-type (:subject-type env) + :subject-eid subject-eid :resource-eid resource-eid + :evidence-witness {:rule rule :evidence path-evidence}}))] + (when-not (evidence/no? (evidence/throw-if-fault! result)) + {:value value :coords coords :evidence result}))) + (when (candidate-accepted? env node subject-eid resource-eid) + {:value value :coords coords}))) + +(defn- accepted-arrow-emission + [env node rule subject-eid resource-eid value coords intermediate path-evidence] + (if (:qualification env) + (when-not (evidence/no? path-evidence) + (let [result (if (and (:legacy-qualified? env) (evidence/has? path-evidence)) + path-evidence + ((:candidate-accept? env) + {:node node :direction (:traversal env) + :subject-type (:subject-type env) + :subject-eid subject-eid :resource-eid resource-eid + :evidence-witness {:rule rule :intermediate intermediate :evidence path-evidence}}))] + (when-not (evidence/no? (evidence/throw-if-fault! result)) + {:value value :coords coords :evidence result}))) + (accepted-emission env node rule subject-eid resource-eid value coords true))) + +(defn- node-evidence "Does `subject-eid` reach `node`'s permission on `resource-eid`? The certified membership-probe check anchored at `node` (`eacl.engine.stable-route/derives-from-node?`)." [{:keys [route-opts] :as env} node subject-eid resource-eid] - (and - (route/derives-from-node? - (assoc route-opts - :start-node node - :subject-eid subject-eid - :resource-eid resource-eid)) - (candidate-accepted? env node subject-eid resource-eid))) + (if (:qualification env) + ;; The qualified local predicate proves the actual node. Running the + ;; structural cover's point traversal first would duplicate that work. + (evidence/throw-if-fault! + ((:candidate-accept? env) + {:node node :direction (:traversal env) :subject-type (:subject-type env) + :subject-eid subject-eid :resource-eid resource-eid})) + (boolean + (and + (route/derives-from-node? + (assoc route-opts + :start-node node + :subject-eid subject-eid + :resource-eid resource-eid)) + (candidate-accepted? env node subject-eid resource-eid))))) + +(defn- derives? [env node subject-eid resource-eid] + (not (evidence/no? (node-evidence env node subject-eid resource-eid)))) ;; --------------------------------------------------------------------------- ;; Coordinates @@ -410,7 +498,7 @@ (:target-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-rev? ctx (:resource-type rule) v + #(probe-rev-evidence ctx (:resource-type rule) v (:via-relation-eid rule) (:intermediate-type rule) %) ;; V's via-set @@ -418,7 +506,7 @@ (:via-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-fwd? ctx subject-type subject-eid + #(probe-fwd-evidence ctx subject-type subject-eid (:target-relation-eid rule) (:intermediate-type rule) %) nil)) @@ -436,12 +524,12 @@ (:via-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(derives? env node subject-eid %) + #(node-evidence env node subject-eid %) (fn [pos] (shared-child-pull ctx asc-env [node subject-eid] #(fwd-mk-level asc-env node) fwd-level-next pos)) - #(probe-rev? ctx (:resource-type rule) v + #(probe-rev-evidence ctx (:resource-type rule) v (:via-relation-eid rule) (:intermediate-type rule) (:value %)) nil))))) @@ -488,7 +576,7 @@ (:intermediate-type rule) %1 %2 false) nil) - #(probe-rev? ctx (:resource-type rule) v + #(probe-rev-evidence ctx (:resource-type rule) v (:via-relation-eid rule) (:intermediate-type rule) %) (stream #(rev-scan (:resource-type rule) v @@ -496,7 +584,7 @@ (:intermediate-type rule) %1 %2 false) nil) - #(probe-fwd? ctx subject-type subject-eid + #(probe-fwd-evidence ctx subject-type subject-eid (:target-relation-eid rule) (:intermediate-type rule) %))] (when i [(:ordinal rule) i v]))) @@ -513,8 +601,10 @@ (let [[i s'] (stream-next ctx s)] (if (nil? i) best - (let [sub (fwd-least-coords - env (:target-node rule) i)] + (let [sub (when (and (path-active? ctx s') + (or (nil? (:qualification env)) + (path-hit? ctx s' (node-evidence env (:target-node rule) subject-eid i)))) + (fwd-least-coords env (:target-node rule) i))] (recur s' (if (and sub (or (nil? best) @@ -547,12 +637,15 @@ nil) (fn [cand] (when-let [least (fwd-least-coords asc-env node cand)] - (neg? (compare-coords least i-coords)))) + (when (neg? (compare-coords least i-coords)) + (if (:qualification env) + (node-evidence env node (:subject-eid env) cand) + true)))) (fn [pos] (shared-child-pull ctx asc-env [node (:subject-eid env)] #(fwd-mk-level asc-env node) fwd-level-next pos)) - #(probe-rev? ctx (:resource-type rule) v + #(probe-rev-evidence ctx (:resource-type rule) v (:via-relation-eid rule) (:intermediate-type rule) (:value %)) #(>= (compare-coords (:coords %) i-coords) 0)))) @@ -573,14 +666,14 @@ (:target-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-rev? ctx (:resource-type rule) v + #(probe-rev-evidence ctx (:resource-type rule) v (:via-relation-eid rule) (:intermediate-type rule) %) (stream #(rev-scan (:resource-type rule) v (:via-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-fwd? ctx subject-type subject-eid + #(probe-fwd-evidence ctx subject-type subject-eid (:target-relation-eid rule) (:intermediate-type rule) %) i)) @@ -623,10 +716,13 @@ (if (nil? v) (recur env (advance)) (let [level' (assoc level :sub {:scan scan'})] - (if (and (fwd-emit2? env level rule nil v) - (candidate-accepted? - env (:node level) (:subject-eid env) v)) - [{:value v :coords [(:ordinal rule) v]} level'] + (if-let [emission (when (and (path-active? (:ctx env) scan') + (fwd-emit2? env level rule nil v)) + (accepted-emission env (:node level) rule + (:subject-eid env) v v + [(:ordinal rule) v] + (get scan' :evidence true)))] + [emission level'] (recur env level'))))) :self-permission @@ -637,19 +733,21 @@ v (:value emission)] ;; earlier-rule witness only: the child already emits ;; least-only within the target node. - (if (and - (not-any? #(fwd-rule-derives? env (nth rules %) v) - (earlier-in-sealed-order level)) - (candidate-accepted? - env (:node level) (:subject-eid env) v)) - [{:value v - :coords (into [(:ordinal rule)] (:coords emission))} - level'] + (if-let [result + (when (not-any? #(fwd-rule-derives? env (nth rules %) v) + (earlier-in-sealed-order level)) + (accepted-emission env (:node level) rule + (:subject-eid env) v v + (into [(:ordinal rule)] (:coords emission)) + (get emission :evidence true)))] + [result level'] (recur env level'))))) :arrow-relation (if (nil? (:i sub)) - (let [[i outer'] (stream-next (:ctx env) (:outer sub))] + (let [[i outer'] (if (:qualification env) + (stream-next-active (:ctx env) (:outer sub)) + (stream-next (:ctx env) (:outer sub)))] (if (nil? i) (recur env (advance)) (recur env (assoc level :sub @@ -660,12 +758,17 @@ (recur env (assoc level :sub (assoc sub :i nil :inner nil))) (let [level' (assoc level :sub (assoc sub :inner inner'))] - (if (and (fwd-emit2? env level rule {:i (:i sub)} v) - (candidate-accepted? - env (:node level) (:subject-eid env) v)) - [{:value v - :coords [(:ordinal rule) (:i sub) v]} level'] - (recur env level')))))) + (let [path (if (:qualification env) + (evidence/combine :arrow (get (:outer sub) :evidence true) + (get inner' :evidence true)) true)] + (if-let [result (when (and (not (evidence/no? path)) + (fwd-emit2? env level rule {:i (:i sub)} v)) + (accepted-arrow-emission env (:node level) rule + (:subject-eid env) v v + [(:ordinal rule) (:i sub) v] + (:i sub) path))] + [result level'] + (recur env level'))))))) :arrow-permission (if (nil? (:i sub)) @@ -681,15 +784,19 @@ (recur env (assoc level :sub (assoc sub :i nil :inner nil))) (let [level' (assoc level :sub (assoc sub :inner inner'))] - (if (and (fwd-emit2? env level rule {:i (:i sub)} v) - (candidate-accepted? - env (:node level) (:subject-eid env) v)) - [{:value v - :coords (-> [(:ordinal rule)] - (into (:coords (:i sub))) - (conj v))} - level'] - (recur env level'))))))))))) + (let [path (if (:qualification env) + (evidence/combine :arrow (get (:i sub) :evidence true) + (get inner' :evidence true)) true)] + (if-let [result (when (and (not (evidence/no? path)) + (fwd-emit2? env level rule {:i (:i sub)} v)) + (accepted-arrow-emission env (:node level) rule + (:subject-eid env) v v + (-> [(:ordinal rule)] + (into (:coords (:i sub))) + (conj v)) + (:value (:i sub)) path))] + [result level'] + (recur env level')))))))))))) ;; --------------------------------------------------------------------------- ;; Reverse enumeration (lookup-subjects): subjects for one entity @@ -753,14 +860,14 @@ (:via-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-fwd? ctx subject-type s + #(probe-fwd-evidence ctx subject-type s (:target-relation-eid rule) (:intermediate-type rule) %) (stream #(fwd-scan subject-type s (:target-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-rev? ctx (:resource-type rule) entity + #(probe-rev-evidence ctx (:resource-type rule) entity (:via-relation-eid rule) (:intermediate-type rule) %) nil)) @@ -774,12 +881,12 @@ (:via-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(derives? env node s %) + #(node-evidence env node s %) (fn [pos] (shared-child-pull ctx child-env [node s] #(fwd-mk-level child-env node) fwd-level-next pos)) - #(probe-rev? ctx (:resource-type rule) entity + #(probe-rev-evidence ctx (:resource-type rule) entity (:via-relation-eid rule) (:intermediate-type rule) (:value %)) nil))))) @@ -797,14 +904,14 @@ (:via-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-fwd? ctx subject-type s + #(probe-fwd-evidence ctx subject-type s (:target-relation-eid rule) (:intermediate-type rule) %) (stream #(fwd-scan subject-type s (:target-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(probe-rev? ctx (:resource-type rule) entity + #(probe-rev-evidence ctx (:resource-type rule) entity (:via-relation-eid rule) (:intermediate-type rule) %) i) @@ -817,13 +924,13 @@ (:via-relation-eid rule) (:intermediate-type rule) %1 %2 false) nil) - #(and (< % i) (derives? env node s %)) + #(and (< % i) (node-evidence env node s %)) (fn [pos] (shared-child-pull ctx child-env [node s] #(fwd-mk-level child-env node) fwd-level-next pos)) #(and (< (:value %) i) - (probe-rev? ctx (:resource-type rule) entity + (probe-rev-evidence ctx (:resource-type rule) entity (:via-relation-eid rule) (:intermediate-type rule) (:value %))) nil))))) @@ -851,10 +958,13 @@ (if (nil? s) (recur env (advance)) (let [level' (assoc level :sub {:scan scan'})] - (if (and (rev-emit? env level rule nil s) - (candidate-accepted? - env (:node level) s (:entity level))) - [{:value s :coords [(:ordinal rule) s]} level'] + (if-let [emission (when (and (path-active? (:ctx env) scan') + (rev-emit? env level rule nil s)) + (accepted-emission env (:node level) rule + s (:entity level) s + [(:ordinal rule) s] + (get scan' :evidence true)))] + [emission level'] (recur env level'))))) :self-permission @@ -863,19 +973,21 @@ (recur env (advance)) (let [level' (assoc level :sub {:child child'}) s (:value emission)] - (if (and - (not-any? #(rev-rule-derives? env (nth rules %) entity s) - (earlier-in-sealed-order level)) - (candidate-accepted? - env (:node level) s (:entity level))) - [{:value s - :coords (into [(:ordinal rule)] (:coords emission))} - level'] + (if-let [result + (when (not-any? #(rev-rule-derives? env (nth rules %) entity s) + (earlier-in-sealed-order level)) + (accepted-emission env (:node level) rule + s (:entity level) s + (into [(:ordinal rule)] (:coords emission)) + (get emission :evidence true)))] + [result level'] (recur env level'))))) :arrow-relation (if (nil? (:i sub)) - (let [[i outer'] (stream-next (:ctx env) (:outer sub))] + (let [[i outer'] (if (:qualification env) + (stream-next-active (:ctx env) (:outer sub)) + (stream-next (:ctx env) (:outer sub)))] (if (nil? i) (recur env (advance)) (recur env @@ -891,16 +1003,23 @@ (if (nil? s) (recur env (assoc level :sub (assoc sub :i nil :inner nil))) (let [level' (assoc level :sub (assoc sub :inner inner'))] - (if (and (rev-emit? env level rule (:i sub) s) - (candidate-accepted? - env (:node level) s (:entity level))) - [{:value s :coords [(:ordinal rule) (:i sub) s]} - level'] - (recur env level')))))) + (let [path (if (:qualification env) + (evidence/combine :arrow (get (:outer sub) :evidence true) + (get inner' :evidence true)) true)] + (if-let [result (when (and (not (evidence/no? path)) + (rev-emit? env level rule (:i sub) s)) + (accepted-arrow-emission env (:node level) rule + s (:entity level) s + [(:ordinal rule) (:i sub) s] + (:i sub) path))] + [result level'] + (recur env level'))))))) :arrow-permission (if (nil? (:i sub)) - (let [[i outer'] (stream-next (:ctx env) (:outer sub))] + (let [[i outer'] (if (:qualification env) + (stream-next-active (:ctx env) (:outer sub)) + (stream-next (:ctx env) (:outer sub)))] (if (nil? i) (recur env (advance)) (recur env @@ -913,14 +1032,18 @@ (recur env (assoc level :sub (assoc sub :i nil :child nil))) (let [level' (assoc level :sub (assoc sub :child child')) s (:value emission)] - (if (and (rev-emit? env level rule (:i sub) s) - (candidate-accepted? - env (:node level) s (:entity level))) - [{:value s - :coords (-> [(:ordinal rule) (:i sub)] - (into (:coords emission)))} - level'] - (recur env level'))))))))))) + (let [path (if (:qualification env) + (evidence/combine :arrow (get (:outer sub) :evidence true) + (get emission :evidence true)) true)] + (if-let [result (when (and (not (evidence/no? path)) + (rev-emit? env level rule (:i sub) s)) + (accepted-arrow-emission env (:node level) rule + s (:entity level) s + (-> [(:ordinal rule) (:i sub)] + (into (:coords emission))) + (:i sub) path))] + [result level'] + (recur env level')))))))))))) ;; --------------------------------------------------------------------------- ;; Resume: rebuild a level positioned strictly past boundary coordinates @@ -942,6 +1065,11 @@ (assoc data :eacl/error :eacl.page/invalid-cursor :reason reason)))) +(defn- resume-evidence! [value] + (when (evidence/no? value) + (invalid-coords! :inactive-qualified-binding {})) + value) + (defn- check-arity! "Coordinate arity is a function of the rule kind; a mismatch must fail typed (the caller maps it to the public stale-cursor error), never as @@ -985,10 +1113,14 @@ :arrow-relation (let [i (nth coords 1) v (nth coords 2)] - {:outer (stream #(fwd-scan subject-type subject-eid - (:target-relation-eid rule) - (:intermediate-type rule) %1 %2 desc?) - i) + {:outer (cond-> (stream #(fwd-scan subject-type subject-eid + (:target-relation-eid rule) + (:intermediate-type rule) %1 %2 desc?) i) + (:qualification env) + (assoc :evidence (resume-evidence! + (probe-fwd-evidence (:ctx env) subject-type subject-eid + (:target-relation-eid rule) + (:intermediate-type rule) i)))) :i i :inner (stream #(fwd-scan (:intermediate-type rule) i (:via-relation-eid rule) @@ -1003,7 +1135,10 @@ ;; The intermediate value is the sub-derivation's own leaf: ;; the deepest coordinate of the sub-path. {:child child - :i {:value i :coords (vec subcoords)} + :i (cond-> {:value i :coords (vec subcoords)} + (:qualification env) + (assoc :evidence (resume-evidence! + (node-evidence env (:target-node rule) subject-eid i)))) :inner (stream #(fwd-scan (:intermediate-type rule) i (:via-relation-eid rule) (:resource-type rule) %1 %2 desc?) @@ -1036,10 +1171,14 @@ :arrow-relation (let [i (nth coords 1) s (nth coords 2)] - {:outer (stream #(rev-scan (:resource-type rule) entity - (:via-relation-eid rule) - (:intermediate-type rule) %1 %2 desc?) - i) + {:outer (cond-> (stream #(rev-scan (:resource-type rule) entity + (:via-relation-eid rule) + (:intermediate-type rule) %1 %2 desc?) i) + (:qualification env) + (assoc :evidence (resume-evidence! + (probe-rev-evidence (:ctx env) (:resource-type rule) entity + (:via-relation-eid rule) + (:intermediate-type rule) i)))) :i i :inner (stream #(rev-scan (:intermediate-type rule) i (:target-relation-eid rule) @@ -1049,10 +1188,14 @@ :arrow-permission (let [i (nth coords 1) subcoords (subvec coords 2)] - {:outer (stream #(rev-scan (:resource-type rule) entity - (:via-relation-eid rule) - (:intermediate-type rule) %1 %2 desc?) - i) + {:outer (cond-> (stream #(rev-scan (:resource-type rule) entity + (:via-relation-eid rule) + (:intermediate-type rule) %1 %2 desc?) i) + (:qualification env) + (assoc :evidence (resume-evidence! + (probe-rev-evidence (:ctx env) (:resource-type rule) entity + (:via-relation-eid rule) + (:intermediate-type rule) i)))) :i i :child (rev-resume-level env (:target-node rule) i subcoords)}))] {:node node :entity entity :rules rules :order order :oi oi :sub sub})) @@ -1061,20 +1204,63 @@ ;; Public pagination API ;; --------------------------------------------------------------------------- +(def maximum-qualified-node-evidence 100000) + +(defn- legacy-node-acceptor [options] + (let [request (:qualification options) + route-options (assoc (select-keys options reducer/run-option-keys) :qualification request) + scope (delay (qualification/exact-reuse-identity request)) + memo (volatile! {})] + (fn [{:keys [node subject-type subject-eid resource-eid evidence-witness]}] + (when evidence-witness (evidence/throw-if-fault! (:evidence evidence-witness))) + (if (and evidence-witness (evidence/has? (:evidence evidence-witness))) + (:evidence evidence-witness) + (let [point [node subject-type subject-eid resource-eid]] + (if (contains? @memo point) + (get @memo point) + (do + (when (>= (count @memo) maximum-qualified-node-evidence) + (throw (ex-info "Qualified local node evidence limit exceeded." + {:type :eacl.operator/invalid-lookup :eacl/error :eacl.operator/invalid-lookup + :reason :node-evidence-limit}))) + (let [result + (evidence/throw-if-fault! + (route/derives-from-node? + (cond-> (assoc route-options :start-node node :subject-type subject-type + :subject-eid subject-eid :resource-eid resource-eid) + evidence-witness + (assoc :known-witness (assoc evidence-witness :point point :scope (force scope))))))] + (vswap! memo assoc point result) + result)))))))) + (defn- make-env [{:keys [plan subject-type subject-eid desc? traversal - candidate-accept?] + candidate-accept? qualification] :as options} ctx] - {:plan plan - :ctx ctx - :subject-type subject-type - :subject-eid subject-eid - :desc? (boolean desc?) - :traversal traversal - :candidate-accept? candidate-accept? - ;; Probe options are invariant per page: plan, subject type, budgets and - ;; the cut point are selected once here, never re-attached per witness. - :route-opts (select-keys options reducer/run-option-keys)}) + (let [legacy-qualified? (and qualification (not (fn? candidate-accept?)) + (not (:operator-synthetic->semantic plan)))] + (when (and qualification (not legacy-qualified?) (not (fn? candidate-accept?))) + (throw (ex-info "Qualified operator cover requires supported exact local predicates." + {:eacl/error :eacl.operator/invalid-lookup + :type :eacl.operator/invalid-lookup + :reason :unsupported-qualified-cover}))) + {:plan plan + :ctx ctx + :subject-type subject-type + :subject-eid subject-eid + :desc? (boolean desc?) + :traversal traversal + :candidate-accept? (if legacy-qualified? (legacy-node-acceptor options) candidate-accept?) + :qualification qualification + ;; Internal positive covers preserve possible candidates. Public callers + ;; supply their policy explicitly; apply it only to complete root results. + :result-policy (if (contains? options :result-policy) + (authorization-result/result-policy options) + :detailed) + :legacy-qualified? legacy-qualified? + ;; Probe options are invariant per page: plan, subject type, budgets and + ;; the cut point are selected once here, never re-attached per witness. + :route-opts (select-keys options reducer/run-option-keys)})) (defn- run-page [env level next-fn page-size raw-candidates?] @@ -1093,7 +1279,12 @@ (if (nil? emission) {:emissions emissions :has-more? false :exhausted? true} (do (vswap! (:counters (:ctx env)) update :emissions inc) - (recur level' (conj emissions emission)))))))) + (recur level' + (if (and (:qualification env) + (= :definite (:result-policy env)) + (not (evidence/has? (get emission :evidence true)))) + emissions + (conj emissions emission))))))))) (defn forward-page "One least-path page of root entities for the subject. diff --git a/modules/eacl/src/eacl/engine/scan_cache.cljc b/modules/eacl/src/eacl/engine/scan_cache.cljc index a345fda0..30a25f03 100644 --- a/modules/eacl/src/eacl/engine/scan_cache.cljc +++ b/modules/eacl/src/eacl/engine/scan_cache.cljc @@ -17,6 +17,7 @@ (nil scope bypasses it) and switched off by `:cache? false` or a disabled client cache." (:require [eacl.cache.standard-lru :as lru] + [eacl.relationships.edge :as edge] [eacl.request.counters :as request-counters])) (def default-memo-bound @@ -45,22 +46,27 @@ "The reuse identity of one read descriptor, or nil for an unknown operation. Bound and limit are not part of it: one prefix serves every (bound, limit) it can reproduce." - [{:keys [operation relation-eid direction] :as descriptor}] - (case operation - :subject->resources - [:subject->resources (:subject-type descriptor) (:subject-eid descriptor) - relation-eid (:resource-type descriptor) (or direction :asc)] - :resource->subjects - [:resource->subjects (:resource-type descriptor) (:resource-eid descriptor) - relation-eid (:subject-type descriptor) (or direction :asc)] - nil)) + [{:keys [operation relation-eid direction include-qualifier?] :as descriptor}] + (let [key (case operation + :subject->resources + [:subject->resources (:subject-type descriptor) (:subject-eid descriptor) + relation-eid (:resource-type descriptor) (or direction :asc)] + :resource->subjects + [:resource->subjects (:resource-type descriptor) (:resource-eid descriptor) + relation-eid (:subject-type descriptor) (or direction :asc)] + nil)] + ;; Retain the ordinary key and its allocation shape. A compact scan is + ;; a different physical response even when every stored qualifier is nil. + (if (and key (true? include-qualifier?)) + (conj key :compact-edge edge/format-version) + key))) (defn- beyond? "True when `value` lies strictly beyond `bound` in the scan direction." [direction value bound] (if (= :desc direction) - (neg? (compare value bound)) - (pos? (compare value bound)))) + (neg? (compare (edge/endpoint value) bound)) + (pos? (compare (edge/endpoint value) bound)))) (defn- first-beyond "Index of the first prefix value strictly beyond `bound`; the prefix count @@ -110,7 +116,7 @@ start (first-beyond prefix bound direction) contiguous? (or (< start (count prefix)) (and (pos? (count prefix)) - (zero? (compare bound (peek prefix)))))] + (zero? (compare bound (edge/endpoint (peek prefix))))))] (when contiguous? (if (>= (- (count prefix) start) (count values)) entry diff --git a/modules/eacl/src/eacl/engine/stable_page.cljc b/modules/eacl/src/eacl/engine/stable_page.cljc index 7dc1c11d..3d7854f1 100644 --- a/modules/eacl/src/eacl/engine/stable_page.cljc +++ b/modules/eacl/src/eacl/engine/stable_page.cljc @@ -20,6 +20,10 @@ [eacl.cache.standard-lru :as lru] [eacl.engine.physical :as physical] [eacl.engine.stable-reducer :as reducer] + [eacl.engine.stable-route :as route] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.temporal :as temporal] + [eacl.authorization.evidence :as evidence] [eacl.execution :as execution] [eacl.secure-format :as secure-format])) @@ -50,16 +54,20 @@ Its `:basis` is intentionally exact. Public EACL Relay cursors validate lineage and frame before calling `edge-page`; they do not use this token." [{:keys [adapter basis-identity plan direction anchor subject-type - page-size]}] - {:v token-version - :order-abi order-abi - :fingerprint (:fingerprint plan) - :lifecycle (:source-lifecycle basis-identity) - :basis (backend/invoke adapter :native-revision) - :direction direction - :anchor anchor - :subject-type subject-type - :page-size page-size}) + page-size qualification] :as options}] + (cond-> {:v token-version + :order-abi order-abi + :fingerprint (:fingerprint plan) + :lifecycle (:source-lifecycle basis-identity) + :basis (backend/invoke adapter :native-revision) + :direction direction + :anchor anchor + :subject-type subject-type + :page-size page-size} + qualification + (assoc :qualification (secure-format/canonical-digest token-domain + (qualification/exact-reuse-identity qualification)) + :result-policy (:result-policy options :definite)))) (defn ^:no-doc checkpoint-key "Exact checkpoint identity for the standalone token API. @@ -124,7 +132,7 @@ (page-error! :eacl.page/expired-cursor "Cursor expired." {:expires-at expires-at}))) (doseq [field [:v :order-abi :fingerprint :lifecycle :direction - :anchor :subject-type :page-size]] + :anchor :subject-type :page-size :qualification :result-policy]] (when (not= (get binding field) (get payload field)) (page-error! :eacl.page/invalid-cursor "Cursor is bound to an incompatible execution context." @@ -314,17 +322,51 @@ (or checkpoint-key ::anonymous-replay) #(guard-exhaustion thunk))) +(defn- valid-qualified-checkpoint + "Malformed retained evidence is a replay miss. Every pending result has + an explicit value, including timeless true; missing annotations cannot + silently turn a conditional lookahead into a grant." + [options checkpoint] + (when checkpoint + (if-not (:qualification options) + (when (reducer/checkpoint-scope-valid? options (:state checkpoint)) checkpoint) + (let [annotations (:pending-evidence checkpoint) + prior-scope (get-in checkpoint [:state :qualified :scope]) + scope (qualification/exact-reuse-identity (:qualification options))] + (when (and (vector? prior-scope) (= 5 (count prior-scope)) + (= scope (assoc prior-scope 2 (:time (:qualification options)))) + (= (:result-policy options :definite) + (get-in checkpoint [:state :qualified :result-policy])) + (temporal/interval-valid? (:qualification-certificate checkpoint)) + (temporal/reusable? (:qualification-certificate checkpoint) (nth prior-scope 2) true) + (temporal/reusable? (:qualification-certificate checkpoint) (:time (:qualification options)) true) + (map? annotations) + (= (set (:pending checkpoint)) (set (keys annotations))) + (every? (fn [value] + (try + (and (not (evidence/no? value)) (not (evidence/fault? value)) + (evidence/before? (:time (:qualification options)) (evidence/valid-until value)) + (string? (evidence/encode value))) + (catch #?(:clj Exception :cljs :default) _ false))) + (vals annotations))) + ;; Only this interval-checked boundary may rebind retained reducer + ;; state to a later request. Direct reducer resume remains exact. + (assoc-in checkpoint [:state :qualified :scope] scope)))))) + (defn- state-at-boundary "Reconstructs semantic state and pending lookahead at boundary `ordinal`: by checkpoint when the exact edge matches, else by governed deterministic replay that validates the boundary identity at the already accepted basis before continuing. Public cursor acceptance always precedes this lookup." [options store key anchor-eid ordinal boundary-eid] - (if-let [hit (checkpoint-hit store key ordinal boundary-eid)] + (if-let [hit (valid-qualified-checkpoint options (checkpoint-hit store key ordinal boundary-eid))] (do + (when-let [request (:qualification options)] + (qualification/observe-interval! request (:qualification-certificate hit))) (when-let [stats reducer/*observer-stats*] (swap! stats update :continuation-hits (fnil inc 0))) - {:state (:state hit) :pending (:pending hit)}) + (cond-> {:state (:state hit) :pending (:pending hit)} + (:qualification options) (assoc :pending-evidence (:pending-evidence hit)))) (let [replayed (governed-replay options #(run-fresh (assoc options @@ -341,6 +383,26 @@ (declare deliver-page deliver-raw-page) +(defn- with-result-evidence [options result evidence] + (cond-> result + (:qualification options) + (assoc :result-evidence + (into {} (keep (fn [eid] + (let [value (get evidence eid true)] + (when-not (true? value) [eid value])))) (:eids result))))) + +(defn- qualified-page-options [options] + (if-let [request (:qualification options)] + (let [direction (:direction options) + options (assoc options (if (= :forward direction) :subject-eid :resource-eid) (:anchor-eid options)) + options (if (:candidate-evidence-fn options) options (route/discovery-options options direction))] + (cond-> options + (:checkpoint-key options) + (update :checkpoint-key #(vector % :qualification-v2 + (assoc (qualification/exact-reuse-identity request) 2 nil) + (:result-policy options :definite))))) + options)) + (defn edge-page "Engine-facing pagination over internal-eid boundaries: `after`/`before` are {:ordinal n :eid e} edges (already authenticated by the caller's @@ -350,90 +412,98 @@ window of the exhausted sequence. When `:service-admission` names a service-edge admission, replays (checkpoint misses, backward runs and last windows) run under its replay ledger keyed by `:checkpoint-key`." - [{:keys [anchor-eid page-size after before last-window? checkpoints - checkpoint-key raw-candidates?] - :as options}] - (cond - (nil? anchor-eid) - {:eids [] :start-ordinal 0 :has-next? false :has-previous? false} - - before - (let [{:keys [ordinal eid]} before - start (max 0 (- ordinal 1 page-size)) - replayed (governed-replay - options - #(run-fresh (assoc options - :result-sink :window - :result-window-size (inc page-size)) - anchor-eid ordinal)) - results (:results replayed)] - (when (or (< (:discovered replayed) ordinal) - (not= eid (peek results))) - (page-error! :eacl.page/invalid-cursor - "Backward run could not validate the supplied edge." - {:ordinal ordinal})) - {:eids (pop results) - :start-ordinal start - :has-next? true - :has-previous? (pos? start)}) - - last-window? - (let [run (governed-replay - options - #(run-fresh (assoc options - :result-sink :window - :result-window-size page-size) - anchor-eid - reducer/exhaustion-target)) - results (:results run)] - {:eids results - :start-ordinal (max 0 (- (:discovered run) (count results))) - :has-next? false - :has-previous? (> (:discovered run) (count results))}) - - :else - (let [ordinal (:ordinal after 0) - {:keys [state pending]} - (if (pos? ordinal) - (state-at-boundary options checkpoints checkpoint-key - anchor-eid ordinal (:eid after)) - {:state nil :pending []}) - {:keys [page-ids lookahead end-state]} - (if state - (if raw-candidates? - (deliver-raw-page options state pending page-size) - (deliver-page options state pending page-size)) - (let [run (guard-exhaustion - #(run-fresh - options anchor-eid - (if raw-candidates? page-size (inc page-size))))] - {:page-ids (let [results (:results run)] - (subvec results 0 (min page-size (count results)))) + [options] + (let [{:keys [anchor-eid page-size after before last-window? checkpoints + checkpoint-key raw-candidates?] :as options} (qualified-page-options options)] + (cond + (nil? anchor-eid) + (with-result-evidence options + {:eids [] :start-ordinal 0 :has-next? false :has-previous? false} {}) + + before + (let [{:keys [ordinal eid]} before + start (max 0 (- ordinal 1 page-size)) + replayed (governed-replay + options + #(run-fresh (assoc options + :result-sink :window + :result-window-size (inc page-size)) + anchor-eid ordinal)) + results (:results replayed)] + (when (or (< (:discovered replayed) ordinal) + (not= eid (peek results))) + (page-error! :eacl.page/invalid-cursor + "Backward run could not validate the supplied edge." + {:ordinal ordinal})) + (with-result-evidence options + {:eids (pop results) + :start-ordinal start + :has-next? true + :has-previous? (pos? start)} (:result-evidence replayed))) + + last-window? + (let [run (governed-replay + options + #(run-fresh (assoc options + :result-sink :window + :result-window-size page-size) + anchor-eid + reducer/exhaustion-target)) + results (:results run)] + (with-result-evidence options + {:eids results + :start-ordinal (max 0 (- (:discovered run) (count results))) + :has-next? false + :has-previous? (> (:discovered run) (count results))} (:result-evidence run))) + + :else + (let [ordinal (:ordinal after 0) + {:keys [state pending pending-evidence]} + (if (pos? ordinal) + (state-at-boundary options checkpoints checkpoint-key + anchor-eid ordinal (:eid after)) + {:state nil :pending []}) + {:keys [page-ids lookahead end-state result-evidence]} + (if state + (if raw-candidates? + (deliver-raw-page options state pending pending-evidence page-size) + (deliver-page options state pending pending-evidence page-size)) + (let [run (guard-exhaustion + #(run-fresh + options anchor-eid + (if raw-candidates? page-size (inc page-size))))] + {:page-ids (let [results (:results run)] + (subvec results 0 (min page-size (count results)))) ;; A fresh vector, never a suffix view retained by the ;; checkpoint store. - :lookahead (if (or raw-candidates? - (<= (count (:results run)) page-size)) - [] - (into [] (subvec (:results run) page-size))) - :end-state (reducer/history-free run)})) - delivered (+ ordinal (count page-ids))] - (when (and (seq page-ids) checkpoints checkpoint-key) - (checkpoint-put! - checkpoints checkpoint-key - {:ordinal delivered - :boundary (peek page-ids) - :pending (vec lookahead) - :state end-state})) - {:eids page-ids - :start-ordinal ordinal - :has-next? (boolean (seq lookahead)) - :has-previous? (pos? ordinal)}))) + :lookahead (if (or raw-candidates? + (<= (count (:results run)) page-size)) + [] + (into [] (subvec (:results run) page-size))) + :end-state (reducer/history-free run) + :result-evidence (:result-evidence run)})) + delivered (+ ordinal (count page-ids))] + (when (and (seq page-ids) checkpoints checkpoint-key) + (checkpoint-put! + checkpoints checkpoint-key + (cond-> {:ordinal delivered + :boundary (peek page-ids) + :pending (vec lookahead) + :state end-state} + (:qualification options) + (assoc :pending-evidence (into {} (map #(vector % (get result-evidence % true))) lookahead) + :qualification-certificate (qualification/certificate (:qualification options)))))) + (with-result-evidence options + {:eids page-ids + :start-ordinal ordinal + :has-next? (boolean (seq lookahead)) + :has-previous? (pos? ordinal)} result-evidence))))) (defn- deliver-raw-page "Continues a checkpoint by exactly `page-size` candidates, with no lookahead. Filtered lookup orchestration owns its sentinel and budget, so fetching an extra authorized candidate here would cross that boundary." - [options state pending page-size] + [options state pending pending-evidence page-size] (let [pending (vec (take page-size pending)) needed (- page-size (count pending)) continued @@ -442,13 +512,14 @@ #(run-resume options state (+ (:discovered state) needed))))] {:page-ids (into pending (when continued (:results continued))) :lookahead [] - :end-state (if continued (reducer/history-free continued) state)})) + :end-state (if continued (reducer/history-free continued) state) + :result-evidence (when (:qualification options) (merge pending-evidence (:result-evidence continued)))})) (defn- deliver-page "Runs from `state`+`pending` (whose scalar `:discovered` count is the absolute delivered ordinal) until `page-size` results plus one lookahead are available or the graph exhausts. Returns page internals." - [options state pending page-size] + [options state pending pending-evidence page-size] (let [needed-fresh (- (inc page-size) (count pending)) continued (when (pos? needed-fresh) (guard-exhaustion @@ -461,7 +532,8 @@ lookahead (if (< page-size n) [(nth available page-size)] [])] {:page-ids page-ids :lookahead lookahead - :end-state (if continued (reducer/history-free continued) state)})) + :end-state (if continued (reducer/history-free continued) state) + :result-evidence (when (:qualification options) (merge pending-evidence (:result-evidence continued)))})) (defn page "Executes one stable-discovery page with self-contained authenticated @@ -495,15 +567,21 @@ externals (mapv #(backend/invoke adapter :internal-id->object %) (:eids result)) start-ordinal (:start-ordinal result)] - {:data externals - :page-info - {:has-next-page? (boolean (and (seq externals) (:has-next? result))) - :has-previous-page? (boolean (and (seq externals) - (:has-previous? result))) - :start-cursor (when (seq externals) - (edge-token options binding (inc start-ordinal) - (first externals))) - :end-cursor (when (seq externals) - (edge-token options binding - (+ start-ordinal (count externals)) - (peek externals)))}})) + (cond-> {:data externals + :page-info + {:has-next-page? (boolean (and (seq externals) (:has-next? result))) + :has-previous-page? (boolean (and (seq externals) + (:has-previous? result))) + :start-cursor (when (seq externals) + (edge-token options binding (inc start-ordinal) + (first externals))) + :end-cursor (when (seq externals) + (edge-token options binding + (+ start-ordinal (count externals)) + (peek externals)))}} + (:qualification options) + (assoc :result-evidence (into {} + (keep (fn [[eid external]] + (when-let [value (get (:result-evidence result) eid)] + [external value]))) + (map vector (:eids result) externals)))))) diff --git a/modules/eacl/src/eacl/engine/stable_reducer.cljc b/modules/eacl/src/eacl/engine/stable_reducer.cljc index 733917df..8deac93e 100644 --- a/modules/eacl/src/eacl/engine/stable_reducer.cljc +++ b/modules/eacl/src/eacl/engine/stable_reducer.cljc @@ -26,6 +26,9 @@ scan at the canonical head (`fetch-values`), which is the preserved seam for any future concurrency change." (:require [eacl.backend.v8 :as backend] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.relationships.edge :as edge] [eacl.request.counters :as request-counters])) (def default-physical-chunk-size 64) @@ -42,6 +45,7 @@ (def default-max-transitions 4000000) (def default-max-values 4000000) (def default-max-stack 1000000) +(def default-max-evidence-size 16777216) (def ^:no-doc run-option-keys "Every reducer run option a traversal entry point forwards verbatim. @@ -49,15 +53,16 @@ page, count, probe, and least-path routes alike." [:adapter :fetch-fn :plan :subject-type :cut-point! :physical-chunk-size :sidecar-cap :result-sink :result-window-size - :max-admissions :max-commands :max-transitions :max-values :max-stack]) + :max-admissions :max-commands :max-transitions :max-values :max-stack + :qualification :candidate-evidence-fn :result-policy :max-evidence-size]) (defrecord ^:private ReducerState - [stack admitted admissions transitions commands fetched-values fetch-fn - sidecar sidecar-order sidecar-order-index sidecar-clock - current-sidecar-values sidecar-cap physical-chunk-size - max-admissions max-commands max-transitions max-values max-stack - maximum-sidecar-buffers maximum-sidecar-values maximum-stack discovered - base-discovered result-sink result-window-size result-index results]) + [stack admitted admissions transitions commands fetched-values fetch-fn + sidecar sidecar-order sidecar-order-index sidecar-clock + current-sidecar-values sidecar-cap physical-chunk-size + max-admissions max-commands max-transitions max-values max-stack + maximum-sidecar-buffers maximum-sidecar-values maximum-stack discovered + base-discovered result-sink result-window-size result-index results qualified]) (defn- replace-state "Constructs one immutable reducer-state revision. Keeping the mutable fields @@ -80,7 +85,8 @@ (.-max_transitions state) (.-max_values state) (.-max_stack state) maximum-sidecar-buffers maximum-sidecar-values maximum-stack discovered (.-base_discovered state) - (.-result_sink state) (.-result_window_size state) result-index results)) + (.-result_sink state) (.-result_window_size state) result-index results + (.-qualified state))) (defn- schedule-state [^ReducerState state stack admitted admissions maximum-stack] @@ -252,15 +258,99 @@ (schedule-state state stack admitted (inc (:admissions state)) (max (:maximum-stack state) (count stack))))) +(declare schedule-qualified) + +(defn- evidence-size + "Conservative retained-size units, including atom strings. Count shared + subtrees again so aliasing cannot understate the bound. Ordinary values + occupy no sparse evidence storage." + [value] + (if (true? value) + 0 + (loop [pending [(evidence/value value)] size 32] + (if (empty? pending) + size + (let [node (peek pending) pending (pop pending)] + (cond (string? node) (recur pending (+ size 16 (* 4 (count node)))) + (vector? node) (recur (into pending node) (+ size 24 (* 8 (count node)))) + :else (recur pending (+ size 8)))))))) + +(defn- weighted-schedule? + [state residual item] + (when-let [qualified (:qualified state)] + (or (seq (:weights qualified)) (seq (:pending qualified)) + (:revision residual) (:revision item) + (and item (not (true? (:evidence item true))))))) + (defn- schedule-item "Specialized zero/one-successor admission without a temporary collection." [state residual item] - (if item - (let [id (work-id item)] - (if (contains? (:admitted state) id) - (commit-zero state residual) - (commit-one state residual item id))) - (commit-zero state residual))) + (if (weighted-schedule? state residual item) + (schedule-qualified state residual (when item [item])) + (if item + (let [id (work-id item)] + (if (contains? (:admitted state) id) + (commit-zero state residual) + (commit-one state residual item id))) + (commit-zero state residual)))) + +(defn- queued-identity [item] + ;; A queued slot must not retain an obsolete prefix after coalescing. The + ;; pending table owns the sole latest work item and its evidence. + {:admission-id (work-id item)}) + +(defn- schedule-qualified + "Stages monotone evidence updates before touching the admitted transient. + A queued identity keeps its position; only its latest prefix is executed. + Timeless true admissions need no retained evidence entry." + [state residual items] + (let [qualified (:qualified state) + pending (cond-> (:pending qualified) + (:revision residual) (assoc (work-id residual) residual)) + staged + (reduce + (fn [{:keys [weights pending new-ids queued changes weight-size] :as stage} item] + (if (nil? item) + stage + (let [incoming (evidence/throw-if-fault! (:evidence item true)) + id (work-id item) + seen? (or (contains? (:admitted state) id) (contains? new-ids id)) + previous (if seen? (get weights id true) false) + joined (evidence/combine :union previous incoming)] + (if (or (evidence/no? incoming) (= previous joined)) + stage + (let [revision (+ (:admissions state) changes 1) + item (cond-> (assoc item :revision revision) + (or (evidence/has? joined) (not= joined incoming)) (dissoc :direct-evidence) + (contains? item :bound-eid) (assoc :bound-eid nil) + (true? joined) (dissoc :evidence) + (not (true? joined)) (assoc :evidence joined))] + {:weights (if (true? joined) (dissoc weights id) (assoc weights id joined)) + :pending (assoc pending id item) + :new-ids (if seen? new-ids (conj new-ids id)) + :queued (if (contains? pending id) queued (conj queued id)) + :changes (inc changes) + :weight-size (+ weight-size (evidence-size joined) + (- (if (contains? weights id) (evidence-size previous) 0)))}))))) + {:weights (:weights qualified) :weight-size (:weight-size qualified) + :pending pending :new-ids #{} :queued [] :changes 0} + items) + depth (+ (count (:stack state)) (if residual 1 0) (count (:queued staged)))] + (when (> (+ (:admissions state) (:changes staged)) (:max-admissions state)) + (limit-failure! :max-admissions state {:staged (:changes staged)})) + (when (> depth (:max-stack state)) + (limit-failure! :max-stack state {:staged (count (:queued staged))})) + (when (> (+ (:weight-size staged) (:result-size qualified)) (:max-evidence-size qualified)) + (limit-failure! :max-evidence-size state {:staged (:weight-size staged)})) + (let [stack (cond-> (:stack state) + residual (conj (if (:revision residual) (queued-identity residual) residual))) + stack (into stack (map (fn [id] {:admission-id id})) (rseq (:queued staged))) + admitted (reduce conj! (:admitted state) (:new-ids staged))] + (assoc (schedule-state state stack admitted + (+ (:admissions state) (:changes staged)) + (max (:maximum-stack state) depth)) + :qualified (assoc qualified :weights (:weights staged) :pending (:pending staged) + :weight-size (:weight-size staged)))))) (defn ^:no-doc schedule "Admits fresh work exactly once and pushes it after the residual: the @@ -273,39 +363,42 @@ [{:keys [admitted] :as state} residual new-work] ;; Zero and one successor dominate live traces. They do not allocate the ;; batch-local transient set/vector used by the general fan-out oracle. - (let [items (seq new-work)] - (cond - (nil? items) - (commit-zero state residual) - - (nil? (next items)) - (schedule-item state residual (first items)) + (if (or (weighted-schedule? state residual nil) + (and (:qualified state) (some #(not (true? (:evidence % true))) new-work))) + (schedule-qualified state residual new-work) + (let [items (seq new-work)] + (cond + (nil? items) + (commit-zero state residual) - :else - (let [fresh - (loop [items items - seen (transient #{}) - fresh (transient [])] - (if items - (let [item (first items)] - (if (nil? item) - (recur (next items) seen fresh) - (let [id (work-id item)] - (if (or (contains? admitted id) - (contains? seen id)) - (recur (next items) seen fresh) - (recur (next items) (conj! seen id) - (conj! fresh [item id])))))) - (persistent! fresh))) - _ (check-schedule-limits! state residual (count fresh)) - stack (cond-> (:stack state) - residual (conj residual)) - stack (into stack (map first) (rseq fresh)) - admitted (reduce (fn [acc [_ id]] (conj! acc id)) - admitted fresh)] - (schedule-state state stack admitted - (+ (:admissions state) (count fresh)) - (max (:maximum-stack state) (count stack))))))) + (nil? (next items)) + (schedule-item state residual (first items)) + + :else + (let [fresh + (loop [items items + seen (transient #{}) + fresh (transient [])] + (if items + (let [item (first items)] + (if (nil? item) + (recur (next items) seen fresh) + (let [id (work-id item)] + (if (or (contains? admitted id) + (contains? seen id)) + (recur (next items) seen fresh) + (recur (next items) (conj! seen id) + (conj! fresh [item id])))))) + (persistent! fresh))) + _ (check-schedule-limits! state residual (count fresh)) + stack (cond-> (:stack state) + residual (conj residual)) + stack (into stack (map first) (rseq fresh)) + admitted (reduce (fn [acc [_ id]] (conj! acc id)) + admitted fresh)] + (schedule-state state stack admitted + (+ (:admissions state) (count fresh)) + (max (:maximum-stack state) (count stack)))))))) ;; --------------------------------------------------------------------------- ;; One-value scan release with bounded disposable buffers @@ -463,6 +556,10 @@ (declare item-scan-descriptor) +(defn- buffer-id [item] + (let [id (work-id item)] + (if-let [revision (:revision item)] [id revision] id))) + (defn- release-one "Releases exactly one ordered scan value for `item`, preferring the retained buffer and refetching from the authoritative bound otherwise. @@ -473,7 +570,7 @@ ;; Sparse/empty endpoints never retain a buffer. Avoid constructing ;; and hashing a sidecar identity until either a buffer exists or this ;; fetch actually needs retention. - identity (when (pos? (count sidecar)) (work-id item)) + identity (when (pos? (count sidecar)) (buffer-id item)) entry (when identity (get sidecar identity)) index (:index entry 0)] (if (and entry (< index (count (:values entry)))) @@ -482,8 +579,9 @@ more? (:more-physical? entry) state (advance-buffer state identity entry next-index)] [state value (when (or (< next-index (count (:values entry))) more?) - (assoc item :bound-eid value))]) - (let [descriptor (or descriptor (item-scan-descriptor item)) + (assoc item :bound-eid (edge/endpoint value)))]) + (let [descriptor (cond-> (or descriptor (item-scan-descriptor item)) + (:qualified state) (assoc :include-qualifier? true)) [state values more?] (fetch-values state descriptor (:bound-eid item))] (if (zero? (count values)) @@ -493,11 +591,11 @@ retain? (and (pos? (:sidecar-cap state)) (or (< next-index (count values)) more?)) identity (if (and retain? (nil? identity)) - (work-id item) + (buffer-id item) identity) state (retain-buffer state identity values next-index more?)] [state value (when (or (< next-index (count values)) more?) - (assoc item :bound-eid value))])))))) + (assoc item :bound-eid (edge/endpoint value)))])))))) ;; --------------------------------------------------------------------------- ;; Transitions @@ -574,6 +672,8 @@ resource->subjects (backend/scan-invoker adapter :resource->subjects)] (fn [{:keys [operation bound-eid] :as descriptor}] (let [options (cond-> {:direction :asc} + (true? (:include-qualifier? descriptor)) + (assoc :include-qualifier? true) (:limit descriptor) (assoc :limit (:limit descriptor)) bound-eid (assoc :bound-eid bound-eid @@ -628,9 +728,26 @@ (schedule-item state residual nil) ;; `value->successors` remains as a mutation-test seam. Production scan ;; kinds have exactly one successor and use the allocation-free path. - (if value->successors - (schedule state residual (value->successors value)) - (schedule-item state residual (scan-successor item value)))))) + (if-let [qualified (:qualified state)] + (let [relation (when (edge/qualifier-id value) + (:relation-eid (or descriptor (item-scan-descriptor item)))) + value-evidence (evidence/throw-if-fault! + (qualification/qualify (:request qualified) relation value)) + joined (evidence/combine :arrow (:evidence item true) value-evidence) + successor (when-not (evidence/no? joined) + (cond-> (scan-successor item (edge/endpoint value)) + (not (true? joined)) (assoc :evidence joined) + (and (not (evidence/has? joined)) + (true? (:evidence item true)) + (contains? #{:seed-relation :reverse-direct} (:kind item))) + (assoc :direct-evidence + {:rule (:rule item) :evidence value-evidence + :resource-eid (if (= :seed-relation (:kind item)) + (edge/endpoint value) (:resource-eid item))})))] + (schedule-item state residual successor)) + (if value->successors + (schedule state residual (value->successors value)) + (schedule-item state residual (scan-successor item value))))))) (defn- emit [state eid] @@ -666,6 +783,57 @@ :arrow-permission {:kind :consumer :rule consumer :resource-eid eid :bound-eid nil})) +(defn- inherit-evidence [item successors] + (let [value (:evidence item true)] + (if (true? value) successors + (map #(assoc % :evidence value) successors)))) + +(defn- stage-result-evidence + "Bounds the emitted evidence pool before the collecting transient changes. + Window compaction discards annotations with the same backing-vector cut." + [state qualified eid value] + (let [retain? (and (not (true? value)) (not= :count (:result-sink state))) + entries (:result-evidence qualified) + entries (cond-> entries retain? (assoc eid value)) + size (+ (:result-size qualified) (if retain? (evidence-size value) 0)) + limit (:result-window-size state) + compact? (and (= :window (:result-sink state)) + (>= (inc (:result-index state)) limit) + (> (- (inc (count (:results state))) (:result-index state)) limit)) + entries (if compact? + (select-keys entries (conj (subvec (:results state) limit) eid)) entries) + size (if compact? (reduce + 0 (map evidence-size (vals entries))) size)] + (when (> (+ size (:weight-size qualified)) (:max-evidence-size qualified)) + (limit-failure! :max-evidence-size state {:staged size})) + (assoc qualified :result-evidence entries :result-size size))) + +(defn- emit-qualified + [state item eid] + (if (and (true? (:evidence item true)) (nil? (:revision item)) + (or (not= :window (:result-sink state)) + (empty? (get-in state [:qualified :result-evidence])))) + (emit state eid) + (if-let [qualified (:qualified state)] + (if (contains? (:processed qualified) eid) + state + (let [incoming (:evidence item true) + value (if (evidence/has? incoming) + incoming + (evidence/throw-if-fault! ((:candidate-evidence-fn qualified) eid item))) + conditional? (= :conditional-permission (evidence/permissionship value)) + include? (or (evidence/has? value) + (and (= :detailed (:result-policy qualified)) conditional?)) + ;; Only revisitable roots need a delivered/filtered identity. + qualified (cond-> qualified + (:revision item) (update :processed conj eid) + (and include? conditional?) + (-> (update :conditional-count inc) + (assoc :last-conditional-at (inc (:discovered state))))) + qualified (if include? (stage-result-evidence state qualified eid value) qualified) + state (if include? (emit state eid) state)] + (assoc state :qualified qualified))) + (emit state eid)))) + (defn- grant-successors "Consumers of a grant at `node` for entity `eid`: self-permission consumers become grants at the head node; arrow-permission consumers @@ -732,8 +900,8 @@ (let [node (:node rule) eid (:resource-eid item) state (cond-> state - (= node root) (emit eid))] - (schedule state nil (grant-successors plan node eid))) + (= node root) (emit-qualified item eid))] + (schedule state nil (inherit-evidence item (grant-successors plan node eid)))) :consumer (scan-transition state item nil nil) @@ -741,8 +909,9 @@ ;; ---- reverse ---- :reverse-goal (schedule state nil - (reverse-goal-work plan subject-type (:node rule) - (:resource-eid item))) + (inherit-evidence item + (reverse-goal-work plan subject-type (:node rule) + (:resource-eid item)))) :reverse-direct (scan-transition state item nil nil) @@ -757,7 +926,7 @@ (scan-transition state item nil nil) :reverse-subject - (emit state (:subject-eid item))))) + (emit-qualified state item (:subject-eid item))))) ;; --------------------------------------------------------------------------- ;; Entry points @@ -769,6 +938,7 @@ sinks do not construct a full result history. Limits are checked before their transition commits." [{:keys [adapter fetch-fn physical-chunk-size sidecar-cap result-sink + qualification candidate-evidence-fn result-policy max-evidence-size result-window-size max-admissions max-commands max-transitions max-values max-stack] @@ -779,7 +949,15 @@ max-transitions default-max-transitions max-values default-max-values max-stack default-max-stack - result-sink :collect}}] + result-sink :collect max-evidence-size default-max-evidence-size} + :as options}] + (when (and qualification (not (fn? candidate-evidence-fn))) + (throw (ex-info "Qualified discovery requires complete candidate evidence." + {:type :eacl.reducer/missing-candidate-evaluator}))) + (when (and (contains? options :result-policy) + (not (contains? #{:definite :detailed} result-policy))) + (throw (ex-info "Invalid discovery result policy." + {:type :eacl.reducer/invalid-result-policy}))) (map->ReducerState {:stack [] :admitted (transient #{}) @@ -808,6 +986,13 @@ :result-sink result-sink :result-window-size result-window-size :result-index 0 + :qualified (when qualification + {:request qualification :candidate-evidence-fn candidate-evidence-fn + :scope (delay (qualification/exact-reuse-identity qualification)) + :result-policy (or result-policy :definite) + :weights {} :weight-size 0 :max-evidence-size max-evidence-size + :pending {} :processed #{} :conditional-count 0 + :result-evidence {} :result-size 0}) :results (case result-sink :collect (transient []) :window [] @@ -830,8 +1015,11 @@ {:max-transitions (:max-transitions state)})) (when cut-point! (cut-point! state)) (let [item (peek (:stack state)) + id (:admission-id item) + item (if id (get-in state [:qualified :pending id] item) item) state (transition-state state (pop (:stack state)) - (inc (:transitions state)))] + (inc (:transitions state))) + state (if id (update-in state [:qualified :pending] dissoc id) state)] (recur (step context state item))))))) (defn ^:no-doc finish @@ -861,6 +1049,11 @@ :completed (- (count admitted) (count (:stack state)))) (dissoc :fetch-fn)) + (:qualified state) + (assoc :result-evidence (select-keys (get-in state [:qualified :result-evidence]) results) + :definite-count (- (:discovered state) (get-in state [:qualified :conditional-count])) + :conditional-count (get-in state [:qualified :conditional-count]) + :last-conditional? (= (:discovered state) (get-in state [:qualified :last-conditional-at]))) (zero? (:base-discovered state)) (dissoc :base-discovered)))) ;; --------------------------------------------------------------------------- @@ -879,7 +1072,12 @@ identities, stack, deterministic counters, and the scalar discovered count — no delivered results, no buffers, no configuration." [finished-state] - (select-keys finished-state semantic-state-keys)) + (cond-> (select-keys finished-state semantic-state-keys) + (:qualified finished-state) + (assoc :qualified (-> (:qualified finished-state) + (select-keys [:weights :weight-size :pending :processed :conditional-count + :last-conditional-at :result-policy]) + (assoc :scope @(get-in finished-state [:qualified :scope])))))) (def ^:dynamic *observer-stats* "When bound to an atom, each completed run bulk-reports its work deltas @@ -933,6 +1131,17 @@ (:fetched-values before 0))}) final-state) +(defn checkpoint-scope-valid? + "A qualified checkpoint is reusable only in the same complete request + scope and result policy. Ordinary checkpoints carry no qualified state." + [options checkpoint-state] + (let [qualified (:qualified checkpoint-state)] + (and (= (boolean qualified) (boolean (:qualification options))) + (or (nil? qualified) + (and (= (:scope qualified) + (qualification/exact-reuse-identity (:qualification options))) + (= (:result-policy qualified) (:result-policy options :definite))))))) + (defn resume "Continues a history-free state to `target` absolute discovered results under fresh runtime options. Emissions from before the checkpoint are @@ -940,10 +1149,16 @@ [{:keys [plan subject-type target cut-point!] :as options} checkpoint-state] (let [context {:plan plan :root (:root plan) :subject-type subject-type} + qualified (:qualified checkpoint-state) + _ (when-not (checkpoint-scope-valid? options checkpoint-state) + (throw (ex-info "Qualified discovery checkpoint scope changed." + {:type :eacl.reducer/checkpoint-scope-mismatch}))) state (-> (initial-state options) (merge (select-keys checkpoint-state semantic-state-keys)) (assoc :admitted (transient (:admitted checkpoint-state)) - :base-discovered (:discovered checkpoint-state))) + :base-discovered (:discovered checkpoint-state)) + (cond-> qualified + (update :qualified merge (dissoc qualified :scope)))) before (select-keys state [:admissions :commands :transitions :fetched-values])] diff --git a/modules/eacl/src/eacl/engine/stable_route.cljc b/modules/eacl/src/eacl/engine/stable_route.cljc index 2886675b..0f6b166b 100644 --- a/modules/eacl/src/eacl/engine/stable_route.cljc +++ b/modules/eacl/src/eacl/engine/stable_route.cljc @@ -23,8 +23,12 @@ stack or a typed `:max-admissions`/`:max-values` failure, never at a silent cap. An order-insensitive specialization remains permitted only behind an independent denotation-equivalence proof (none exists yet)." - (:require [eacl.backend.v8 :as backend] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.execution :as execution] + [eacl.backend.v8 :as backend] [eacl.engine.stable-reducer :as reducer] + [eacl.relationships.edge :as edge] [eacl.request.counters :as request-counters])) (def exhaustion-target @@ -67,8 +71,31 @@ :relation-eid relation-eid :resource-type resource-type :bound-eid bound-eid :limit limit}) +(defn- validate-known-witness! + [{:keys [plan qualification known-witness subject-type subject-eid resource-eid start-node]}] + (when (some? known-witness) + (execution/check! :stable-route/witness) + (let [node (or start-node (:root plan)) + rule (:rule known-witness) + arrow? (contains? #{:arrow-permission :arrow-relation} (:rule rule)) + required-keys (cond-> #{:point :rule :evidence :scope} arrow? (conj :intermediate)) + valid? (and qualification (map? known-witness) + (= required-keys (set (keys known-witness))) + (= [node subject-type subject-eid resource-eid] (:point known-witness)) + (= (qualification/exact-reuse-identity qualification) (:scope known-witness)) + (some #(= rule %) (get-in plan [:indexes :reverse-rules node])) + (or (not arrow?) (and (integer? (:intermediate known-witness)) + (edge/valid? (:intermediate known-witness)) + (pos? (:intermediate known-witness)))) + (evidence/before? (:time qualification) (evidence/valid-until (:evidence known-witness))))] + (when-not valid? + (throw (ex-info "Known path evidence does not match the selected point." + {:type :eacl.route/invalid-witness :eacl/error :eacl.route/invalid-witness}))) + (evidence/encode (:evidence known-witness)) + known-witness))) + (defn- probe-check-eids - "Iterative depth-first membership search. Returns true iff a derivation + "Iterative depth-first membership search. Without qualification, returns true iff a derivation of the plan's root permission on `resource-eid` bottoms out in a tuple whose subject is `subject-eid`; equivalently, iff `subject-eid` belongs to the exhaustive reverse denotation `run-reverse` would emit. @@ -88,9 +115,16 @@ enumerate their intermediates and descend. Typed limits mirror the reducer's budgets: `:max-admissions` bounds distinct visited states, `:max-transitions` visits, `:max-commands` fetches, `:max-values` fetched - values, `:max-stack` instantaneous stack depth." + values, `:max-stack` instantaneous stack depth. + + Qualification annotates the same physical paths with bounded residuals + and temporal evidence. A visited state retains the union of its incoming + path prefixes, and only a changed full value/certificate revisits it. + Conditional matches continue the existing search; definite witnesses and + demanded faults terminate it. Either exhausted side of a bidirectional + arm covers every matching pair, including its qualified evidence." [{:keys [adapter fetch-fn plan subject-type subject-eid resource-eid - cut-point! physical-chunk-size + cut-point! physical-chunk-size qualification max-admissions max-commands max-transitions max-values max-stack] :or {physical-chunk-size reducer/default-physical-chunk-size max-admissions reducer/default-max-admissions @@ -99,8 +133,17 @@ max-values reducer/default-max-values max-stack reducer/default-max-stack} :as options}] - (let [fetch-fn (or fetch-fn (reducer/adapter-fetch-fn adapter)) + (let [known-witness (validate-known-witness! options) + start-node (or (:start-node options) (:root plan)) + known-rule? (fn [node eid rule] + (and known-witness (= start-node node) (= resource-eid eid) + (= (:rule known-witness) rule))) + fetch-fn (or fetch-fn (reducer/adapter-fetch-fn adapter)) reverse-rules (get-in plan [:indexes :reverse-rules]) + qualify (if qualification + (fn [relation value] (qualification/qualify qualification relation value)) + (fn [_ value] (some? value))) + done? (fn [value] (or (evidence/has? value) (evidence/fault? value))) counters (volatile! {:admissions 0 :transitions 0 :commands 0 :fetched-values 0}) fetch! (fn [descriptor] @@ -114,7 +157,9 @@ (limit-failure! :max-commands @counters {:max-commands max-commands})) (let [values (reducer/bounded-vector - (fetch-fn descriptor) (:limit descriptor))] + (fetch-fn (cond-> descriptor qualification + (assoc :include-qualifier? true))) + (:limit descriptor))] (when (> (+ (:fetched-values @counters) (count values)) max-values) (limit-failure! :max-values @counters @@ -125,11 +170,15 @@ (update :fetched-values + (count values)))) values)) + matching-edge (fn [candidate values] + (let [value (first values)] + (when (= candidate (edge/endpoint value)) value))) probe? (fn [resource-type eid relation-eid] - (= subject-eid - (first (fetch! (reverse-scan resource-type eid - relation-eid subject-type - (dec subject-eid) 1))))) + (qualify relation-eid + (matching-edge subject-eid + (fetch! (reverse-scan resource-type eid + relation-eid subject-type + (dec subject-eid) 1))))) intermediates (fn [resource-type eid via-relation-eid intermediate-type] (loop [bound nil acc (transient [])] (let [chunk (fetch! (reverse-scan resource-type eid @@ -140,26 +189,26 @@ acc (reduce conj! acc chunk)] (if (< (count chunk) physical-chunk-size) (persistent! acc) - (recur (peek chunk) acc))))) + (recur (edge/endpoint (peek chunk)) acc))))) ;; One exact-bound probe per side of a two-layer arrow arm ;; (BidirectionalArrowIntersection.dfy): a via candidate is decided ;; on the subject's forward index, a holding candidate on the ;; resource's reverse index. holding-probe? (fn [target-relation-eid intermediate-type candidate] - (= candidate - (first (fetch! (forward-scan - subject-type subject-eid - target-relation-eid - intermediate-type - (dec candidate) 1))))) + (matching-edge candidate + (fetch! (forward-scan + subject-type subject-eid + target-relation-eid + intermediate-type + (dec candidate) 1)))) via-probe? (fn [resource-type eid via-relation-eid intermediate-type candidate] - (= candidate - (first (fetch! (reverse-scan - resource-type eid - via-relation-eid - intermediate-type - (dec candidate) 1))))) + (matching-edge candidate + (fetch! (reverse-scan + resource-type eid + via-relation-eid + intermediate-type + (dec candidate) 1)))) ;; The interleaved bidirectional decision for one two-layer arm: ;; vias(resource) ∩ holdings(subject) ≠ ∅. Round order and both ;; exhaustion exits follow the verified model exactly @@ -168,10 +217,10 @@ ;; cost is bounded by the smaller side plus one chunk per side. intersect-arm? (fn [resource-type eid via-relation-eid intermediate-type - target-relation-eid] + target-relation-eid skip-intermediate] (loop [vias [] via-index 0 via-bound nil vias-done? false holdings [] holding-index 0 holding-bound nil - holdings-done? false] + holdings-done? false answer false] (let [[vias via-index via-bound vias-done?] (if (and (>= via-index (count vias)) (not vias-done?)) (let [chunk (fetch! (reverse-scan resource-type eid @@ -179,41 +228,60 @@ intermediate-type via-bound physical-chunk-size))] - [chunk 0 (if (seq chunk) (peek chunk) via-bound) + [chunk 0 (if (seq chunk) (edge/endpoint (peek chunk)) via-bound) (< (count chunk) physical-chunk-size)]) [vias via-index via-bound vias-done?])] (if (>= via-index (count vias)) - ;; The via side is exhausted with every candidate probed - ;; negative: the intersection is empty. - false - (if (holding-probe? target-relation-eid intermediate-type - (nth vias via-index)) - true - (let [[holdings holding-index holding-bound holdings-done?] - (if (and (>= holding-index (count holdings)) - (not holdings-done?)) - (let [chunk (fetch! (forward-scan - subject-type subject-eid - target-relation-eid - intermediate-type - holding-bound - physical-chunk-size))] - [chunk 0 - (if (seq chunk) (peek chunk) holding-bound) - (< (count chunk) physical-chunk-size)]) - [holdings holding-index holding-bound - holdings-done?])] - (if (>= holding-index (count holdings)) - ;; The holdings side is exhausted with every candidate - ;; probed negative: the intersection is empty. - false - (if (via-probe? resource-type eid via-relation-eid - intermediate-type - (nth holdings holding-index)) - true - (recur vias (inc via-index) via-bound vias-done? - holdings (inc holding-index) holding-bound - holdings-done?))))))))) + ;; Either exhausted physical side proves that every possible + ;; matching pair has contributed its evidence. + answer + (let [via-edge (nth vias via-index) + via (if (= skip-intermediate (edge/endpoint via-edge)) + false (qualify via-relation-eid via-edge)) + path (if (or (evidence/no? via) (evidence/fault? via)) + via + (evidence/combine + :arrow via + (qualify target-relation-eid + (holding-probe? target-relation-eid intermediate-type + (edge/endpoint via-edge))))) + answer (evidence/combine :union answer path)] + (cond + (done? answer) answer + :else + (let [[holdings holding-index holding-bound holdings-done?] + (if (and (>= holding-index (count holdings)) + (not holdings-done?)) + (let [chunk (fetch! (forward-scan + subject-type subject-eid + target-relation-eid + intermediate-type + holding-bound + physical-chunk-size))] + [chunk 0 + (if (seq chunk) (edge/endpoint (peek chunk)) holding-bound) + (< (count chunk) physical-chunk-size)]) + [holdings holding-index holding-bound + holdings-done?])] + (if (>= holding-index (count holdings)) + answer + (let [holding-edge (nth holdings holding-index) + via (if (= skip-intermediate (edge/endpoint holding-edge)) + false + (qualify via-relation-eid + (via-probe? resource-type eid via-relation-eid + intermediate-type (edge/endpoint holding-edge)))) + path (if (or (evidence/no? via) (evidence/fault? via)) + via + (evidence/combine :arrow via + (qualify target-relation-eid holding-edge))) + answer (evidence/combine :union answer path)] + (cond + (done? answer) answer + :else + (recur vias (inc via-index) via-bound vias-done? + holdings (inc holding-index) holding-bound + holdings-done? answer))))))))))) ;; A target permission every one of whose derivations is a base ;; relation reduces its arrow to a union of two-layer intersections; ;; any other shape keeps the enumerate-and-descend route. @@ -238,107 +306,154 @@ :advanced-datoms commands :queued-work transitions :fetched-values fetched-values}))))] - (loop [stack [[(or (:start-node options) (:root plan)) resource-eid]] - visited (transient #{})] - (if (zero? (count stack)) - (do (report!) false) - (let [[node eid :as state] (peek stack) + (loop [stack [[start-node resource-eid]] + visited (transient (if qualification {} #{})) + answer (if known-witness (:evidence known-witness) false)] + (if (or (done? answer) (zero? (count stack))) + (do (report!) answer) + (let [[node eid incoming :as frame] (peek stack) + state (if qualification [node eid] frame) + admitted? (contains? visited state) + prefix (if qualification + (evidence/combine :union (get visited state false) + (if (= 3 (count frame)) incoming true)) + true) + unchanged? (if qualification + (and admitted? (= prefix (get visited state))) + admitted?) stack (pop stack)] (when (>= (:transitions @counters) max-transitions) (limit-failure! :max-transitions @counters {:max-transitions max-transitions})) (vswap! counters update :transitions inc) (when cut-point! (cut-point! @counters)) - (if (contains? visited state) - (recur stack visited) - (let [_ (when (>= (:admissions @counters) max-admissions) + (if unchanged? + (recur stack visited answer) + (let [_ (when (and (not admitted?) (>= (:admissions @counters) max-admissions)) (limit-failure! :max-admissions @counters {:max-admissions max-admissions :staged 1})) - _ (vswap! counters update :admissions inc) - visited (conj! visited state) - rules (get reverse-rules node)] + _ (when-not admitted? (vswap! counters update :admissions inc)) + visited (if qualification (assoc! visited state prefix) (conj! visited state)) + rules (get reverse-rules node) + successor (if qualification + (fn [target next-eid via] [target next-eid via]) + (fn [target next-eid _] [target next-eid])) + join-path (fn [answer path] + (evidence/combine :union answer + (evidence/combine :arrow prefix path))) + base-answer + (reduce (fn [answer rule] + (let [answer (if (and (= :relation (:rule rule)) + (= subject-type (:subject-type rule)) + (not (known-rule? node eid rule))) + (join-path answer + (probe? (:resource-type rule) eid + (:relation-eid rule))) + answer)] + (if (done? answer) (reduced answer) answer))) + answer rules)] ;; Base tuples first: one exact-bound probe per direct rule. - (if (some (fn [rule] - (and (= :relation (:rule rule)) - (= subject-type (:subject-type rule)) - (probe? (:resource-type rule) eid - (:relation-eid rule)))) - rules) - (do (report!) true) + (if (done? base-answer) + (do (report!) base-answer) ;; Then the arrows: enumerate intermediates, probe or descend. - (let [outcome + (let [answer (volatile! base-answer) + outcome (reduce (fn [successors rule] - (case (:rule rule) - :self-permission - (conj successors [(:target-node rule) eid]) + (let [next-successors + (case (:rule rule) + :self-permission + (if (known-rule? node eid rule) + successors + (conj successors (successor (:target-node rule) eid prefix))) - :arrow-permission - (if-let [target-rules - (relation-only-rules (:target-node rule))] + :arrow-permission + (if-let [target-rules + (relation-only-rules (:target-node rule))] ;; Every derivation of the target permission is ;; a base relation: the arm is a union of ;; two-layer intersections, each decided ;; bidirectionally without materializing the ;; via fan-in. - (if (some (fn [target-rule] - (and (= subject-type - (:subject-type target-rule)) - (intersect-arm? - (:resource-type rule) eid - (:via-relation-eid rule) - (:intermediate-type rule) - (:relation-eid target-rule)))) - target-rules) - (reduced ::found) - successors) - (into successors - (map (fn [i] [(:target-node rule) i])) - (intermediates (:resource-type rule) eid - (:via-relation-eid rule) - (:intermediate-type rule)))) + (do + (reduce (fn [_ target-rule] + (when (= subject-type (:subject-type target-rule)) + (vswap! answer join-path + (intersect-arm? + (:resource-type rule) eid + (:via-relation-eid rule) + (:intermediate-type rule) + (:relation-eid target-rule) + (when (known-rule? node eid rule) + (:intermediate known-witness))))) + (when (done? @answer) (reduced nil))) + nil target-rules) + successors) + (reduce (fn [successors compact-edge] + (let [via (if (and (known-rule? node eid rule) + (= (:intermediate known-witness) (edge/endpoint compact-edge))) + false + (evidence/combine + :arrow prefix + (qualify (:via-relation-eid rule) compact-edge)))] + (if (or (evidence/no? via) (evidence/fault? via)) + (do (vswap! answer #(evidence/combine :union % via)) + (if (done? @answer) (reduced successors) successors)) + (conj successors + (successor (:target-node rule) + (edge/endpoint compact-edge) via))))) + successors + (intermediates (:resource-type rule) eid + (:via-relation-eid rule) + (:intermediate-type rule)))) - :arrow-relation - (if (and (= subject-type (:target-subject-type rule)) - (intersect-arm? - (:resource-type rule) eid - (:via-relation-eid rule) - (:intermediate-type rule) - (:target-relation-eid rule))) - (reduced ::found) - successors) + :arrow-relation + (do + (when (= subject-type (:target-subject-type rule)) + (vswap! answer join-path + (intersect-arm? + (:resource-type rule) eid + (:via-relation-eid rule) + (:intermediate-type rule) + (:target-relation-eid rule) + (when (known-rule? node eid rule) + (:intermediate known-witness))))) + successors) - :relation - successors + :relation + successors ;; Fail closed on an unrecognized rule kind, like ;; the reducer's reverse-goal-work: silently ;; skipping one would under-derive and answer ;; false where enumeration paths error. - (throw - (ex-info - "Point check met an unrecognized sealed rule kind." - {:eacl/error :eacl.plan/unknown-rule-kind - :rule-kind (:rule rule) - :node node})))) + (throw + (ex-info + "Point check met an unrecognized sealed rule kind." + {:eacl/error :eacl.plan/unknown-rule-kind + :rule-kind (:rule rule) + :node node})))] + (if (done? @answer) (reduced next-successors) next-successors))) [] rules)] - ;; Value comparison, not `identical?`: ClojureScript keyword - ;; literals are not interned objects. - (if (= ::found outcome) - (do (report!) true) + (if (done? @answer) + (do (report!) @answer) (let [stack (into stack (rseq outcome))] (when (> (count stack) max-stack) (limit-failure! :max-stack @counters {:max-stack max-stack :staged (count outcome)})) - (recur stack visited)))))))))))) + (recur stack visited @answer)))))))))))) (defn check-eids "Anchored point check over pre-resolved internal ids: does the subject hold the plan's root permission on the resource? Decided by the - membership-probe search (`probe-check-eids`); nil ids never hold." + membership-probe search (`probe-check-eids`); nil ids never hold. + With `:qualification`, returns conditional/temporal Evidence or a plain + timeless Boolean; callers must project through evidence/has?. A scoped + `:known-witness` contributes one rule or arrow binding; the same search + completes all remaining alternatives without repeating that path." [{:keys [subject-eid resource-eid] :as options}] (if (or (nil? subject-eid) (nil? resource-eid)) false @@ -399,14 +514,38 @@ :resource-eid (backend/invoke adapter :object-id->internal resource-id)))) +(defn discovery-options + "Completes conditional first-discovery candidates with the existing point + evaluator. Direct tuple witnesses skip only their exact rule; arbitrary + path prefixes never claim a whole child permission." + [options direction] + (if-let [request (:qualification options)] + (assoc options :candidate-evidence-fn + (fn [eid item] + (let [point (assoc options (if (= :reverse direction) :subject-eid :resource-eid) eid) + known (:direct-evidence item) + root (get-in options [:plan :root]) + point (cond-> point + (and known (= root (get-in known [:rule :node])) + (= (:resource-eid point) (:resource-eid known))) + (assoc :known-witness {:point [root (:subject-type point) + (:subject-eid point) (:resource-eid point)] + :rule (:rule known) :evidence (:evidence known) + :scope (qualification/exact-reuse-identity request)}))] + (check-eids point)))) + options)) + (defn- exhaustive-count "Exact count by exhausting the reducer through `run` from `anchor-eid`; :count-limit truncates with an explicit marker exactly like the public contract." [run anchor-key anchor-eid {:keys [count-limit] :as options}] - (let [target (if count-limit (inc count-limit) exhaustion-target)] + (let [target (if count-limit (inc count-limit) exhaustion-target) + options (discovery-options (assoc options anchor-key anchor-eid) + (if (= :subject-eid anchor-key) :forward :reverse))] (if (nil? anchor-eid) - {:count 0 :limit (or count-limit -1) :truncated? false} + (cond-> {:count 0 :limit (or count-limit -1) :truncated? false} + (:qualification options) (assoc :definite-count 0 :conditional-count 0)) (let [finished (run (merge (select-keys options reducer/run-option-keys) {anchor-key anchor-eid :result-sink :count @@ -414,9 +553,14 @@ discovered (:discovered finished) truncated? (boolean (and count-limit (> discovered count-limit)))] - {:count (if truncated? count-limit discovered) - :limit (or count-limit -1) - :truncated? truncated?})))) + (cond-> {:count (if truncated? count-limit discovered) + :limit (or count-limit -1) + :truncated? truncated?} + (:qualification options) + (assoc :definite-count (- (:definite-count finished) + (if (and truncated? (not (:last-conditional? finished))) 1 0)) + :conditional-count (- (:conditional-count finished) + (if (and truncated? (:last-conditional? finished)) 1 0)))))))) (defn count-resources "Exact count by exhausting the reducer; :count-limit truncates with an diff --git a/modules/eacl/src/eacl/engine/v8.cljc b/modules/eacl/src/eacl/engine/v8.cljc index 7450fd0b..0a49d2e8 100644 --- a/modules/eacl/src/eacl/engine/v8.cljc +++ b/modules/eacl/src/eacl/engine/v8.cljc @@ -1,5 +1,7 @@ (ns eacl.engine.v8 - (:require [eacl.backend.v8 :as backend] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.result :as authorization-result] + [eacl.backend.v8 :as backend] [eacl.cache.derived-schema :as derived-schema] [eacl.core :refer [spice-object]] [eacl.engine.least-path :as least-path] @@ -19,12 +21,36 @@ [eacl.operator.recursive :as operator-recursive] [eacl.operator.vector-evaluator :as operator-vector] [eacl.request.counters :as request-counters] + [eacl.relationships.edge :as relationship-edge] [eacl.schema.expression-persistence :as expression-persistence] [eacl.secure-format :as secure-format] [eacl.subproblem-cache :as subproblem] [eacl.verified-kernel :as verified])) -(def engine-version 8) +(def engine-version 9) + +(def ^:dynamic *qualification* + "The one selected-basis qualification request, bound by public orchestration." + nil) + +(def ^:dynamic ^:private *lookup-result-policy* :definite) + +(defn- with-emission-evidence [page emissions] + (if (and *qualification* (= :detailed *lookup-result-policy*)) + (assoc page :result-evidence + (into {} (keep (fn [emission] + (let [value (get emission :evidence true)] + (when-not (true? value) [(:value emission) value])))) + emissions)) + page)) + +(defn- project-lookup-page [page] + (let [values (:result-evidence page)] + (cond-> (dissoc page :result-evidence) + (= :detailed *lookup-result-policy*) + (update :data (fn [objects] + (mapv #(authorization-result/lookup-result % (get values (:id %) true)) + objects)))))) (def ^:dynamic *evaluation-mode* "Normalized public evaluation mode. Cache state never changes this value." @@ -257,9 +283,13 @@ [cursor-or-opts] (if (and (map? cursor-or-opts) (contains? cursor-or-opts :direction)) - {:direction (:direction cursor-or-opts) - :bound-eid (:bound-eid cursor-or-opts) - :inclusive-bound? (boolean (:inclusive-bound? cursor-or-opts))} + (cond-> {:direction (:direction cursor-or-opts) + :bound-eid (:bound-eid cursor-or-opts) + :inclusive-bound? (boolean (:inclusive-bound? cursor-or-opts))} + (true? (:include-qualifier? cursor-or-opts)) + (assoc :include-qualifier? true) + (:limit cursor-or-opts) + (assoc :limit (:limit cursor-or-opts))) {:direction :asc :bound-eid cursor-or-opts :inclusive-bound? false})) @@ -1498,7 +1528,7 @@ The encrypted progress edge still names the last examined candidate; the inclusive marker tells this layer to present that candidate once before it resumes the ordinary exclusive authorized stream." - [result-type fetch-exclusive bound limit] + [result-type fetch-exclusive bound limit inclusive-evidence-fn] (if (:resume-inclusive? bound) (let [exclusive-bound (dissoc bound :resume-inclusive?) boundary-eid (bound-result-eid exclusive-bound)] @@ -1508,15 +1538,18 @@ {:type :eacl.pagination/invalid-cursor :eacl/error :eacl.pagination/invalid-cursor :reason :malformed-inclusive-boundary})) - (into [{:node (spice-object result-type boundary-eid) - :cursor exclusive-bound}] + (into [(cond-> {:node (spice-object result-type boundary-eid) + :cursor exclusive-bound} + inclusive-evidence-fn + (assoc :evidence (inclusive-evidence-fn boundary-eid)))] (when (> limit 1) (fetch-exclusive exclusive-bound (dec limit))))) (fetch-exclusive bound limit))) (defn- execute-filtered-lookup-window [result-type {:keys [direction size bound]} - {:keys [candidate-window accept? accept-many? maximum-batch-width]} + {:keys [candidate-window accept? accept-many? accept-evidence maximum-batch-width + evidence-decisions? result-policy inclusive-evidence-fn]} fetch-exclusive] (when-not (and (integer? candidate-window) (pos? candidate-window)) (page-error! @@ -1525,7 +1558,7 @@ :eacl/error :eacl.execution/resource-limit-exceeded :limit-kind :candidate-window :value candidate-window})) - (when-not (or (fn? accept?) (fn? accept-many?)) + (when-not (or (fn? accept?) (fn? accept-many?) (fn? accept-evidence)) (page-error! "A filtered lookup window requires an accept predicate." {:type :eacl/invalid-config :eacl/error :eacl/invalid-config})) @@ -1539,7 +1572,7 @@ :limit-kind :maximum-batch-width :value maximum-batch-width})) (let [fetch-candidates - #(fetch-inclusive-candidates result-type fetch-exclusive %1 %2) + #(fetch-inclusive-candidates result-type fetch-exclusive %1 %2 inclusive-evidence-fn) result (loop [cursor bound examined 0 @@ -1581,23 +1614,39 @@ (if accept-many? (let [values (vec (accept-many? (mapv :node chunk)))] (when-not (and (= (count chunk) (count values)) - (every? boolean? values)) + (or evidence-decisions? + (every? boolean? values))) (page-error! "A filtered lookup batch returned malformed decisions." {:type :eacl/backend-contract-violation :eacl/error :eacl/backend-contract-violation :obligation :aligned-filter-decisions})) values) - (mapv #(boolean (accept? (:node %))) chunk)) + (if accept-evidence + (mapv #(accept-evidence (:node %)) chunk) + (mapv #(boolean (accept? (:node %))) chunk))) [accepted examined last-examined] (reduce - (fn [[accepted examined _] [candidate accepted?]] + (fn [[accepted examined _] [candidate decision]] (request-counters/add-candidates-examined!) - [(cond-> accepted - accepted? - (conj candidate)) - (inc examined) - candidate]) + (let [qualified? (or evidence-decisions? *qualification*) + decision (if (and (not evidence-decisions?) *qualification* + (contains? candidate :evidence)) + (evidence/combine :intersection (:evidence candidate) decision) + decision) + _ (when qualified? (evidence/throw-if-fault! decision)) + accepted? (if qualified? + (if (= :detailed (or result-policy *lookup-result-policy*)) + (not (evidence/no? decision)) + (evidence/has? decision)) + decision)] + [(cond-> accepted + accepted? + (conj (cond-> candidate + (and qualified? (not (true? decision))) + (assoc :evidence decision)))) + (inc examined) + candidate])) [accepted examined last-examined] (map vector chunk decisions)) last-cursor (some-> last-examined :cursor)] @@ -1618,7 +1667,7 @@ end-cursor (case direction :asc progress :desc (or last-selected progress))] - {:data (mapv :node selected) + (cond-> {:data (mapv :node selected) :page-info {:start-cursor start-cursor :end-cursor end-cursor @@ -1630,7 +1679,14 @@ (case direction :asc (boolean bound) :desc (boolean (:more? result))) - :bounded? (boolean (:bounded? result))}})) + :bounded? (boolean (:bounded? result))}} + (and (or evidence-decisions? *qualification*) + (= :detailed (or result-policy *lookup-result-policy*))) + (assoc :result-evidence + (into {} (keep (fn [candidate] + (when (contains? candidate :evidence) + [(get-in candidate [:node :id]) (:evidence candidate)]))) + selected))))) (defn- with-stale-boundary-errors "A well-formed authenticated boundary that replay cannot validate means @@ -1677,6 +1733,8 @@ :fetch-fn fetch-fn :subject-type subject-type :page-size page-size + :qualification *qualification* + :result-policy *lookup-result-policy* :cut-point! (stable-cut-point)} (when raw? {:raw-candidates? true}) (if (= :forward traversal) @@ -1717,6 +1775,8 @@ :subject-type subject-type :cut-point! (stable-cut-point) :page-size page-size + :qualification *qualification* + :result-policy *lookup-result-policy* :after (when (= :asc direction) edge) :before (when (= :desc direction) edge) :last-window? (and (= :desc direction) (nil? edge)) @@ -1747,6 +1807,7 @@ :plan plan :candidates candidates :scope-identity proof-identity + :qualification *qualification* :limits (recursive-operator-limits) ;; The engine reads only the decisions; skip the portable ;; checkpoint's sorts and digest. @@ -1770,6 +1831,7 @@ (if (nil? anchor-eid) empty-bounded-page (let [accept? (:accept? candidate-filter) + accept-evidence (:accept-evidence candidate-filter) candidate-window (or (:candidate-window candidate-filter) operator-lookup/default-candidate-window) run @@ -1784,6 +1846,8 @@ :subject-type subject-type :anchor-eid anchor-eid :page-size size + :qualification *qualification* + :result-policy *lookup-result-policy* :candidate-window candidate-window :order-direction direction :boundary (:coords bound) @@ -1792,6 +1856,9 @@ (when accept? (fn [eid] (accept? (spice-object result-type eid)))) + :accept-result-evidence + (when accept-evidence + (fn [eid] (accept-evidence (spice-object result-type eid)))) :cut-point! (stable-cut-point) :traversal-limits (stable-limits)}))) emissions (:emissions run) @@ -1813,6 +1880,7 @@ progress (or last-selected progress))] (report-least-path-run! run) + (with-emission-evidence {:data (mapv :node items) :page-info {:start-cursor start-cursor @@ -1825,7 +1893,8 @@ (if (= :asc direction) (boolean bound) (boolean (:has-more? run))) - :bounded? (boolean (:bounded? run))}})))) + :bounded? (boolean (:bounded? run))}} + ordered))))) (defn- least-path-lookup-page "Keyset pagination for an acyclic plan: ascending pages resume strictly @@ -1859,6 +1928,7 @@ ordered)] (report-least-path-run! run) (report-adapter-attempts! attempts) + (with-emission-evidence (page-response {:items items :range-reusable? true @@ -1867,7 +1937,20 @@ (boolean (:has-more? run))) :has-previous? (if descending? (boolean (:has-more? run)) - (boolean bound))}))))) + (boolean bound))}) + ordered))))) + +(defn- structural-cover-fetch + "Enumerates the positive structural cover from the same compact scan/cache. + Only the exact candidate evaluator decides authorization. Projection keeps + qualified candidates, including expired rows, and leaves ordinary chunks + untouched. It is never used for an authorization decision." + [fetch-fn] + (fn [descriptor] + (let [rows (fetch-fn (assoc descriptor :include-qualifier? true))] + (if (some vector? rows) + (mapv relationship-edge/endpoint rows) + rows)))) (defn- filtered-lookup-page [db plan traversal query {:keys [direction size bound] :as page-req} @@ -1887,6 +1970,9 @@ empty-bounded-page (let [{:keys [fetch-fn attempts]} ((if least-path? least-path-fetch-fn stable-fetch-fn) db) + fetch-fn (if (:structural-cover? candidate-filter) + (structural-cover-fetch fetch-fn) + fetch-fn) cache (when-not least-path? (when cache-fn (cache-fn))) checkpoints (stable-checkpoints cache) @@ -1896,6 +1982,7 @@ (checkpoint-series-size query size))) fetch-exclusive (fn [candidate-bound limit] + (binding [*qualification* (when-not (:structural-cover? candidate-filter) *qualification*)] (if least-path? (let [run-options (least-path-run-options plan fetch-fn traversal subject-type @@ -1906,9 +1993,10 @@ #(run-least-path-page run-options traversal)) items (mapv - (fn [{:keys [value coords]}] - {:node (spice-object result-type value) - :cursor (least-path-edge plan traversal coords)}) + (fn [{:keys [value coords evidence]}] + (cond-> {:node (spice-object result-type value) + :cursor (least-path-edge plan traversal coords)} + (some? evidence) (assoc :evidence evidence))) (:emissions run))] (report-least-path-run! run) ;; Raw descending least-path emissions are already in @@ -1924,16 +2012,36 @@ limit direction candidate-bound checkpoints checkpoint-key true)))) items - (stable-items plan traversal result-type - (:start-ordinal result) (:eids result))] + (cond->> (stable-items plan traversal result-type + (:start-ordinal result) (:eids result)) + *qualification* + (mapv (fn [item] + (let [value (get (:result-evidence result) (get-in item [:node :id]) true)] + (cond-> item (not (true? value)) (assoc :evidence value))))))] ;; Stable-page returns canonical order for both directions; ;; filtering examines backward windows in reverse order. (if (= :desc direction) (vec (reverse items)) - items)))) + items))))) page (execute-filtered-lookup-window - result-type page-req candidate-filter fetch-exclusive)] + result-type page-req + (cond-> candidate-filter + (and *qualification* (not (:structural-cover? candidate-filter))) + ;; A filtered cursor carries a structural inclusive sentinel. + ;; If its evidence was not retained, recover the complete root + ;; decision at this same selected basis before exposing it. + (assoc :inclusive-evidence-fn + (fn [eid] + (stable-route/check-eids + (merge (stable-limits) + {:adapter db :fetch-fn fetch-fn :plan plan + :qualification *qualification* + :subject-type subject-type + :subject-eid (if (= :forward traversal) anchor-eid eid) + :resource-eid (if (= :forward traversal) eid anchor-eid) + :cut-point! (stable-cut-point)}))))) + fetch-exclusive)] (report-adapter-attempts! attempts) page)))) @@ -1958,6 +2066,7 @@ (if (nil? anchor-eid) empty-bounded-page (let [external-accept? (:accept? candidate-filter) + external-accept-evidence (:accept-evidence candidate-filter) external-accept-many? (:accept-many? candidate-filter) recursive-decisions (recursive-batch-evaluator @@ -1967,6 +2076,9 @@ (let [decisions (recursive-decisions nodes) external-decisions (cond + external-accept-evidence + (mapv external-accept-evidence nodes) + external-accept-many? (vec (external-accept-many? nodes)) @@ -1975,7 +2087,11 @@ :else (vec (repeat (count nodes) true)))] - (mapv #(and %1 %2) decisions external-decisions))) + (mapv (fn [decision external] + (if *qualification* + (evidence/combine :intersection decision external) + (and decision external))) + decisions external-decisions))) cover-page (filtered-lookup-page db cover-plan traversal query @@ -1985,6 +2101,9 @@ (or (:candidate-window candidate-filter) operator-lookup/default-candidate-window) :maximum-batch-width operator-batch-schedule/maximum-width + :structural-cover? (some? *qualification*) + :evidence-decisions? (some? *qualification*) + :result-policy (authorization-result/result-policy query) :accept-many? evaluate-batch})] (update cover-page :page-info @@ -2064,14 +2183,16 @@ (checkpoint-series-size query size))) false))))] (report-adapter-attempts! attempts) - (page-response + (cond-> (page-response {:items (stable-items plan traversal result-type (:start-ordinal result) (:eids result)) :range-reusable? true :has-next? (:has-next? result) - :has-previous? (:has-previous? result)}))) + :has-previous? (:has-previous? result)}) + (and *qualification* (= :detailed *lookup-result-policy*)) + (assoc :result-evidence (:result-evidence result))))) -(defn can? +(defn check-evidence [db subject permission resource] (let [subject-type (:type subject) subject-eid (object-eid db (:id subject)) @@ -2092,6 +2213,7 @@ (if (operator-recursive/recursive-plan? plan) (operator-recursive/check-cached-eids {:adapter db :plan plan + :qualification *qualification* :subject-type subject-type :subject-eid subject-eid :resource-eid resource-eid @@ -2102,6 +2224,7 @@ (first (operator-vector/check-cached-many-eids {:adapter db :plan plan + :qualification *qualification* :scope-identity scope-identity :candidates [{:direction :forward @@ -2116,6 +2239,7 @@ (stable-route/check-eids (merge (stable-limits) {:adapter db + :qualification *qualification* :fetch-fn fetch-fn :plan plan :subject-type subject-type @@ -2125,6 +2249,11 @@ (report-adapter-attempts! attempts) allowed?))) false))) + +(defn can? [db subject permission resource] + (evidence/has? + (evidence/throw-if-fault! (check-evidence db subject permission resource)))) + (defn lookup-resources "Stable-discovery forward pagination. @@ -2139,7 +2268,8 @@ (let [cache-fn (fn [] (or continuation-cache (when continuation-cache-fn (continuation-cache-fn))))] - (stable-lookup-page db :forward query cache-fn candidate-filter)))) + (binding [*lookup-result-policy* (authorization-result/result-policy query)] + (project-lookup-page (stable-lookup-page db :forward query cache-fn candidate-filter)))))) (defn lookup-subjects "Stable-discovery reverse pagination; cursors are only valid against the @@ -2157,7 +2287,8 @@ (let [cache-fn (fn [] (or continuation-cache (when continuation-cache-fn (continuation-cache-fn))))] - (stable-lookup-page db :reverse query cache-fn candidate-filter)))) + (binding [*lookup-result-policy* (authorization-result/result-policy query)] + (project-lookup-page (stable-lookup-page db :reverse query cache-fn candidate-filter)))))) (def ^:private count-pagination-keys [:cursor :limit :first :last :before :after]) @@ -2178,11 +2309,18 @@ :count-limit limit})) limit))) -(defn- count-response - [{:keys [count truncated?]} limit] - (cond-> {:count count - :limit (or limit -1)} - (some? limit) (assoc :truncated? truncated?))) +(defn- count-page-categories + [categories page remaining] + (when categories + (reduce (fn [counts node] + (update counts + (if (evidence/has? (get (:result-evidence page) (:id node) true)) + :definite-count :conditional-count) + inc)) + categories + (if (some? remaining) + (take (max 0 remaining) (:data page)) + (:data page))))) (defn- recursive-operator-count "Streams exact recursive operator pages with bounded retained continuation @@ -2205,11 +2343,14 @@ evaluate-batch (recursive-batch-evaluator db plan traversal subject-type anchor-eid proof-identity) - target (when (some? count-limit) (inc count-limit))] + target (when (some? count-limit) (inc count-limit)) + policy (authorization-result/result-policy query)] (if (nil? anchor-eid) {:count 0 :truncated? false} (loop [cover-bound nil - accumulated 0] + accumulated 0 + categories (when (= :detailed policy) + {:definite-count 0 :conditional-count 0})] (execution/check! execution/*contract* :operator-recursive/count-page {:count accumulated}) @@ -2224,13 +2365,20 @@ cache-fn result-type anchor subject-type {:candidate-window operator-lookup/default-candidate-window :maximum-batch-width operator-batch-schedule/maximum-width + :structural-cover? (some? *qualification*) + :evidence-decisions? (some? *qualification*) + :result-policy policy :accept-many? evaluate-batch}) next-count (+ accumulated (count (:data page))) + categories + (count-page-categories categories page + (when (some? count-limit) + (- count-limit accumulated))) more? (get-in page [:page-info :has-next-page?]) next-bound (get-in page [:page-info :end-cursor])] (cond (and target (>= next-count target)) - {:count count-limit :truncated? true} + (merge {:count count-limit :truncated? true} categories) more? (do @@ -2239,10 +2387,10 @@ "Recursive count continuation made no cursor progress." {:type :eacl.page/invalid-cursor :eacl/error :eacl.page/invalid-cursor})) - (recur next-bound next-count)) + (recur next-bound next-count categories)) :else - {:count next-count :truncated? false})))))) + (merge {:count next-count :truncated? false} categories))))))) (defn- operator-count [db plan traversal query anchor subject-type result-type count-limit] @@ -2258,10 +2406,12 @@ :cover-plan (stable-cover-plan db plan) :subject-type subject-type :anchor-eid anchor-eid :count-limit count-limit + :qualification *qualification* + :result-policy (authorization-result/result-policy query) :scope-identity (operator-snapshot-proof-identity db) :cut-point! (stable-cut-point) :traversal-limits (stable-limits)}))) - [:count :truncated?])) + [:count :truncated? :definite-count :conditional-count])) {:count 0 :truncated? false})) (defn- stable-count @@ -2274,10 +2424,11 @@ {:adapter db :fetch-fn fetch-fn :plan plan + :qualification *qualification* :cut-point! (stable-cut-point)} options))))] (report-adapter-attempts! attempts) - (select-keys counted [:count :truncated?]))) + (select-keys counted [:count :truncated? :definite-count :conditional-count]))) (defn- count-route "The count pipeline shared by both traversal directions: limit @@ -2286,8 +2437,9 @@ [db query {:keys [traversal root-type anchor subject-type result-type stable-count-fn anchor-id-key]}] (let [limit (query-count-limit query) + policy (authorization-result/result-policy query) permission (:permission query)] - (count-response + (authorization-result/count-result (if-not (permission-root-defined? db root-type permission) {:count 0 :truncated? false} (let [plan (stable-plan @@ -2298,8 +2450,9 @@ (stable-count db plan stable-count-fn {:subject-type subject-type anchor-id-key (:id anchor) + :result-policy policy :count-limit limit})))) - limit))) + limit policy))) (defn count-resources [db {:keys [subject] :as query}] diff --git a/modules/eacl/src/eacl/operator/cover_plan.cljc b/modules/eacl/src/eacl/operator/cover_plan.cljc index 8e3ef0aa..88a67841 100644 --- a/modules/eacl/src/eacl/operator/cover_plan.cljc +++ b/modules/eacl/src/eacl/operator/cover_plan.cljc @@ -3,7 +3,8 @@ least-path rule domain. Operator composition becomes synthetic self-permission edges, so an execution-time local predicate can make each child generator exact before the parent consumes its witness." - (:require [eacl.backend.v8 :as backend] + (:require [eacl.authorization.data :as qualification-data] + [eacl.backend.v8 :as backend] [eacl.engine.sealed-plan :as sealed-plan] [eacl.operator.plan :as operator-plan])) @@ -153,6 +154,9 @@ semantic synthetic)))) operations (cond-> operations + (backend/supports? adapter :qualification qualification-data/capability) + (assoc :qualification-data (forwarding-operation adapter :qualification-data)) + (backend/supports? adapter :cache-proofs :ordered-generations) (assoc :proof-frame (forwarding-operation adapter :proof-frame)) diff --git a/modules/eacl/src/eacl/operator/evaluator.cljc b/modules/eacl/src/eacl/operator/evaluator.cljc index 43470096..35c78b39 100644 --- a/modules/eacl/src/eacl/operator/evaluator.cljc +++ b/modules/eacl/src/eacl/operator/evaluator.cljc @@ -6,10 +6,14 @@ exclusion evaluates its right operand only after exact left success. Arrow scans stay on the selected immutable adapter basis and stop at the first exact witness." - (:require [eacl.backend.v8 :as backend] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.backend.v8 :as backend] + [eacl.exact-integer :as exact] [eacl.execution :as execution] [eacl.operator.plan :as operator-plan] - [eacl.request.counters :as request-counters])) + [eacl.request.counters :as request-counters] + [eacl.relationships.edge :as edge])) (def default-limits {:maximum-transitions 100000 @@ -95,7 +99,11 @@ (when (and (not (contains? memo key)) (>= (count memo) maximum)) (limit! :memo-entries maximum (inc (count memo)))) - [(assoc memo key (boolean value)) (disj active key) (boolean value)]) + [(assoc memo key value) (disj active key) value]) + +(defn- decisive? [op value] + (or (evidence/fault? value) + (if (= :union op) (evidence/has? value) (evidence/no? value)))) (defn- direct-match? [direct-match! subject-type subject-eid resource-type resource-eid @@ -123,7 +131,7 @@ (defn- arrow-values! [resource->subjects! {:keys [key partition-index descriptor bound] :as frame} - limits counters] + limits counters qualification] (let [[permission _ _ _ resource-eid] key resource-type (first permission) partition (nth (:partitions descriptor) partition-index) @@ -134,6 +142,7 @@ (when (> next-command (:maximum-arrow-commands limits)) (limit! :arrow-commands (:maximum-arrow-commands limits) next-command)) (let [options (cond-> {:direction :asc :limit chunk-size} + qualification (assoc :include-qualifier? true) bound (assoc :bound-eid bound :inclusive-bound? false)) values (into [] @@ -162,7 +171,7 @@ (assoc frame :values values :value-index 0 - :bound (peek values) + :bound (edge/endpoint (peek values)) :exhausted? (< fetched chunk-size)) (next-partition frame))))) @@ -175,6 +184,34 @@ {:permission permission})) [permission root subject-type subject-eid intermediate-eid])) +(defn- validate-arrow-witness! + "A generator may discharge one exact arrow binding in this selected point. + Its evidence is only a lower bound for the arrow's union of bindings." + [plan qualification root-key witness] + (when (some? witness) + (execution/check! :arrow-witness) + (when-not (and (map? witness) + (= #{:point :partition :intermediate :evidence :scope} (set (keys witness))) + qualification + (= root-key (:point witness)) + (= (qualification/exact-reuse-identity qualification) (:scope witness))) + (invalid! :arrow-witness-scope "Arrow witness must belong to this exact point and request." {})) + (let [predicate (get-in (:predicate-programs plan) [(first root-key) (second root-key)]) + partition (:partition witness)] + (when-not (and (= :arrow-membership (:instruction predicate)) + (exact/natural? partition) + (< partition (count (get-in predicate [:descriptor :partitions]))) + (exact/natural? (:intermediate witness))) + (invalid! :arrow-witness-binding "Arrow witness names an invalid binding." {}))) + (when-not (boolean? (:evidence witness)) (evidence/encode (:evidence witness))) + (when-not (evidence/before? (:time qualification) (evidence/valid-until (:evidence witness))) + (invalid! :expired-arrow-witness "Arrow witness certificate has already expired." {}))) + witness) + +(defn- known-arrow-binding? [witness root-key key partition-index intermediate-eid] + (and (= root-key key) (= partition-index (:partition witness)) + (= intermediate-eid (:intermediate witness)))) + (defn check-eids "Returns exact point membership for an acyclic operator plan. @@ -183,7 +220,7 @@ denied without backend work. Recursive plans fail with a typed transition requirement; they are never interpreted as false." [{:keys [adapter plan subject-type subject-eid resource-eid limits - permission node-id]}] + permission node-id qualification arrow-witness]}] (when-not (operator-plan/operator-plan? plan) (invalid! :operator-plan-required "Operator evaluation requires a sealed operator plan." @@ -209,17 +246,23 @@ root-id (or node-id (get roots root-permission)) root-key [root-permission root-id subject-type subject-eid resource-eid] + arrow-witness (validate-arrow-witness! plan qualification root-key arrow-witness) counters (volatile! {:transitions 0 :arrow-commands 0 :arrow-values 0}) - direct-match! (backend/direct-match-invoker adapter) + direct-match! (if qualification + (let [direct-edge! (backend/direct-edge-invoker adapter)] + (fn [st s r rt o] + (qualification/qualify qualification r (direct-edge! st s r rt o)))) + (backend/direct-match-invoker adapter)) resource->subjects! (backend/scan-invoker adapter :resource->subjects)] (when-not (some? root-id) (invalid! :missing-root "Operator plan root expression is missing." {:root root-permission})) (loop [stack [{:kind :eval :key root-key}] - memo {} + memo (if (and arrow-witness (decisive? :union (:evidence arrow-witness))) + {root-key (:evidence arrow-witness)} {}) active #{} returned no-value] (if (not= no-value returned) @@ -240,55 +283,46 @@ :nary (let [op (:op continuation) - decisive? (if (= :union op) returned (not returned)) - decision (= :union op)] - (if decisive? + accumulated (evidence/combine op (:accumulated continuation) returned)] + (if (or (decisive? op accumulated) (empty? remaining)) (let [[memo active value] - (complete-value memo active key decision - maximum-memo-entries)] + (complete-value memo active key accumulated maximum-memo-entries)] (recur stack memo active value)) - (if-let [child (first remaining)] - (recur (conj stack - (assoc continuation - :remaining (subvec remaining 1)) - {:kind :eval - :key [(first key) child - (nth key 2) (nth key 3) - (nth key 4)]}) - memo active no-value) - (let [[memo active value] - (complete-value memo active key - (not= :union op) - maximum-memo-entries)] - (recur stack memo active value))))) + (recur (conj stack + (assoc continuation :accumulated accumulated + :remaining (subvec remaining 1)) + {:kind :eval + :key [(first key) (first remaining) + (nth key 2) (nth key 3) (nth key 4)]}) + memo active no-value))) :exclusion-left - (if-not returned + (if (decisive? :exclusion returned) (let [[memo active value] - (complete-value memo active key false - maximum-memo-entries)] + (complete-value memo active key returned maximum-memo-entries)] (recur stack memo active value)) (recur (conj stack - {:kind :exclusion-right :key key} + {:kind :exclusion-right :key key :left returned} {:kind :eval :key [(first key) (:right continuation) (nth key 2) (nth key 3) (nth key 4)]}) memo active no-value)) :exclusion-right - (let [[memo active value] - (complete-value memo active key (not returned) - maximum-memo-entries)] + (let [decision (evidence/combine :exclusion (:left continuation) returned) + [memo active value] + (complete-value memo active key decision maximum-memo-entries)] (recur stack memo active value)) :arrow-child - (if returned - (let [[memo active value] - (complete-value memo active key true - maximum-memo-entries)] - (recur stack memo active value)) - (recur (conj stack next-frame) - memo active no-value)) + (let [witness (evidence/combine :arrow (:via continuation) returned) + accumulated (evidence/combine :union (:accumulated next-frame) witness)] + (if (decisive? :union accumulated) + (let [[memo active value] + (complete-value memo active key accumulated maximum-memo-entries)] + (recur stack memo active value)) + (recur (conj stack (assoc next-frame :accumulated accumulated)) + memo active no-value))) (invalid! :invalid-continuation "Operator evaluator encountered an invalid continuation." @@ -359,6 +393,8 @@ :key key :descriptor (:descriptor predicate) :partition-index 0 + :accumulated (if (and arrow-witness (= root-key key)) + (:evidence arrow-witness) false) :values [] :value-index 0 :bound nil :exhausted? false}) memo active no-value) @@ -376,6 +412,7 @@ (recur (conj stack {:kind :nary :key key :op op + :accumulated (not= :union op) :remaining (subvec children 1)} {:kind :eval :key [permission first-child @@ -404,54 +441,69 @@ :arrow-next (let [{:keys [descriptor partition-index values value-index - exhausted?]} frame + exhausted? accumulated]} frame partitions (:partitions descriptor) [stack memo active returned] (cond (>= partition-index (count partitions)) (let [[memo active value] - (complete-value - memo active key false - maximum-memo-entries)] + (complete-value memo active key accumulated maximum-memo-entries)] [stack memo active value]) (< value-index (count values)) - (let [intermediate-eid (nth values value-index) + (let [compact-edge (nth values value-index) + intermediate-eid (edge/endpoint compact-edge) partition (nth partitions partition-index) + known? (and arrow-witness + (known-arrow-binding? arrow-witness root-key key + partition-index intermediate-eid)) + via (when-not known? + (if qualification + (qualification/qualify qualification (:via-relation-eid partition) compact-edge) + true)) next-frame (update frame :value-index inc)] - (if (= :relation (:target-kind partition)) - (let [decision - (direct-match? - direct-match! (nth key 2) (nth key 3) - (:intermediate-type partition) - intermediate-eid - (:target-relation partition))] - (if decision + (cond + known? + ;; The seed already contributed this exact binding. + ;; Visit only remaining target obligations. + [(conj stack next-frame) memo active no-value] + + (evidence/fault? via) + (let [[memo active value] + (complete-value memo active key via maximum-memo-entries)] + [stack memo active value]) + + (evidence/no? via) + [(conj stack (assoc next-frame :accumulated + (evidence/combine :union accumulated via))) + memo active no-value] + + (= :relation (:target-kind partition)) + (let [child (direct-match? direct-match! (nth key 2) (nth key 3) + (:intermediate-type partition) intermediate-eid + (:target-relation partition)) + witness (evidence/combine :arrow via child) + result (evidence/combine :union accumulated witness)] + (if (decisive? :union result) (let [[memo active value] - (complete-value - memo active key true - maximum-memo-entries)] + (complete-value memo active key result maximum-memo-entries)] [stack memo active value]) - [(conj stack next-frame) + [(conj stack (assoc next-frame :accumulated result)) memo active no-value])) + + :else [(conj stack - {:kind :arrow-child - :key key :next-frame next-frame} + {:kind :arrow-child :key key :next-frame next-frame :via via} {:kind :eval - :key (target-key roots (nth key 2) - (nth key 3) - intermediate-eid - partition)}) + :key (target-key roots (nth key 2) (nth key 3) + intermediate-eid partition)}) memo active no-value])) exhausted? - [(conj stack (next-partition frame)) - memo active no-value] + [(conj stack (next-partition frame)) memo active no-value] :else - [(conj stack - (arrow-values! resource->subjects! - frame limits counters)) + [(conj stack (arrow-values! resource->subjects! frame limits counters qualification)) memo active no-value])] (recur stack memo active returned)) diff --git a/modules/eacl/src/eacl/operator/lookup.cljc b/modules/eacl/src/eacl/operator/lookup.cljc index 015c3eb9..a18498f7 100644 --- a/modules/eacl/src/eacl/operator/lookup.cljc +++ b/modules/eacl/src/eacl/operator/lookup.cljc @@ -1,10 +1,13 @@ (ns eacl.operator.lookup "Demand-bounded lookup and counts over a least-path raw cover plus exact aligned operator predicates." - (:require [eacl.engine.least-path :as least-path] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.engine.least-path :as least-path] [eacl.execution :as execution] [eacl.operator.batch-schedule :as batch-schedule] [eacl.operator.cover-plan :as cover-plan] + [eacl.operator.evaluator :as scalar] [eacl.operator.plan :as operator-plan] [eacl.operator.seekable :as seekable] [eacl.operator.vector-evaluator :as vector-evaluator] @@ -44,6 +47,12 @@ {:field field :value value})) value) +(defn- result-policy [options] + (let [policy (get options :result-policy :definite)] + (when-not (contains? #{:definite :detailed} policy) + (invalid! :result-policy "Lookup result policy must be :definite or :detailed." {})) + policy)) + (defn- semantic-candidate [permission direction subject-type subject-eid resource-eid true-nodes] {:direction direction @@ -53,39 +62,105 @@ :resource-eid resource-eid :true-nodes true-nodes}) +(def maximum-local-node-evidence + "Maximum exact node entries retained during one bounded raw batch." + 100000) + +(defn- complete-arrow-witness + [options predicate permission node-id subject-type subject-eid resource-eid witness] + (let [value (evidence/throw-if-fault! (:evidence witness))] + (if (evidence/has? value) + value + (let [rule (:rule witness) + partition (first (keep-indexed + (fn [i p] + (when (and (= (:intermediate-type rule) (:intermediate-type p)) + (= (:via-relation-eid rule) (:via-relation-eid p))) i)) + (get-in predicate [:descriptor :partitions]))) + request (:qualification options)] + (when-not (some? partition) + (invalid! :arrow-witness-partition "Generated arrow binding is outside its predicate." {})) + (scalar/check-eids + {:adapter (:adapter options) :plan (:plan options) + :permission permission :node-id node-id :subject-type subject-type + :subject-eid subject-eid :resource-eid resource-eid + :qualification request :limits (:vector-limits options) + :arrow-witness {:point [permission node-id subject-type subject-eid resource-eid] + :partition partition :intermediate (:intermediate witness) + :evidence value :scope (qualification/exact-reuse-identity request)}}))))) + (defn- local-node-acceptor - [{:keys [adapter plan cache-lookup vector-limits scope-identity]} cover-plan] + [{:keys [adapter plan cache-lookup vector-limits scope-identity qualification] :as options} cover-plan] (let [root-semantic (:operator-root-semantic cover-plan) - node-map (:operator-synthetic->semantic cover-plan)] - (fn [{:keys [node direction subject-type subject-eid resource-eid]}] + node-map (:operator-synthetic->semantic cover-plan) + memo (when qualification (volatile! {:points {} :entries 0}))] + (fn [{:keys [node direction subject-type subject-eid resource-eid evidence-witness]}] (let [[permission node-id :as semantic] (get node-map node) - predicate (get-in plan - [:predicate-programs permission node-id])] + predicate (get-in plan [:predicate-programs permission node-id])] (when-not semantic (invalid! :unknown-cover-node - "Least-path requested an unmapped operator cover node." - {:node node})) - ;; The public root is filtered in an aligned demand-sized vector. - ;; Direct leaves are exact by the certified relation scan itself. - (if (or (= semantic root-semantic) - (= :direct-membership (:instruction predicate))) - true - (first - (vector-evaluator/check-cached-many-eids - (cond-> - {:adapter adapter :plan plan - :permission permission :node-id node-id - :candidates - [(semantic-candidate permission direction - subject-type subject-eid resource-eid #{})] - :limits vector-limits - :scope-identity scope-identity} - cache-lookup (assoc :cache-lookup cache-lookup))))))))) + "Least-path requested an unmapped operator cover node." {:node node})) + (if qualification + (let [point [direction subject-type subject-eid resource-eid] + _ (when evidence-witness + (evidence/throw-if-fault! (:evidence evidence-witness))) + previous (get-in @memo [:points point] {}) + proof-node (when evidence-witness + (case (get-in evidence-witness [:rule :rule]) + :relation semantic + :self-permission (get node-map (get-in evidence-witness [:rule :target-node])) + (:arrow-relation :arrow-permission) nil + (invalid! :unsupported-generator-witness + "Qualified generator witness is not an exact node result." {}))) + known (if proof-node + (assoc previous proof-node + (evidence/throw-if-fault! (:evidence evidence-witness))) + previous) + result + (cond + (contains? known semantic) + (get known semantic) + + (and evidence-witness (= :arrow-membership (:instruction predicate))) + (complete-arrow-witness options predicate permission node-id subject-type + subject-eid resource-eid evidence-witness) + + :else + (first + (vector-evaluator/check-cached-many-eids + (cond-> + {:adapter adapter :plan plan :permission permission :node-id node-id + :candidates [(assoc (semantic-candidate permission direction subject-type + subject-eid resource-eid #{}) + :evidence-witnesses known)] + :qualification qualification + :witness-scope (qualification/exact-reuse-identity qualification) + :limits vector-limits :scope-identity scope-identity} + cache-lookup (assoc :cache-lookup cache-lookup))))) + known (assoc known semantic (evidence/throw-if-fault! result)) + entries (+ (:entries @memo) (- (count known) (count previous)))] + (when (> entries maximum-local-node-evidence) + (invalid! :node-evidence-limit "Qualified local node evidence limit exceeded." {})) + (vswap! memo #(-> % (assoc-in [:points point] known) (assoc :entries entries))) + result) + ;; The public root is filtered in an aligned demand-sized vector. + ;; Direct leaves are exact by the certified relation scan itself. + (if (or (= semantic root-semantic) + (= :direct-membership (:instruction predicate))) + true + (first + (vector-evaluator/check-cached-many-eids + (cond-> + {:adapter adapter :plan plan :permission permission :node-id node-id + :candidates [(semantic-candidate permission direction subject-type + subject-eid resource-eid #{})] + :limits vector-limits :scope-identity scope-identity} + cache-lookup (assoc :cache-lookup cache-lookup)))))))))) (defn- raw-options [{:keys [adapter cover-plan traversal subject-type anchor-eid width order-direction boundary cut-point! traversal-limits - candidate-accept?]}] + candidate-accept? qualification]}] (merge traversal-limits {:adapter adapter @@ -94,7 +169,8 @@ :page-size width :raw-candidates? true :cut-point! cut-point! - :candidate-accept? candidate-accept?} + :candidate-accept? candidate-accept? + :qualification qualification} (if (= :forward traversal) {:subject-eid anchor-eid} {:resource-eid anchor-eid}) @@ -105,10 +181,14 @@ (defn- raw-page [options] (if (:specialization-node options) (seekable/page options) - ((if (= :forward (:traversal options)) - least-path/forward-page - least-path/reverse-page) - (raw-options options)))) + (let [options (cond-> options + (:qualification options) + (assoc :candidate-accept? + (local-node-acceptor options (:cover-plan options))))] + ((if (= :forward (:traversal options)) + least-path/forward-page + least-path/reverse-page) + (raw-options options))))) (defn- specialization-node [plan permission] (let [root-id (get (operator-plan/expression-roots plan) permission) @@ -164,30 +244,53 @@ (defn- evaluate-emissions [{:keys [adapter plan traversal subject-type anchor-eid cache-lookup vector-limits permission specialization-node - accept-result? scope-identity]} + accept-result? accept-result-evidence scope-identity qualification result-policy]} cover-plan emissions] (let [permission (or permission (:root plan)) node-id (get (operator-plan/expression-roots plan) permission) - witnesses (mapv (emission-witness-fn plan cover-plan permission - node-id specialization-node) + witnesses (mapv (if (and qualification (nil? specialization-node)) + (constantly #{[permission node-id]}) + (emission-witness-fn plan cover-plan permission node-id specialization-node)) emissions) - candidates (mapv #(candidate permission traversal subject-type - anchor-eid %1 %2) + candidates (mapv (fn [witness emission] + (let [value (candidate permission traversal subject-type anchor-eid witness emission)] + (if qualification + (do + (when-not (contains? emission :evidence) + (invalid! :missing-evidence-witness "Qualified traversal must supply exact generator evidence." {})) + (-> value + (assoc :true-nodes #{} + :evidence-witnesses (zipmap witness (repeat (:evidence emission)))))) + value))) witnesses emissions) decisions (vector-evaluator/check-cached-many-eids (cond-> {:adapter adapter :plan plan :permission permission :node-id node-id :candidates candidates :limits vector-limits :scope-identity scope-identity} + qualification (assoc :qualification qualification + :witness-scope (qualification/exact-reuse-identity qualification)) cache-lookup (assoc :cache-lookup cache-lookup)))] (mapv (fn [emission witness decision] - (assoc emission - :true-nodes witness - :accepted? - (boolean - (and decision - (or (nil? accept-result?) - (accept-result? (:value emission))))))) + (when qualification (evidence/throw-if-fault! decision)) + (let [decision (if (and accept-result-evidence + (if (= result-policy :detailed) + (not (evidence/no? decision)) + (evidence/has? decision))) + (evidence/throw-if-fault! + (evidence/combine :intersection decision + (accept-result-evidence (:value emission)))) + decision)] + (cond-> (assoc emission + :accepted? + (boolean + (and (if (= result-policy :detailed) + (not (evidence/no? decision)) + (evidence/has? decision)) + (or (nil? accept-result?) + (accept-result? (:value emission)))))) + qualification (assoc :evidence decision) + (not qualification) (assoc :true-nodes witness)))) emissions witnesses decisions))) (defn- add-counters [total delta] @@ -230,13 +333,14 @@ (invalid! :invalid-result-filter "Operator result filter must be callable." {:value-type (some-> (:accept-result? options) type str)})) - (let [permission (or permission (:root plan)) + (let [result-policy (result-policy options) + permission (or permission (:root plan)) cover-plan (or (:cover-plan options) (cover-plan/seal-plan adapter plan permission)) - candidate-accept? (local-node-acceptor - (assoc options :permission permission) - cover-plan) - options (assoc options :permission permission + candidate-accept? (when-not (:qualification options) + (local-node-acceptor + (assoc options :permission permission) cover-plan)) + options (assoc options :permission permission :result-policy result-policy :order-direction order-direction :candidate-accept? candidate-accept? :specialization-node @@ -338,6 +442,26 @@ counters (some-> widths (conj width)))))))))))) +(defn- count-response [n count-limit truncated? counters categories] + (cond-> {:count n :limit (or count-limit -1) :truncated? truncated? + :exhaustive? (nil? count-limit) :counters counters} + categories (merge categories))) + +(defn- count-categories [categories evaluated remaining] + ;; Count only selected results. A lookahead grant establishes truncation but + ;; belongs to neither reported category, even when the vector overreads it. + (loop [categories categories entries (seq evaluated) remaining remaining] + (if (or (nil? entries) (and (some? remaining) (zero? remaining))) + categories + (let [entry (first entries)] + (if (:accepted? entry) + (recur (update categories + (if (evidence/has? (get entry :evidence true)) + :definite-count :conditional-count) + inc) + (next entries) (when remaining (dec remaining))) + (recur categories (next entries) remaining)))))) + (defn count-results "Exact count when :count-limit is absent; otherwise stops after the lookahead result needed to report truncation. Exact and bounded work remain @@ -348,13 +472,14 @@ (and (integer? count-limit) (not (neg? count-limit)))) (invalid! :invalid-count-limit "Count limit must be a natural integer." {:count-limit count-limit})) - (let [permission (or permission (:root plan)) + (let [result-policy (result-policy options) + permission (or permission (:root plan)) cover-plan (or (:cover-plan options) (cover-plan/seal-plan adapter plan permission)) - candidate-accept? (local-node-acceptor - (assoc options :permission permission) - cover-plan) - options (assoc options :permission permission + candidate-accept? (when-not (:qualification options) + (local-node-acceptor + (assoc options :permission permission) cover-plan)) + options (assoc options :permission permission :result-policy result-policy :candidate-accept? candidate-accept? :specialization-node (when-not (false? (:direct-specializations? options)) @@ -365,6 +490,8 @@ batch-schedule/maximum-width)] (loop [boundary nil accumulated 0 + categories (when (= :detailed result-policy) + {:definite-count 0 :conditional-count 0}) width initial-width counters {:commands 0 :fetched-values 0 :stream-opens 0 :emissions 0}] @@ -378,22 +505,23 @@ emissions (:emissions raw) counters (add-counters counters (:counters raw))] (if (empty? emissions) - {:count accumulated :limit (or count-limit -1) :truncated? false - :exhaustive? (nil? count-limit) :counters counters} + (count-response accumulated count-limit false counters categories) (let [evaluated (evaluate-emissions options cover-plan emissions) grants (reduce (fn [n entry] (if (:accepted? entry) (inc n) n)) 0 evaluated) - next-count (+ accumulated grants)] + next-count (+ accumulated grants) + categories (when categories + (count-categories categories evaluated + (when count-limit + (- count-limit accumulated))))] (if (and target (>= next-count target)) - {:count count-limit :limit count-limit :truncated? true - :exhaustive? false :counters counters} + (count-response count-limit count-limit true counters categories) (if (:exhausted? raw) - {:count next-count :limit (or count-limit -1) - :truncated? false :exhaustive? (nil? count-limit) - :counters counters} + (count-response next-count count-limit false counters categories) (recur (some-> emissions peek :coords) next-count + categories (if target (min batch-schedule/maximum-width (max (- target next-count) diff --git a/modules/eacl/src/eacl/operator/recursive.cljc b/modules/eacl/src/eacl/operator/recursive.cljc index 11412359..e4672d9e 100644 --- a/modules/eacl/src/eacl/operator/recursive.cljc +++ b/modules/eacl/src/eacl/operator/recursive.cljc @@ -6,7 +6,11 @@ Positive components use a deterministic fact worklist; intersection state is materialized only after its sealed anchor fact exists. Negative edges may consume absence only after their dependency component is complete." - (:require [eacl.backend.direct-membership :as direct] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.evidence-index :as evidence-index] + [eacl.authorization.qualification :as qualification] + [eacl.relationships.edge :as edge] + [eacl.backend.direct-membership :as direct] [eacl.backend.v8 :as backend] [eacl.execution :as execution] [eacl.operator.batch-schedule :as batch-schedule] @@ -24,6 +28,7 @@ :maximum-anchor-states 100000 :maximum-join-slots 1000000 :maximum-join-words 100000 + :maximum-evidence-cells 1000000 :maximum-components 100000 :maximum-strata 1024 :maximum-commands 1000000 @@ -185,7 +190,7 @@ intermediate-eid))) (defn- scan-intermediate-chunk! - [resource->subjects! q partition bound limits counters] + [resource->subjects! q partition bound limits counters qualification] (let [descriptor {:resource-type (first (question-permission q)) :resource-eid (question-resource-eid q) @@ -196,6 +201,7 @@ ;; chunk instead of materializing the whole slice that `take` ;; then discards; lazy adapters ignore it. options (cond-> {:direction :asc :limit chunk-size} + qualification (assoc :include-qualifier? true) (some? bound) (assoc :bound-eid bound :inclusive-bound? false)) _ (execution/check! execution/*contract* @@ -420,9 +426,9 @@ components) component-key #(question-key (first (nth components %))) starts (mapv second - (sort-by first compare - (map (fn [index] [(component-key index) index]) - (range (count components))))) + (sort-by first compare + (map (fn [index] [(component-key index) index]) + (range (count components))))) order (loop [remaining starts visited #{} order []] (if-let [start (first remaining)] @@ -472,7 +478,7 @@ :component (component-of head)}))) (defn- expand-arrow-chunk! - [resource->subjects! plan roots nodes q limits counters] + [resource->subjects! plan roots nodes q limits counters qualification] (let [{:keys [arrow-state] :as spec} (get nodes q) {:keys [partitions partition-index bound complete?]} arrow-state] (when-not arrow-state @@ -485,35 +491,39 @@ (assoc-in nodes [q :arrow-state :complete?] true) (let [partition (nth partitions partition-index) values (scan-intermediate-chunk! - resource->subjects! q partition bound limits counters) + resource->subjects! q partition bound limits counters qualification) first-slot (count (:dependencies spec)) permission-target? (= :permission (:target-kind partition)) - ;; One pass: a permission target yields a positive dependency - ;; in the next free slot; a relation target yields a direct - ;; probe on the intermediate's relation partition. - [dependencies probes] + first-probe (count (:base-probes spec)) + [dependencies probes probe-vias inactive] (reduce - (fn [[dependencies probes] intermediate-eid] - (if permission-target? - [(conj dependencies - {:key (arrow-target-question - roots q intermediate-eid partition) - :sign :positive - :slot (+ first-slot (count dependencies))}) - probes] - [dependencies - (if-let [probe - (direct-probe - (question [(:intermediate-type partition) - (:target-name partition)] - -1 (question-direction q) - (question-subject-type q) - (question-subject-eid q) - intermediate-eid) - (:target-relation partition))] - (conj probes probe) - probes)])) - [[] []] + (fn [[dependencies probes probe-vias inactive] compact-edge] + (let [intermediate-eid (edge/endpoint compact-edge) + via (if qualification + (qualification/qualify qualification (:via-relation-eid partition) compact-edge) + true)] + (cond + (or (evidence/no? via) (evidence/fault? via)) + [dependencies probes probe-vias (evidence/combine :union inactive via)] + + permission-target? + [(conj dependencies + (cond-> {:key (arrow-target-question roots q intermediate-eid partition) + :sign :positive :slot (+ first-slot (count dependencies))} + (not (true? via)) (assoc :via via))) + probes probe-vias inactive] + + :else + (if-let [probe (direct-probe + (question [(:intermediate-type partition) (:target-name partition)] + -1 (question-direction q) (question-subject-type q) + (question-subject-eid q) intermediate-eid) + (:target-relation partition))] + [dependencies (conj probes probe) + (if (true? via) probe-vias (assoc probe-vias (+ first-probe (count probes)) via)) + inactive] + [dependencies probes probe-vias inactive])))) + [[] [] (:base-probe-vias spec) false] values) short-chunk? (< (count values) (:physical-chunk-size limits)) @@ -523,7 +533,7 @@ next-state {:partitions partitions :partition-index next-partition - :bound (when-not short-chunk? (peek values)) + :bound (when-not short-chunk? (edge/endpoint (peek values))) :complete? (and short-chunk? (>= next-partition (count partitions)))} @@ -532,6 +542,12 @@ (update-in [q :dependencies] into dependencies) (update-in [q :base-probes] into probes) (assoc-in [q :arrow-state] next-state)) + nodes (cond-> nodes + (seq probe-vias) (assoc-in [q :base-probe-vias] probe-vias) + (or (seq probe-vias) (some :via dependencies) (not (false? inactive))) + (assoc-in [q :qualified-evidence?] true) + (not (false? inactive)) + (update-in [q :base-evidence] #(evidence/combine :union (or % false) inactive))) dependency-questions (mapv :key dependencies)] (add-counter! counters :arrow-chunks) (discover-graph! plan roots dependency-questions @@ -540,17 +556,25 @@ (defn- rule-bounds [rule lower-facts upper-facts] (let [{:keys [kind dependencies base-true? arrow-state]} rule - lower? #(contains? lower-facts (:key %)) - upper? #(contains? upper-facts (:key %)) + weighted? (:qualified-evidence? rule) + base (get rule :base-evidence (boolean base-true?)) + base-lower? (if weighted? (evidence/has? base) (boolean base-true?)) + base-upper? (if weighted? (not (evidence/no? base)) (boolean base-true?)) + lower? (if weighted? + #(and (evidence/has? (get % :via true)) (contains? lower-facts (:key %))) + #(contains? lower-facts (:key %))) + upper? (if weighted? + #(and (not (evidence/no? (get % :via true))) (contains? upper-facts (:key %))) + #(contains? upper-facts (:key %))) incomplete-arrow? (and arrow-state (not (:complete? arrow-state)))] (case kind :base - [(boolean base-true?) (boolean base-true?)] + [base-lower? base-upper?] :union - [(boolean (or base-true? (some lower? dependencies))) - (boolean (or base-true? (some upper? dependencies) + [(boolean (or base-lower? (some lower? dependencies))) + (boolean (or base-upper? (some upper? dependencies) incomplete-arrow?))] :intersection @@ -649,7 +673,7 @@ (defn- expand-arrow-wave! [resource->subjects! plan roots nodes initial-arrows lower-facts upper-facts - known-questions width limits counters] + known-questions width limits counters qualification] (loop [nodes nodes frontier (vec initial-arrows) remaining width] @@ -661,7 +685,7 @@ (reduce (fn [current q] (expand-arrow-chunk! - resource->subjects! plan roots current q limits counters)) + resource->subjects! plan roots current q limits counters qualification)) nodes batch) descendants (mapcat @@ -676,7 +700,8 @@ (fn [result q rule] (if (and (:arrow-state rule) (not (get-in rule [:arrow-state :complete?]))) - (assoc-in result [q :arrow-state :complete?] true) + (-> result (assoc-in [q :arrow-state :complete?] true) + (assoc-in [q :incomplete-evidence?] true)) result)) nodes nodes)) @@ -768,9 +793,9 @@ (let [spec (get nodes head)] (case (:kind spec) :union - (reduce (fn [m {:keys [key]}] + (reduce (fn [m {:keys [key slot]}] (update m key (fnil conj []) - {:kind :unary :head head})) + {:kind :unary :head head :slot slot})) result (:dependencies spec)) :intersection @@ -860,18 +885,125 @@ state))) state component)) +(defn- evidence-step! [limits counters] + (let [next (inc (:transitions @counters))] + (limit-counter! limits counters :transitions :maximum-transitions next) + (vswap! counters assoc :transitions next) + (execution/check! execution/*contract* :operator-recursive/evidence-transition))) + +(defn- dependency-value [state dependency] + (evidence/combine :arrow (get dependency :via true) + (get (:facts state) (:key dependency) false))) + +(defn- enqueue-evidence! [state head value limits counters] + (if (= value (get (:facts state) head false)) + state + (let [facts (assoc (:facts state) head value) + pending? (contains? (:queued state) head) + queue-size (+ (- (count (:agenda state)) (:agenda-index state)) (if pending? 0 1))] + (limit-counter! limits counters :facts :maximum-facts (count facts)) + (limit-counter! limits counters :queue :maximum-queue queue-size) + (vswap! counters #(-> % (assoc :facts (count facts)) (update :maximum-queue max queue-size))) + (cond-> (assoc state :facts facts) + (not pending?) (update :agenda conj head) + (not pending?) (update :queued conj head))))) + +(defn- unanchored-evidence [state rule slot anchor] + ;; No join is retained before an anchor has a nonempty completion set. + ;; A false anchor supplies a conservative certificate by itself. Already + ;; encountered faults remain authoritative even without an allocated join. + (if (nil? slot) + (or (reduce (fn [fault dependency] + (let [value (dependency-value state dependency)] + (if (evidence/fault? value) + (if fault (evidence/combine :intersection fault value) value) + fault))) nil (:dependencies rule)) + anchor) + (let [child (dependency-value state (nth (:dependencies rule) slot)) + prior (get (:facts state) (:key rule) false)] + (cond (evidence/fault? child) (evidence/combine :intersection prior child) + (evidence/fault? prior) prior + :else anchor)))) + +(defn- charge-evidence-anchor! [rule limits counters] + (let [states (inc (:anchor-states @counters)) + slots (+ (:join-slots @counters) (count (:dependencies rule))) + words (+ (:join-words @counters) (portable-word-count (count (:dependencies rule))))] + (limit-counter! limits counters :anchor-states :maximum-anchor-states states) + (limit-counter! limits counters :join-slots :maximum-join-slots slots) + (limit-counter! limits counters :join-words :maximum-join-words words) + (vswap! counters assoc :anchor-states states :join-slots slots :join-words words))) + +(defn- refresh-evidence-head! + [state rule slot completed component-of limits counters] + (let [{:keys [key kind dependencies]} rule + base (get rule :base-evidence (boolean (:base-true? rule))) + step! #(evidence-step! limits counters) + [state value] + (case kind + :base [state base] + :exclusion + (let [[left right] dependencies] + (when-not (contains? completed (component-of (:key right))) + (invalid! :incomplete-negative-component + "Exclusion attempted to consume incomplete evidence." {:head key})) + (step!) + [state (evidence/combine :exclusion (dependency-value state left) (dependency-value state right))]) + (:union :intersection) + (let [prior (get-in state [:join-states key]) + anchor (when (= kind :intersection) + (dependency-value state (nth dependencies (:anchor-slot rule)))) + unanchored? (and (= kind :intersection) (nil? prior) + (or (evidence/no? anchor) (evidence/fault? anchor)))] + (if unanchored? + [state (unanchored-evidence state rule slot anchor)] + (let [_ (when (and (= kind :intersection) (nil? prior)) + (charge-evidence-anchor! rule limits counters)) + joined (if prior + (evidence-index/replace-slot prior (if (= kind :union) (inc slot) slot) + (dependency-value state (nth dependencies slot)) step!) + (let [children (mapv #(dependency-value state %) dependencies) + values (if (= kind :union) + (cond-> (into [base] children) + (:incomplete-evidence? rule) + (conj (evidence/with-certificate false nil false))) + children)] + (evidence-index/build kind values step!))) + cells (+ (get @counters :evidence-cells 0) + (if prior 0 (evidence-index/storage-weight joined)))] + (limit-counter! limits counters :evidence-cells :maximum-evidence-cells cells) + (vswap! counters assoc :evidence-cells cells) + [(assoc-in state [:join-states key] joined) (evidence-index/value joined)]))))] + (enqueue-evidence! state key value limits counters))) + +(defn- consume-boolean-fact [state nodes {:keys [kind head slot right]} limits counters] + (case kind + :unary (enqueue-fact! state head limits counters) + :exclusion (if (contains? (:facts state) right) state (enqueue-fact! state head limits counters)) + :intersection + (let [rule (get nodes head) + state (case (join-transition-action state rule slot) + :update (update-join state rule slot) + :reserve (reserve-join! state rule limits counters) + :ignore state)] + (if (join-complete? state rule) (enqueue-fact! state head limits counters) state)) + state)) + (defn- run-component! [state nodes component completed component-of limits counters] (let [consumers (update-vals (component-consumers nodes component) sorted-consumer-bucket) state (assoc state :agenda [] :agenda-index 0) - state (initialize-component state nodes component completed - component-of limits counters)] + state (if (map? (:facts state)) + (reduce #(refresh-evidence-head! %1 (get nodes %2) nil completed component-of limits counters) + (assoc state :queued #{}) component) + (initialize-component state nodes component completed component-of limits counters))] (loop [state state] (if (= (:agenda-index state) (count (:agenda state))) (assoc state :agenda [] :agenda-index 0) (let [fact (nth (:agenda state) (:agenda-index state)) - state (update state :agenda-index inc) + state (cond-> (update state :agenda-index inc) + (map? (:facts state)) (update :queued disj fact)) next-transition (inc (:transitions @counters))] (limit-counter! limits counters :transitions :maximum-transitions next-transition) @@ -882,28 +1014,11 @@ :facts (count (:facts state)))) (let [state (reduce - (fn [state {:keys [kind head slot right]}] - (case kind - :unary - (enqueue-fact! state head limits counters) - - :exclusion - (if (contains? (:facts state) right) - state - (enqueue-fact! state head limits counters)) - - :intersection - (let [rule (get nodes head) - state - (case (join-transition-action state rule slot) - :update (update-join state rule slot) - :reserve (reserve-join! state rule limits counters) - :ignore state)] - (if (join-complete? state rule) - (enqueue-fact! state head limits counters) - state)) - - state)) + (fn [state consumer] + (if (map? (:facts state)) + (refresh-evidence-head! state (get nodes (:head consumer)) (:slot consumer) + completed component-of limits counters) + (consume-boolean-fact state nodes consumer limits counters))) state (get consumers fact []))] (recur state))))))) @@ -912,19 +1027,16 @@ [(:direction probe) (:descriptor probe) (:candidate probe)]) (defn- attach-base-decisions! - "Dispatches every not-yet-attached base probe and folds the decisions back - into their questions. Only questions that carried new probes are rewritten; - every other question already has its probes attached." - [adapter nodes limits counters direct-decisions] - (let [entries - (into [] - (mapcat - (fn [q] - (let [spec (get nodes q)] - (map #(vector q %) - (drop (or (:attached-probe-count spec) 0) - (:base-probes spec)))))) - (sorted-questions (keys nodes))) + "Attaches each new physical probe once, keeping its compact result in the + owning request memo. Via evidence composes with the target at attachment." + [adapter nodes limits counters direct-decisions qualification] + (let [entries (into [] + (mapcat (fn [q] + (let [spec (get nodes q) + attached (or (:attached-probe-count spec) 0)] + (map-indexed (fn [i probe] [q probe (+ attached i)]) + (drop attached (:base-probes spec)))))) + (sorted-questions (keys nodes))) probe-count (count entries) next-probes (+ (:probes @counters) probe-count)] (limit-counter! limits counters :probes :maximum-probes next-probes) @@ -932,43 +1044,39 @@ (if (zero? probe-count) nodes (let [physical (atom {}) - cache-lookup - (fn [probe] - (get @direct-decisions (direct-memo-key probe) - direct/cache-miss)) - decisions - (binding [direct/*physical-stats* physical] - (direct/dispatch adapter (mapv second entries) cache-lookup)) - [memo true-heads touched] - (loop [index 0 - memo (transient @direct-decisions) - true-heads #{} - touched #{}] - (if (= index probe-count) - [(persistent! memo) true-heads touched] - (let [[head probe] (nth entries index) - decision (nth decisions index)] - (recur (inc index) - (assoc! memo (direct-memo-key probe) decision) - (cond-> true-heads decision (conj head)) - (conj touched head)))))] + cache-lookup #(get @direct-decisions (direct-memo-key %) direct/cache-miss) + decisions (binding [direct/*physical-stats* physical] + (if qualification + (direct/dispatch-cached-edges adapter (mapv second entries) cache-lookup) + (direct/dispatch adapter (mapv second entries) cache-lookup))) + [memo heads] + (loop [i 0 memo (transient @direct-decisions) heads {}] + (if (= i probe-count) + [(persistent! memo) heads] + (let [[head probe slot] (nth entries i) + physical-value (nth decisions i) + decision (if qualification + (qualification/qualify qualification (get-in probe [:descriptor :relation-eid]) + physical-value) + physical-value) + via (get-in nodes [head :base-probe-vias slot] true) + decision (evidence/combine :arrow via decision) + prior (get heads head (get-in nodes [head :base-evidence] + (boolean (get-in nodes [head :base-true?]))))] + (recur (inc i) (assoc! memo (direct-memo-key probe) physical-value) + (assoc heads head (evidence/combine :union prior decision))))))] (vreset! direct-decisions memo) - (add-counter! counters :direct-adapter-commands - (:adapter-commands @physical 0)) - (add-counter! counters :direct-fetched-values - (:adapter-fetched-values @physical 0)) - (add-counter! counters :direct-memo-hits - (:cache-hits @physical 0)) - (reduce - (fn [result q] - (update result q - (fn [spec] - (assoc spec - :attached-probe-count (count (:base-probes spec)) - :base-true? (or (:base-true? spec) - (contains? true-heads q)))))) - nodes - touched))))) + (add-counter! counters :direct-adapter-commands (:adapter-commands @physical 0)) + (add-counter! counters :direct-fetched-values (:adapter-fetched-values @physical 0)) + (add-counter! counters :direct-memo-hits (:cache-hits @physical 0)) + (reduce-kv (fn [result q value] + (update result q + (fn [spec] + (cond-> (assoc spec :attached-probe-count (count (:base-probes spec)) + :base-true? (evidence/has? value)) + (not (boolean? value)) (assoc :base-evidence value :qualified-evidence? true) + (boolean? value) (dissoc :base-evidence))))) + nodes heads))))) (defn- checkpoint-weight [facts join-states completed-components completed-strata] @@ -992,61 +1100,56 @@ :scope scope-identity}]])) (defn- make-checkpoint - "Builds the portable checkpoint for a completed evaluation. The weight is - accounted and bounded whether or not a checkpoint is requested; the sorted - facts, join states, and command digest are produced only when - `checkpoint?` is set (the engine's point and page routes never read it)." - [identity state completed component-strata counters limits - undelivered-boundary checkpoint?] + "Serializes only completed state. Qualified joins need no retained update + tree after their fixed point; the canonical evidence facts suffice for replay." + [identity state completed component-strata counters limits undelivered-boundary checkpoint?] (let [completed (vec completed) - completed-strata - (->> completed (map component-strata) distinct sort vec) - weight - (checkpoint-weight (:facts state) (:join-states state) - completed completed-strata)] - (limit-counter! limits counters :checkpoint-weight - :maximum-checkpoint-weight weight) + completed-strata (->> completed (map component-strata) distinct sort vec) + qualified? (map? (:facts state)) + weight (if qualified? + (+ (* 7 (count (:facts state))) + (reduce + 0 (map evidence-index/storage-weight (vals (:join-states state))))) + (checkpoint-weight (:facts state) (:join-states state) completed completed-strata))] + (limit-counter! limits counters :checkpoint-weight :maximum-checkpoint-weight weight) (vswap! counters assoc :checkpoint-weight weight) (when checkpoint? - {:version checkpoint-version - :command-identity identity - :completed? true - :facts (vec (sorted-questions (:facts state))) - :anchor-states - (mapv second - (sort-by first compare - (map (fn [[key value]] - [(question-key key) [key value]]) - (:join-states state)))) - :completed-components completed - :completed-strata completed-strata - :pending-negative-questions [] - :pending-commands [] - :undelivered-boundary undelivered-boundary - :counters @counters}))) - -(defn- replay-checkpoint - [checkpoint identity root-questions] - (when-not (and (map? checkpoint) - (= checkpoint-version (:version checkpoint)) - (= identity (:command-identity checkpoint)) - (true? (:completed? checkpoint)) - (vector? (:facts checkpoint)) - (vector? (:anchor-states checkpoint)) - (vector? (:completed-components checkpoint)) - (vector? (:completed-strata checkpoint)) - (vector? (:pending-negative-questions checkpoint)) - (empty? (:pending-negative-questions checkpoint)) - (vector? (:pending-commands checkpoint)) - (empty? (:pending-commands checkpoint))) - (invalid! :invalid-checkpoint - "Recursive checkpoint is malformed or incompatible." + (let [facts (if qualified? + (first + (reduce (fn [[out size] q] + (let [encoded (evidence/encode (get (:facts state) q)) + size (+ size (count (secure-format/utf8-bytes encoded)))] + (limit-counter! limits counters :checkpoint-weight :maximum-checkpoint-weight size) + [(conj out [q encoded]) size])) + [[] 0] (sorted-questions (keys (:facts state))))) + (vec (sorted-questions (:facts state))))] + (cond-> {:version checkpoint-version :command-identity identity :completed? true :facts facts + :anchor-states (if qualified? [] + (mapv second (sort-by first compare + (map (fn [[key value]] [(question-key key) [key value]]) + (:join-states state))))) + :completed-components completed :completed-strata completed-strata + :pending-negative-questions [] :pending-commands [] + :undelivered-boundary undelivered-boundary :counters @counters} + qualified? (assoc :evidence-format evidence/format-version)))))) + +(defn- replay-checkpoint [checkpoint identity root-questions] + (when-not (and (map? checkpoint) (= checkpoint-version (:version checkpoint)) + (= identity (:command-identity checkpoint)) (true? (:completed? checkpoint)) + (vector? (:facts checkpoint)) (vector? (:anchor-states checkpoint)) + (vector? (:completed-components checkpoint)) (vector? (:completed-strata checkpoint)) + (vector? (:pending-negative-questions checkpoint)) (empty? (:pending-negative-questions checkpoint)) + (vector? (:pending-commands checkpoint)) (empty? (:pending-commands checkpoint)) + (or (not (contains? checkpoint :evidence-format)) + (= evidence/format-version (:evidence-format checkpoint)))) + (invalid! :invalid-checkpoint "Recursive checkpoint is malformed or incompatible." {:checkpoint-version (:version checkpoint)})) - (let [facts (set (:facts checkpoint))] - {:decisions (mapv #(contains? facts %) root-questions) - :checkpoint checkpoint - :counters (:counters checkpoint) - :replayed? true})) + (let [qualified? (contains? checkpoint :evidence-format) + facts (if qualified? + (into {} (map (fn [[q encoded]] [q (evidence/decode encoded)])) (:facts checkpoint)) + (set (:facts checkpoint)))] + {:decisions (if qualified? (mapv #(get facts % false) root-questions) + (mapv #(contains? facts %) root-questions)) + :checkpoint checkpoint :counters (:counters checkpoint) :replayed? true})) (defn- validate-many-options! [plan candidates] @@ -1075,15 +1178,21 @@ "Trusted core of `evaluate-many`: options already validated and limits normalized (each caller validates exactly once at its boundary)." [{:keys [adapter plan candidates permission limits checkpoint - scope-identity undelivered-boundary checkpoint?] + scope-identity undelivered-boundary checkpoint? qualification] :or {checkpoint? true}}] (let [roots (operator-plan/expression-roots plan) permission (or permission (:root plan)) root-questions (mapv #(candidate->root-question roots permission %) candidates) - identity (command-identity plan root-questions scope-identity)] + identity (command-identity plan root-questions + (if qualification + [:qualified evidence/format-version scope-identity + (qualification/exact-reuse-identity qualification)] + scope-identity))] (if checkpoint - (replay-checkpoint checkpoint identity root-questions) + (let [result (replay-checkpoint checkpoint identity root-questions)] + (when qualification (doseq [value (:decisions result)] (qualification/observe-evidence! qualification value))) + result) (if (empty? candidates) {:decisions [] :checkpoint nil :counters {} :replayed? false} (let [counters @@ -1105,7 +1214,7 @@ (loop [nodes (attach-base-decisions! adapter initial-nodes limits counters - direct-decisions) + direct-decisions qualification) wave-width (min batch-schedule/maximum-width (max 1 (count root-questions)))] @@ -1134,24 +1243,23 @@ (uncertain-arrow-questions nodes root-questions lower-facts upper-facts known-questions)] - (when (empty? arrows) - (invalid! - :stalled-demand-discovery - "Recursive demand discovery could not refine an uncertain result." - {:questions (count nodes) - :roots (count root-questions)})) - (let [nodes - (expand-arrow-wave! - resource->subjects! plan roots nodes arrows - lower-facts upper-facts known-questions - wave-width limits counters)] - (add-counter! counters :demand-rounds) - (recur - (attach-base-decisions! - adapter nodes limits counters - direct-decisions) - (min batch-schedule/maximum-width - (* 2 wave-width)))))))) + (if (empty? arrows) + (if qualification + [(finalize-unused-arrows nodes) nil graph] + (invalid! :stalled-demand-discovery + "Recursive demand discovery could not refine an uncertain result." + {:questions (count nodes) :roots (count root-questions)})) + (let [nodes (expand-arrow-wave! resource->subjects! plan roots nodes arrows + lower-facts upper-facts known-questions + wave-width limits counters qualification)] + (add-counter! counters :demand-rounds) + (recur (attach-base-decisions! adapter nodes limits counters direct-decisions qualification) + (min batch-schedule/maximum-width (* 2 wave-width))))))))) + annotated? (and qualification + (some (fn [[_ rule]] + (or (and (contains? rule :base-evidence) + (not (boolean? (:base-evidence rule)))) + (some :via (:dependencies rule)))) nodes)) _ (vswap! counters assoc :questions (count nodes)) {:keys [components component-of order]} graph component-count (count components) @@ -1186,7 +1294,7 @@ :strata stratum-count) [state completed] (loop [remaining order - state {:facts #{} :join-states {} + state {:facts (if annotated? {} #{}) :join-states {} :agenda [] :agenda-index 0} completed #{}] (if-let [component (first remaining)] @@ -1205,14 +1313,27 @@ checkpoint (make-checkpoint identity state (sort completed) component-strata counters limits - undelivered-boundary checkpoint?) + undelivered-boundary + (and checkpoint? (or (not annotated?) + (not-any? evidence/fault? (vals (:facts state)))))) result - {:decisions (mapv #(contains? (:facts state) %) - root-questions) + {:decisions (if annotated? + (mapv #(get (:facts state) % false) root-questions) + (mapv #(contains? (:facts state) %) root-questions)) :checkpoint checkpoint :counters @counters :replayed? false}] - (when-not (= bounded-decisions (:decisions result)) + (when-not (if qualification + (every? (fn [[q decision]] + (let [lower? (contains? (:lower graph) q) + upper? (contains? (:upper graph) q)] + (case (evidence/permissionship decision) + :evaluation-failure true + :has-permission upper? + :no-permission (not lower?) + :conditional-permission (and (not lower?) upper?)))) + (map vector root-questions (:decisions result))) + (= bounded-decisions (:decisions result))) (invalid! :demand-discovery-mismatch "Demand-bounded and exact recursive evaluation disagreed." @@ -1224,6 +1345,8 @@ (def ^:private point-cache-options {:valid? boolean?}) +(def ^:private qualified-point-cache-options {:valid? string?}) + (defn- point-cache-key [plan permission scope-identity candidate] [:operator-recursive-point checkpoint-version @@ -1233,13 +1356,17 @@ "Returns aligned recursive point decisions with proof-compatible completed point reuse. Only unresolved distinct points enter the recursive evaluator; no point is published until that whole demanded vector succeeds." - [{:keys [plan candidates permission scope-identity checkpoint] :as options}] + [{:keys [plan candidates permission scope-identity checkpoint qualification] :as options}] (validate-many-options! plan candidates) (let [options (assoc options :limits (normalize-limits (:limits options)))] (if (or checkpoint (nil? subproblem/*store*)) (evaluate-many-validated options) (let [permission (or permission (:root plan)) store subproblem/*store* + scope-identity (if qualification + [:qualified-point evidence/format-version scope-identity + (qualification/exact-reuse-identity qualification)] + scope-identity) looked-up (mapv (fn [candidate] @@ -1250,7 +1377,8 @@ (do (subproblem/record-avoided-backend-operation! store) {:candidate candidate :key key - :decision (:value resolved) :cached? true}) + :decision (if qualification (qualification/observe-evidence! qualification (evidence/decode (:value resolved))) (:value resolved)) + :cached? true}) {:candidate candidate :key key :cached? false}))) candidates) misses @@ -1268,14 +1396,15 @@ (mapv (fn [{:keys [candidate decision cached?]}] (if cached? decision (get miss-decisions candidate))) looked-up)] - (when subproblem/*populate?* + (when (and subproblem/*populate?* (not-any? evidence/fault? (:decisions evaluated))) ;; Duplicate candidates share one key; publish each key once. (reduce (fn [published {:keys [candidate key cached?]}] (if (or cached? (contains? published key)) published (do (subproblem/publish-denotation! - key point-cache-options - (get miss-decisions candidate)) + key (if qualification qualified-point-cache-options point-cache-options) + (let [value (get miss-decisions candidate)] + (if qualification (evidence/encode value) value))) (conj published key)))) #{} looked-up)) diff --git a/modules/eacl/src/eacl/operator/seekable.cljc b/modules/eacl/src/eacl/operator/seekable.cljc index 9118307f..b137ea76 100644 --- a/modules/eacl/src/eacl/operator/seekable.cljc +++ b/modules/eacl/src/eacl/operator/seekable.cljc @@ -2,9 +2,12 @@ "History-free ordered direct-leaf generators. Max-head k-way intersection and monotone anti-join refine a certified generic cover path without materializing any operand." - (:require [eacl.backend.v8 :as backend] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.backend.v8 :as backend] [eacl.engine.stable-reducer :as reducer] [eacl.execution :as execution] + [eacl.relationships.edge :as edge] [eacl.request.counters :as request-counters])) (def ^:dynamic *seek-stats* @@ -69,7 +72,7 @@ (defn- scan-values [{:keys [scan-invoker traversal subject-type anchor-eid order-direction resource-type cut-point! physical-chunk-size max-commands - max-values counters]} + max-values counters qualification]} relation-id bound inclusive?] (let [current @counters] ;; The routed cut point is the same deadline/cancellation check as the @@ -84,6 +87,7 @@ (let [scan-options ;; `:limit` lets natively paging adapters stop at the chunk. (cond-> {:direction order-direction :limit physical-chunk-size} + qualification (assoc :include-qualifier? true) (some? bound) (assoc :bound-eid bound :inclusive-bound? inclusive?)) values @@ -114,21 +118,35 @@ (let [values (scan-values options (:relation-id cursor) bound inclusive?) chunk (:physical-chunk-size options)] (assoc cursor :buffer values :index 0 - :bound (peek values) + :bound (edge/endpoint (peek values)) :done? (< (count values) chunk)))) -(defn- head [options cursor] - (cond - (< (:index cursor) (count (:buffer cursor))) - [(nth (:buffer cursor) (:index cursor)) cursor] - - (:done? cursor) [nil cursor] +(defn- head-evidence [cursor] + (let [compact (nth (:buffer cursor) (:index cursor))] + (if (vector? compact) (:evidence cursor) true))) - :else - (let [cursor (refill options cursor (:bound cursor) false)] - (if (seq (:buffer cursor)) - [(first (:buffer cursor)) cursor] - [nil cursor])))) +(defn- head [options cursor] + (loop [cursor cursor] + (if (< (:index cursor) (count (:buffer cursor))) + (let [compact (nth (:buffer cursor) (:index cursor))] + (if-not (and (:qualification options) (vector? compact)) + [(edge/endpoint compact) cursor] + (let [cached? (= compact (:qualified-edge cursor)) + value (if cached? (:evidence cursor) + (qualification/qualify (:qualification options) (:relation-id cursor) compact)) + cursor (if cached? cursor (assoc cursor :qualified-edge compact :evidence value))] + (evidence/throw-if-fault! value) + (when (and (not cached?) (not (boolean? value))) + (vswap! (:examined-certificate options) + #(evidence/combine :intersection % + (evidence/with-certificate true (evidence/valid-until value) + (evidence/complete? value))))) + (if (evidence/no? value) + (recur (update cursor :index inc)) + [(edge/endpoint compact) cursor])))) + (if (:done? cursor) + [nil cursor] + (recur (refill options cursor (:bound cursor) false)))))) (defn- advance [options cursor] (head options (update cursor :index inc))) @@ -150,15 +168,13 @@ :else (loop [index (inc (:index cursor))] (if (< index (count (:buffer cursor))) - (let [value (nth (:buffer cursor) index)] + (let [value (edge/endpoint (nth (:buffer cursor) index))] (if (at-or-beyond? order-direction value target) - [value (assoc cursor :index index)] + (head options (assoc cursor :index index)) (recur (inc index)))) (let [cursor (refill options cursor target true)] (add-stat! :inclusive-reseeks 1) - (if (seq (:buffer cursor)) - [(first (:buffer cursor)) cursor] - [nil cursor]))))))) + (head options cursor))))))) (defn- furthest [order-direction values] @@ -192,10 +208,16 @@ (if exhausted? {:emissions emissions :exhausted? true} (if (every? #(= driver-head %) heads) - (let [emissions - (conj emissions - {:value driver-head - :coords (conj coords-prefix driver-head)})] + (let [value (if (:qualification options) + (reduce #(evidence/combine :intersection %1 (head-evidence %2)) + (head-evidence driver) operands) + true) + emissions + (cond-> emissions + (not (evidence/no? value)) + (conj (cond-> {:value driver-head + :coords (conj coords-prefix driver-head)} + (:qualification options) (assoc :evidence value))))] (if (= width (count emissions)) {:emissions emissions :exhausted? false} (let [[_ driver] (advance options driver) @@ -219,91 +241,69 @@ (let [[left-head left] (head options left)] (if (nil? left-head) {:emissions emissions :exhausted? true} - (let [[right-head right] (seek options right left-head)] + (let [[right-head right] (seek options right left-head) + match? (= left-head right-head) + value (if (:qualification options) + (evidence/combine :exclusion (head-evidence left) + (if match? (head-evidence right) false)) + (not match?)) + emissions (cond-> emissions + (not (evidence/no? value)) + (conj (cond-> {:value left-head :coords (conj coords-prefix left-head)} + (:qualification options) (assoc :evidence value))))] (add-stat! :anti-join-rounds 1) - (if (= left-head right-head) + (if (= width (count emissions)) + {:emissions emissions :exhausted? false} (let [[_ left] (advance options left) - [_ right] (advance options right)] - (recur left right emissions)) - (let [emissions - (conj emissions - {:value left-head - :coords (conj coords-prefix left-head)})] - (if (= width (count emissions)) - {:emissions emissions :exhausted? false} - (let [[_ left] (advance options left)] - (recur left right emissions))))))))))) + right (if match? (second (advance options right)) right)] + (recur left right emissions))))))))) (defn page "Returns a raw exact generator page for one certified direct - specialization. Its coordinates are exactly the generic cover path." + specialization, with the generic cover coordinates and qualified evidence. + :examined-certificate covers evaluated heads, not the continuation frontier." [{:keys [adapter plan cover-plan specialization-node traversal - subject-type width boundary traversal-limits cut-point!] + subject-type width boundary traversal-limits cut-point! qualification] :as request}] (when-not (and (integer? width) (not (neg? width))) - (invalid! :invalid-width "Seekable page width must be a natural integer." - {:width width})) + (invalid! :invalid-width "Seekable page width must be a natural integer." {:width width})) (if (zero? width) {:emissions [] :has-more? nil :exhausted? false :counters {:commands 0 :fetched-values 0 :stream-opens 0 :emissions 0}} (let [permission (first (:operator-root-semantic cover-plan)) - specialization - (get-in plan [:specializations permission specialization-node]) + specialization (get-in plan [:specializations permission specialization-node]) {:keys [kind typed-partitions driver operands]} specialization - partitions (get typed-partitions subject-type) - by-node (into {} (map (juxt :node :relation-eid)) partitions) - relation-ids (mapv by-node (into [driver] operands))] - (when-not (and (contains? #{:direct-k-way-intersection - :direct-monotone-exclusion} kind) - (= (count relation-ids) - (count (distinct relation-ids))) - (every? some? relation-ids)) - (invalid! :ineligible "Direct specialization is not eligible." - {:permission permission :node specialization-node - :subject-type subject-type :kind kind})) - (let [coords-prefix - (relation-path cover-plan specialization-node driver subject-type - (first relation-ids)) - boundary-eid - (when boundary - (when-not (and (= coords-prefix (pop (vec boundary))) - (integer? (peek boundary))) - (invalid! :invalid-boundary - "Seekable boundary is outside the generic cover path." - {:boundary boundary - :expected-prefix coords-prefix})) - (peek boundary)) + by-node (into {} (map (juxt :node :relation-eid)) (get typed-partitions subject-type)) + relation-ids (mapv by-node (into [driver] operands)) + _ (when-not (and (contains? #{:direct-k-way-intersection :direct-monotone-exclusion} kind) + (= (count relation-ids) (count (distinct relation-ids))) + (every? some? relation-ids)) + (invalid! :ineligible "Direct specialization is not eligible." + {:permission permission :node specialization-node :subject-type subject-type :kind kind})) + coords-prefix (relation-path cover-plan specialization-node driver subject-type (first relation-ids)) + boundary-eid (when boundary + (when-not (and (= coords-prefix (pop (vec boundary))) (integer? (peek boundary))) + (invalid! :invalid-boundary "Seekable boundary is outside the generic cover path." + {:boundary boundary :expected-prefix coords-prefix})) + (peek boundary)) limits (or traversal-limits {}) - counters (volatile! - {:commands 0 :fetched-values 0 :stream-opens 0 - :emissions 0}) - options (assoc request - :resource-type (first permission) - :scan-invoker - (backend/scan-invoker - adapter - (if (= :forward traversal) - :subject->resources - :resource->subjects)) - :boundary-eid boundary-eid - :physical-chunk-size - (or (:physical-chunk-size limits) - reducer/default-physical-chunk-size) - :max-commands (or (:max-commands limits) - reducer/default-max-commands) - :max-values (or (:max-values limits) - reducer/default-max-values) - :cut-point! cut-point! - :counters counters) - result - (case kind - :direct-k-way-intersection - (intersection-emissions options relation-ids width coords-prefix) - :direct-monotone-exclusion - (exclusion-emissions options relation-ids width coords-prefix)) + counters (volatile! {:commands 0 :fetched-values 0 :stream-opens 0 :emissions 0}) + options (cond-> (assoc request + :resource-type (first permission) + :scan-invoker (backend/scan-invoker adapter (if (= :forward traversal) + :subject->resources :resource->subjects)) + :boundary-eid boundary-eid + :physical-chunk-size (or (:physical-chunk-size limits) reducer/default-physical-chunk-size) + :max-commands (or (:max-commands limits) reducer/default-max-commands) + :max-values (or (:max-values limits) reducer/default-max-values) + :cut-point! cut-point! + :counters counters) + qualification (assoc :examined-certificate (volatile! true))) + result (case kind + :direct-k-way-intersection (intersection-emissions options relation-ids width coords-prefix) + :direct-monotone-exclusion (exclusion-emissions options relation-ids width coords-prefix)) emissions (:emissions result)] (vswap! counters assoc :emissions (count emissions)) (add-stat! :emissions (count emissions)) - (assoc result - :has-more? nil - :counters @counters))))) + (cond-> (assoc result :has-more? nil :counters @counters) + qualification (assoc :examined-certificate @(:examined-certificate options)))))) diff --git a/modules/eacl/src/eacl/operator/vector_evaluator.cljc b/modules/eacl/src/eacl/operator/vector_evaluator.cljc index 7b8302c8..ee542934 100644 --- a/modules/eacl/src/eacl/operator/vector_evaluator.cljc +++ b/modules/eacl/src/eacl/operator/vector_evaluator.cljc @@ -1,7 +1,11 @@ (ns eacl.operator.vector-evaluator "Aligned mask-driven predicates for bounded acyclic candidate vectors." - (:require [eacl.backend.direct-membership :as direct] + (:require [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.backend.direct-membership :as direct] [eacl.backend.v8 :as backend] + [eacl.caveats.values :as caveat-values] + [eacl.execution :as execution] [eacl.exact-integer :as exact-integer] [eacl.operator.bitmask :as bitmask] [eacl.operator.evaluator :as scalar] @@ -10,7 +14,9 @@ (def ^:private required-candidate-keys #{:direction :subject-type :subject-eid :resource-type :resource-eid}) -(def ^:private optional-candidate-keys #{:true-nodes}) +(def ^:private optional-candidate-keys #{:true-nodes :evidence-witnesses}) +(def maximum-evidence-witnesses 4096) +(def maximum-evidence-witness-bytes 4194304) (def ^:private unresolved ::unresolved) (def ^:dynamic *vector-stats* @@ -38,11 +44,12 @@ (invalid! :invalid-candidate "Vector candidate must be a map." {:index index :candidate candidate})) ;; Closed key set without allocating one: every required key present and - ;; the map holds nothing beyond the required keys plus the one optional key. + ;; the map holds nothing beyond the required and optional keys. (when-not (and (every? #(contains? candidate %) required-candidate-keys) (case (count candidate) 5 true - 6 (contains? candidate :true-nodes) + 6 (or (contains? candidate :true-nodes) (contains? candidate :evidence-witnesses)) + 7 (and (contains? candidate :true-nodes) (contains? candidate :evidence-witnesses)) false)) (invalid! :invalid-candidate-fields "Vector candidate has unknown or missing fields." @@ -72,6 +79,9 @@ (invalid! :invalid-witness "Vector candidate witness nodes must be a set of plan-node keys." {:index index :true-nodes true-nodes}))) + (when (and (contains? candidate :evidence-witnesses) + (not (map? (:evidence-witnesses candidate)))) + (invalid! :invalid-witness "Evidence witnesses must map exact plan nodes to evidence." {:index index})) (if (:true-nodes candidate) candidate (assoc candidate :true-nodes #{}))) @@ -95,6 +105,38 @@ {:width (count identities)})))) normalized)) +(defn- validate-evidence-witnesses! + "Exact node evidence produced in this same selected request can discharge + predicate work. A derivation's conditional lower bound is not an exact + witness; its unresolved alternatives must still be evaluated by the owner. + Validate before point-cache lookup as well as before fresh evaluation." + [{:keys [plan qualification witness-scope]} candidates] + (when (and qualification (some #(seq (:true-nodes %)) candidates)) + (invalid! :unqualified-witness "Qualified evaluation requires temporal witness evidence." {})) + (let [n (reduce #(+ %1 (count (:evidence-witnesses %2))) 0 candidates)] + (when (pos? n) + (when (> n maximum-evidence-witnesses) + (invalid! :witness-limit "Evidence witness count exceeds the vector bound." {:count n})) + (when-not qualification + (invalid! :qualified-witness-required "Evidence witnesses require a qualified request." {})) + (when-not (= witness-scope (qualification/exact-reuse-identity qualification)) + (invalid! :witness-scope "Evidence witnesses belong to another request scope." {})) + (reduce + (fn [bytes [node value]] + (execution/check! :evidence-witness) + (when-not (and (vector? node) (= 2 (count node)) + (get-in (:predicate-programs plan) node)) + (invalid! :invalid-witness-node "Evidence witness is outside the sealed plan." {:node node})) + (let [bytes (+ bytes (if (boolean? value) 1 + (caveat-values/utf8-size (evidence/encode value))))] + (when (> bytes maximum-evidence-witness-bytes) + (invalid! :witness-size "Evidence witnesses exceed the vector byte bound." {:bytes bytes})) + (when-not (evidence/before? (:time qualification) (evidence/valid-until value)) + (invalid! :expired-witness "Evidence witness certificate has already expired." {})) + bytes)) + 0 (mapcat :evidence-witnesses candidates)))) + nil) + (defn- direct-probe [candidate descriptor] (when-let [{:keys [relation-id]} (operator-plan/relation-partition @@ -123,31 +165,48 @@ (let [indexes-where (fn [pred] (bitmask/from-indexes width (filter #(pred (nth row %)) (range width))))] - {:known-true (bitmask/portable (indexes-where true?)) - :known-false (bitmask/portable (indexes-where false?)) + {:known-true (bitmask/portable (indexes-where evidence/has?)) + :known-false (bitmask/portable (indexes-where evidence/no?)) :unresolved (bitmask/portable (indexes-where #(= unresolved %))) - :failed (bitmask/portable (bitmask/native width))})) + :failed (bitmask/portable (indexes-where evidence/fault?))})) (declare check-many-normalized) +(defn- decisive? [op result] + (or (evidence/fault? result) + (if (= :union op) (evidence/has? result) (evidence/no? result)))) + +(defn- demanded-witness-fault [candidate] + ;; These faults have already been encountered by traversal. Neither a + ;; cached answer nor a later Boolean absorber may erase that demand. + (when-let [proofs (:evidence-witnesses candidate)] + (reduce-kv (fn [fault _ value] + (if (evidence/fault? value) + (if fault (evidence/combine :union fault value) value) + fault)) + nil proofs))) + (defn check-many-eids "Evaluates a distinct vector of complete typed candidate contexts and - returns one aligned Boolean per candidate, or throws without returning a + returns one aligned decision per candidate, or throws without returning a partial vector. Direct leaves are regrouped through the bounded backend - dispatcher; arrow leaves retain exact scalar semantics." + dispatcher; arrow leaves retain exact scalar semantics. Qualified callers + may supply :evidence-witnesses per candidate and the complete :witness-scope + from qualification/exact-reuse-identity. Only exact node results qualify." [{:keys [plan candidates] :as options}] (when-not (operator-plan/operator-plan? plan) (invalid! :operator-plan-required "Vector evaluation requires a sealed operator plan." {:plan-domain (:domain plan)})) - (check-many-normalized - (assoc options :candidates (normalize-candidates candidates)))) + (let [candidates (normalize-candidates candidates)] + (validate-evidence-witnesses! options candidates) + (check-many-normalized (assoc options :candidates candidates)))) (defn- check-many-normalized "Trusted core of `check-many-eids`: the candidate vector is already normalized (each caller normalizes exactly once at its boundary)." [{:keys [adapter plan candidates cache-lookup cache-publish-many! - limits permission node-id]}] + limits permission node-id qualification]}] (let [width (count candidates)] (if (zero? width) [] @@ -175,7 +234,7 @@ (let [current (get @memo node-key unresolved-row) resolved (reduce (fn [result index] (assoc result index - (boolean (nth values index)))) + (nth values index))) current indexes)] (vswap! memo assoc node-key resolved) resolved)) @@ -184,10 +243,15 @@ witnessed (reduce (fn [values index] - (if (contains? (:true-nodes (nth candidates index)) - node-key) - (assoc values index true) - values)) + (let [candidate (nth candidates index) + proofs (:evidence-witnesses candidate) + fault (when (and proofs (= permission root-permission) (= node-id root-id)) + (demanded-witness-fault candidate))] + (cond + fault (assoc values index fault) + (contains? proofs node-key) (assoc values index (get proofs node-key)) + (contains? (:true-nodes candidate) node-key) (assoc values index true) + :else values))) initial indexes) _ (vswap! memo assoc node-key witnessed) pending (filterv #(= unresolved (nth witnessed %)) @@ -221,8 +285,13 @@ probes (mapv second indexed-probes) decisions (if (seq probes) - (direct/dispatch adapter probes - cache-lookup) + (if qualification + (mapv (fn [probe compact-edge] + (qualification/qualify qualification + (get-in probe [:descriptor :relation-eid]) + compact-edge)) + probes (direct/dispatch-edges adapter probes)) + (direct/dispatch adapter probes cache-lookup)) [])] ;; Retain exact leaf decisions privately until ;; every demanded subgroup in the vector has @@ -265,67 +334,37 @@ :subject-eid (:subject-eid candidate) :resource-eid (:resource-eid candidate) - :limits limits})] + :limits limits :qualification qualification})] (assoc result index decision))) witnessed pending) - :any-true - (loop [children (:children predicate) - remaining pending - result witnessed] - (if (or (empty? children) - (empty? remaining)) - (reduce #(assoc %1 %2 false) - result remaining) - (let [child (evaluate! - [permission (first children)] - remaining) - granted (filterv #(true? (nth child %)) - remaining) - remaining (filterv #(false? (nth child %)) - remaining) - result (reduce #(assoc %1 %2 true) - result granted)] - (recur (subvec children 1) - remaining result)))) - - :all-true - (loop [children (:children predicate) - remaining pending - result witnessed] - (if (or (empty? children) - (empty? remaining)) - (reduce #(assoc %1 %2 true) - result remaining) - (let [child (evaluate! - [permission (first children)] - remaining) - rejected (filterv #(false? (nth child %)) - remaining) - remaining (filterv #(true? (nth child %)) - remaining) - result (reduce #(assoc %1 %2 false) - result rejected)] - (recur (subvec children 1) - remaining result)))) + (:any-true :all-true) + (let [op (if (= :any-true instruction) :union :intersection)] + (loop [children (:children predicate) + remaining pending + result (reduce #(assoc %1 %2 (not= op :union)) witnessed pending)] + (if (or (empty? children) (empty? remaining)) + result + (let [child (evaluate! [permission (first children)] remaining) + result (reduce (fn [row index] + (assoc row index (evidence/combine op + (nth row index) + (nth child index)))) + result remaining) + remaining (filterv #(not (decisive? op (nth result %))) remaining)] + (recur (subvec children 1) remaining result))))) :left-and-not-right - (let [left (evaluate! - [permission (:left predicate)] - pending) - admitted (filterv #(true? (nth left %)) - pending) - rejected (filterv #(false? (nth left %)) - pending) - result (reduce #(assoc %1 %2 false) - witnessed rejected)] + (let [left (evaluate! [permission (:left predicate)] pending) + admitted (filterv #(not (decisive? :exclusion (nth left %))) pending) + result (reduce #(assoc %1 %2 (nth left %2)) witnessed pending)] (if (empty? admitted) result - (let [right (evaluate! - [permission (:right predicate)] - admitted)] - (reduce #(assoc %1 %2 - (not (nth right %2))) + (let [right (evaluate! [permission (:right predicate)] admitted)] + (reduce (fn [row index] + (assoc row index (evidence/combine :exclusion + (nth left index) + (nth right index)))) result admitted)))) (invalid! :unknown-predicate-instruction @@ -347,9 +386,9 @@ :root-masks (root-masks width (get @memo [root-permission root-id])))) - (when cache-publish-many! + (when (and cache-publish-many! (not-any? evidence/fault? decisions)) (cache-publish-many! @completed-leaves)) - (mapv boolean decisions)) + decisions) (catch #?(:clj Exception :cljs :default) error (add-stat! :failed-vectors 1) (throw error)))))))) @@ -357,6 +396,8 @@ (def ^:private point-cache-options {:valid? boolean?}) +(def ^:private qualified-point-cache-options {:valid? string?}) + (defn- point-cache-key [plan permission node-id scope-identity candidate] [:operator-acyclic-point 1 @@ -368,18 +409,23 @@ reuse. Cache hits only fill already demanded decisions. Point misses remain private until the entire demanded vector succeeds; cache-disabled execution performs no cache work." - [{:keys [plan candidates permission node-id scope-identity] :as options}] + [{:keys [plan candidates permission node-id scope-identity qualification] :as options}] (when-not (operator-plan/operator-plan? plan) (invalid! :operator-plan-required "Vector evaluation requires a sealed operator plan." {:plan-domain (:domain plan)})) (let [candidates (normalize-candidates candidates) + _ (validate-evidence-witnesses! options candidates) permission (or permission (:root plan)) node-id (or node-id (get (operator-plan/expression-roots plan) permission)) options (assoc options :candidates candidates :permission permission :node-id node-id) - store subproblem/*store*] + store subproblem/*store* + scope-identity (if (and store qualification) + [:qualified-point evidence/format-version scope-identity + (qualification/exact-reuse-identity qualification)] + scope-identity)] (if (or (nil? store) (empty? candidates)) (check-many-normalized options) (let [looked-up @@ -388,11 +434,13 @@ (let [key (point-cache-key plan permission node-id scope-identity candidate)] (if-let [resolved - (subproblem/lookup-denotation! key)] + (when-not (demanded-witness-fault candidate) + (subproblem/lookup-denotation! key))] (do (subproblem/record-avoided-backend-operation! store) {:candidate candidate :key key - :decision (:value resolved) :cached? true}) + :decision (if qualification (qualification/observe-evidence! qualification (evidence/decode (:value resolved))) (:value resolved)) + :cached? true}) {:candidate candidate :key key :cached? false}))) candidates) miss-records (filterv (complement :cached?) looked-up) @@ -416,12 +464,14 @@ (nth miss-decisions miss-index)))))))] ;; The full miss vector and its leaf subgroups have succeeded before ;; any completed point becomes externally reusable. - (when subproblem/*populate?* + (when (and subproblem/*populate?* (not-any? evidence/fault? miss-decisions)) (dotimes [miss-index (count miss-records)] - (subproblem/publish-denotation! - (:key (nth miss-records miss-index)) - point-cache-options - (nth miss-decisions miss-index)))) + (let [decision (nth miss-decisions miss-index)] + (when-not (evidence/fault? decision) + (subproblem/publish-denotation! + (:key (nth miss-records miss-index)) + (if qualification qualified-point-cache-options point-cache-options) + (if qualification (evidence/encode decision) decision)))))) (add-stat! :point-cache-hits (- (count looked-up) (count misses))) (add-stat! :point-cache-misses (count misses)) decisions)))) diff --git a/modules/eacl/src/eacl/relationships/edge.cljc b/modules/eacl/src/eacl/relationships/edge.cljc new file mode 100644 index 00000000..c140f5c5 --- /dev/null +++ b/modules/eacl/src/eacl/relationships/edge.cljc @@ -0,0 +1,30 @@ +(ns eacl.relationships.edge + "Compact ordered scan values. Ordinary edges remain their native eid; + only qualified edges allocate an [eid qualifier-eid] pair." + (:require [eacl.exact-integer :as exact])) + +(def format-version 1) + +(defn pack [eid qualifier-eid] + (if (nil? qualifier-eid) eid [eid qualifier-eid])) + +(defn endpoint [edge] + (if (vector? edge) (nth edge 0) edge)) + +(defn qualifier-id [edge] + (when (vector? edge) (nth edge 1))) + +(defn from-datom [datom] + (let [value (:v datom)] + (pack (nth value 3) (nth value 4)))) + +(defn valid? + "Structural adapter guard. Native qualifier existence/format/allowance is + checked by the request resolver, never by treating a bad ref as nil." + [edge] + (if (vector? edge) + (and (= 2 (count edge)) + (exact/natural? (nth edge 0)) + (exact/natural? (nth edge 1)) + (pos? (nth edge 1))) + (exact/natural? edge))) diff --git a/modules/eacl/src/eacl/relationships/endpoint_pair.cljc b/modules/eacl/src/eacl/relationships/endpoint_pair.cljc index a7cb6c7c..be164f18 100644 --- a/modules/eacl/src/eacl/relationships/endpoint_pair.cljc +++ b/modules/eacl/src/eacl/relationships/endpoint_pair.cljc @@ -62,24 +62,27 @@ (defn assert-supported! "Checks a value at a serving boundary; integrity and migration decoders may - inspect structurally valid future qualifiers without authorizing them." - [value] + inspect structurally valid future qualifiers without authorizing them. The + explicit compact-scan mode exposes refs for qualification, never membership." + ([value] (assert-supported! value false)) + ([value include-qualifier?] (when-not (endpoint-value? value) (throw (ex-info "Malformed EACL Relationship endpoint value." {:type :eacl/invalid-relationship-storage :eacl/error :eacl/invalid-relationship-storage :value value}))) - (when (some? (nth value 4)) + (when (and (some? (nth value 4)) (not (true? include-qualifier?))) (throw (ex-info "Relationship qualifiers are not enabled in this release." {:type :eacl/unsupported-qualifier :eacl/error :eacl/unsupported-qualifier :qualifier-eid (nth value 4)}))) - value) + value)) (defn checked-datoms "Validates one ordered stream before publishing each row. A single-row lookahead detects competing qualifier variants before either can authorize." - [datoms] + ([datoms] (checked-datoms datoms false)) + ([datoms include-qualifier?] (lazy-seq (when-let [rows (seq datoms)] (let [row (first rows) @@ -94,8 +97,8 @@ :reason :duplicate-identity :endpoint-eid (:e row) :identity (identity-prefix value)}))) - (assert-supported! value) - (cons row (checked-datoms (rest rows))))))) + (assert-supported! value include-qualifier?) + (cons row (checked-datoms (rest rows) include-qualifier?))))))) (defn valid-prefix? "True for the complete typed three-component endpoint index prefix." diff --git a/modules/eacl/src/eacl/relationships/filters.cljc b/modules/eacl/src/eacl/relationships/filters.cljc index b23125ad..88ae0238 100644 --- a/modules/eacl/src/eacl/relationships/filters.cljc +++ b/modules/eacl/src/eacl/relationships/filters.cljc @@ -19,7 +19,7 @@ (into known-anchor-keys [:first :last :after :before :cursor :limit :page/basis :consistency :cache? :populate-cache? - :evaluation :timeout-ms + :evaluation :timeout-ms :caveat-context :relationship-state :cancellation-token :aggregate-limits :authorization])) (def ^:dynamic ^:no-doc *validated-request?* false) @@ -34,6 +34,11 @@ {:type :eacl.filters/invalid-filter :eacl/error :eacl.filters/invalid-filter :value filters}))) + (when (and (contains? filters :relationship-state) + (not (#{:stored :expiry-active} (:relationship-state filters)))) + (throw (ex-info "Relationship state must be :stored or :expiry-active." + {:type :eacl.filters/invalid-filter :eacl/error :eacl.filters/invalid-filter + :filter :relationship-state}))) (doseq [[unsupported-key hint] [[:resource/id-prefix "Filter on :resource/id, or filter external ids client-side."] diff --git a/modules/eacl/src/eacl/relationships/inspection.cljc b/modules/eacl/src/eacl/relationships/inspection.cljc new file mode 100644 index 00000000..d0ac82ce --- /dev/null +++ b/modules/eacl/src/eacl/relationships/inspection.cljc @@ -0,0 +1,49 @@ +(ns eacl.relationships.inspection + "Physical Relationship inspection, separate from Caveat authorization." + (:require [eacl.authorization.qualification :as qualification])) + +(def qualifier-key ::qualifier-eid) +(def relation-key ::relation-eid) + +(defn row + "Keeps qualifier alignment inside a native Relationship row until decoding. + Ordinary rows retain their existing value with no annotation allocation." + [relationship relation-eid qualifier-eid] + (if qualifier-eid + (assoc relationship qualifier-key qualifier-eid relation-key relation-eid) + relationship)) + +(defn decode [request relationship] + (if-let [qid (get relationship qualifier-key)] + (merge (dissoc relationship qualifier-key relation-key) + (qualification/inspect request (get relationship relation-key) qid)) + relationship)) + +(defn active? + "Exclusive expiry only. Caveated input is not an authorization grant." + [time relationship] + (let [deadline (:valid-until-ms relationship)] + (or (nil? deadline) (< time deadline)))) + +(defn window-options [request filters options] + (if-not request + options + (let [options (assoc options :include-qualifier? true)] + (if (= :expiry-active (:relationship-state filters)) + (assoc options :accept? + (fn [relationship] + (let [relationship (decode request relationship)] + (and (active? (:time request) relationship) + (if-let [accept? (:accept? options)] (accept? relationship) true))))) + options)))) + +(defn decode-page [request page] + (if request + (update page :data #(mapv (partial decode request) %)) + page)) + +(defn page-info [request filters page] + (if request + (assoc page :relationship-state (get filters :relationship-state :stored) + :evaluation-time-ms (:time request)) + page)) diff --git a/modules/eacl/src/eacl/relationships/mutations.cljc b/modules/eacl/src/eacl/relationships/mutations.cljc index ca161cb7..9dd3c34c 100644 --- a/modules/eacl/src/eacl/relationships/mutations.cljc +++ b/modules/eacl/src/eacl/relationships/mutations.cljc @@ -1,6 +1,38 @@ (ns eacl.relationships.mutations "Backend-neutral relationship mutation helpers." - (:require [eacl.relationships.storage :as storage])) + (:require [eacl.authorization.context :as context] + [eacl.caveats.values :as values] + [eacl.relationships.storage :as storage])) + +(def qualifier-keys #{:caveat :caveat-context :valid-until-ms}) +(def ^:private relationship-keys (into #{:subject :relation :resource} qualifier-keys)) + +(defn- invalid-qualifier! [reason] + (throw (ex-info "Invalid Relationship qualifier input." + {:type :eacl/invalid-relationship-qualifier + :eacl/error :eacl/invalid-relationship-qualifier :reason reason}))) + +(defn normalize-relationship + "Admits portable public qualifier input before basis selection. Caveat names + resolve at the selected writer basis; declared parameter validation remains + at that boundary. Empty optional data canonicalizes to the ordinary shape." + [{:keys [caveat caveat-context valid-until-ms] :as relationship}] + (when-not (and (map? relationship) (every? relationship-keys (keys relationship))) + (invalid-qualifier! :relationship-shape)) + (when (and (some? caveat) (not (values/parameter-name? caveat))) + (invalid-qualifier! :caveat-name)) + (when (and (contains? relationship :caveat-context) (nil? caveat)) + (invalid-qualifier! :context-without-caveat)) + (when (and (some? valid-until-ms) (not (values/valid-time? valid-until-ms))) + (invalid-qualifier! :expiry)) + (if-not (some #(contains? relationship %) qualifier-keys) + relationship + (let [bound (when (some? caveat-context) + (context/value (context/prepare caveat-context)))] + (cond-> (apply dissoc relationship qualifier-keys) + caveat (assoc :caveat caveat) + (seq bound) (assoc :caveat-context bound) + (some? valid-until-ms) (assoc :valid-until-ms valid-until-ms))))) (defn- relationship-key [{:keys [subject relation resource]}] @@ -40,8 +72,9 @@ (vector? (nth op 3 nil))) (nth (nth op 3) 1 nil))) -(defn validate-batch! - "Rejects different operations on one resolved logical relationship. +(defn coalesce-updates + "Rejects different intents on one resolved logical relationship and keeps + one identical update before native allocation or transaction planning. Every backend plans a batch from one immutable calculation snapshot, so repeating an operation has the same outcome as one occurrence and may @@ -50,25 +83,41 @@ statement-order visibility can make DataScript or Datahike choose a different result. Reject them before any transaction is submitted." [updates] - (reduce - (fn [operations-by-relationship - {:keys [operation relationship]}] - (let [key (relationship-key relationship)] - (if-let [previous (get operations-by-relationship key)] - (if (= previous operation) - operations-by-relationship - (throw - (ex-info - "A relationship mutation batch contains conflicting operations for one relationship." - {:type :eacl/invalid-relationship-update-batch - :eacl/error :eacl/invalid-relationship-update-batch - :reason :conflicting-operations - :operations [previous operation]}))) - (assoc operations-by-relationship key operation)))) - {} - updates) + (:updates + (reduce + (fn [{:keys [seen] :as state} {:keys [operation relationship] :as entry}] + (let [key (relationship-key relationship) + intent [operation (when-not (= :delete operation) (select-keys relationship qualifier-keys)) + (:prepared-qualifier entry)]] + (if-let [previous (get seen key)] + (if (= previous intent) + state + (throw + (ex-info + "A relationship mutation batch contains conflicting updates for one relationship." + {:type :eacl/invalid-relationship-update-batch + :eacl/error :eacl/invalid-relationship-update-batch + :reason (if (= (first previous) operation) :conflicting-qualifiers :conflicting-operations) + :operations [(first previous) operation]}))) + (-> state (assoc-in [:seen key] intent) (update :updates conj entry))))) + {:seen {} :updates []} + updates))) + +(defn validate-batch! + "Compatibility predicate for already normalized/resolved update batches." + [updates] + (coalesce-updates updates) true) +(defn normalize-updates + "Normalizes and coalesces public input before any inert qualifier allocation." + [updates] + (coalesce-updates + (mapv (fn [{:keys [operation relationship] :as update}] + (validate-operation! operation) + (assoc update :relationship (normalize-relationship relationship))) + updates))) + (defn stamp-relation-generations "Adds one idempotent backend-native generation stamp per affected relation. @@ -102,3 +151,11 @@ (keep (partial relationship-operation-relation-id operations))) sort vec)) + +(defn canonical-qualifier-metadata? + "Validates closed canonical Relationship metadata at cache ingress." + [relationship] + (try + (= (select-keys relationship qualifier-keys) + (select-keys (normalize-relationship relationship) qualifier-keys)) + (catch #?(:clj Exception :cljs :default) _ false))) diff --git a/modules/eacl/src/eacl/relationships/qualifier.cljc b/modules/eacl/src/eacl/relationships/qualifier.cljc index 0fc18cbf..a11c02ff 100644 --- a/modules/eacl/src/eacl/relationships/qualifier.cljc +++ b/modules/eacl/src/eacl/relationships/qualifier.cljc @@ -77,10 +77,16 @@ (contains? entity caveat-attribute) (assoc :caveat (get entity caveat-attribute)) (contains? entity expiration-attribute) (assoc :valid-until-ms (get entity expiration-attribute)) (contains? entity context-attribute) (assoc :caveat-context context)) - normalized (normalize input parameters)] + normalized (normalize input parameters) + canonical (cond-> {marker-attribute format-version} + (some? (:caveat normalized)) (assoc caveat-attribute (:caveat normalized)) + (seq (:caveat-context normalized)) (assoc context-attribute (get entity context-attribute)) + (some? (:valid-until-ms normalized)) (assoc expiration-attribute (:valid-until-ms normalized)))] (when-not normalized (error! :empty-qualifier)) (when (and (contains? entity context-attribute) (empty? context)) (error! :nonsparse-context)) - (when-not (= (dissoc entity :db/id) - (dissoc (entity-data (:db/id entity) normalized parameters) :db/id)) + ;; decode-context already checked the original payload's canonical bytes + ;; and types. Preserve those bytes when checking sparse field presence; + ;; rebuilding an entity would normalize and encode the same context again. + (when-not (= (dissoc entity :db/id) canonical) (error! :noncanonical-qualifier)) normalized)) diff --git a/modules/eacl/src/eacl/relationships/staged.cljc b/modules/eacl/src/eacl/relationships/staged.cljc index c507dbcf..0ad2ee8e 100644 --- a/modules/eacl/src/eacl/relationships/staged.cljc +++ b/modules/eacl/src/eacl/relationships/staged.cljc @@ -13,23 +13,49 @@ (throw (ex-info "Invalid staged qualified Relationship operation." {:type :eacl.qualifier/staged-write :eacl/error :eacl.qualifier/staged-write :reason reason}))) +(defn prepared-contention? + "Recognizes native CAS contention and a changed cleanup source through + bounded host wrappers. Other staged validation failures remain terminal." + [error] + (loop [cause error depth 0] + (when (and cause (< depth 16)) + (let [data (ex-data cause)] + (if (or (= :transact/cas (:error data)) + (and (= :eacl.qualifier/staged-write (:type data)) + (= :cleanup-source-changed (:reason data)))) + true + (recur (ex-cause cause) (inc depth))))))) + (defrecord NativeWriter [native identity source]) -(deftype PreparedQualifier [writer-id relationship qid value facts generation]) +(defrecord NativePlanner [native source]) +(deftype PreparedQualifier [writer-id source relationship qid value facts generation]) + +(defn prepared? [value] (instance? PreparedQualifier value)) + +(defn planner + "Constructs a pure snapshot planner from a backend's read/transaction-data + functions. No connection, source selector, or submission function is retained." + [native db] + (when (some #(contains? native %) [:snapshot :with-snapshot :transact!]) + (error! :mutable-planner)) + (let [source ((:source native) db)] + (when-not source (error! :missing-source-identity)) + (->NativePlanner native source))) (defn native-writer "Backend implementation boundary. The factory supplies native, certified allocation, identity reads, commit-time assertions, and atomic fences." [native] (when-not (and (contains? #{:datomic :datascript :datahike :datalevin} (:backend native)) - (= (:strategy native) (get {:datomic :inline :datalevin :inline - :datascript :prepared :datahike :prepared} (:backend native))) - (every? #(ifn? (get native %)) - [:snapshot :source :entity :facts :rows :generation :fence :assert-entity :tempid :transact!])) + (= (:strategy native) (get {:datomic :inline :datalevin :inline + :datascript :prepared :datahike :prepared} (:backend native))) + (every? #(ifn? (get native %)) + [:snapshot :source :entity :facts :rows :generation :fence :assert-entity :tempid :transact!])) (error! :unsupported-backend)) (let [source ((:source native) ((:snapshot native)))] (when-not source (error! :missing-source-identity)) (->NativeWriter (assoc native :with-snapshot - (or (:with-snapshot native) (fn [f] (f ((:snapshot native)))))) + (or (:with-snapshot native) (fn [f] (f ((:snapshot native)))))) (random-uuid) source))) (defn capability [writer] @@ -52,18 +78,18 @@ (defn- selected-relation [native db identity] (when-not (and (vector? identity) (= 5 (count identity)) - (keyword? (nth identity 0)) (keyword? (nth identity 3)) - (every? concrete-eid? (map #(nth identity %) [1 2 4]))) + (keyword? (nth identity 0)) (keyword? (nth identity 3)) + (every? concrete-eid? (map #(nth identity %) [1 2 4]))) (error! :relationship-identity)) (let [[subject-type subject-id relation-id resource-type resource-id] identity entity (:entity native) relation (entity db relation-id)] (when-not (and (seq (dissoc (entity db subject-id) :db/id)) - (seq (dissoc (entity db resource-id) :db/id))) + (seq (dissoc (entity db resource-id) :db/id))) (error! :missing-endpoint)) (when-not (and (= subject-type (:eacl.relation/subject-type relation)) - (= resource-type (:eacl.relation/resource-type relation)) - (keyword? (:eacl.relation/relation-name relation))) + (= resource-type (:eacl.relation/resource-type relation)) + (keyword? (:eacl.relation/relation-name relation))) (error! :missing-relation)) relation)) @@ -91,13 +117,18 @@ b (vec (take 2 (rows db resource-id storage/reverse-attribute reverse)))] (cond (and (empty? a) (empty? b)) {:present? false} - (not= 1 (count a) (count b)) (error! :asymmetric-or-duplicate-relationship) + (or (> (count a) 1) (> (count b) 1)) (error! :asymmetric-or-duplicate-relationship) :else - (let [av (:v (first a)) bv (:v (first b)) qid (nth av 4 nil)] - (when-not (and (pair/endpoint-value? av) (pair/endpoint-value? bv) - (= [av bv] (values-for identity qid))) + (let [av (:v (first a)) bv (:v (first b)) qid (nth (or av bv) 4 nil) + [expected-a expected-b] (values-for identity qid)] + ;; Preserve ordinary one-sided repair. A qualified half-pair has lost + ;; its atomic ownership proof and remains a corruption fault. + (when (and qid (or (nil? av) (nil? bv))) (error! :asymmetric-or-duplicate-relationship)) - {:present? true :qid qid})))) + (when-not (and (or (nil? av) (and (pair/endpoint-value? av) (= av expected-a))) + (or (nil? bv) (and (pair/endpoint-value? bv) (= bv expected-b)))) + (error! :asymmetric-or-duplicate-relationship)) + {:present? true :qid qid :forward? (some? av) :reverse? (some? bv)})))) (defn- stored-qualifier [native db qid] (when qid @@ -123,7 +154,7 @@ [(qualifier/entity-data tempid value parameters)]))}))) {:keys [value parameters tempid generation tx-data]} prepared] (if-not value - (PreparedQualifier. (:identity writer) relationship nil nil [] generation) + (PreparedQualifier. (:identity writer) (:source writer) relationship nil nil [] generation) (let [report ((:transact! native) tx-data) qid (get (:tempids report) tempid) generation (if-let [after-reference (:generation-after-reference native)] @@ -134,13 +165,15 @@ (assert-source! writer db) (when-not (= value (qualifier/decode ((:entity native) db qid) parameters)) (error! :prepared-qualifier-changed)) - (PreparedQualifier. (:identity writer) relationship qid value + (PreparedQualifier. (:identity writer) (:source writer) relationship qid value ((:facts native) db qid) generation))))))) (defn- prepared-value [writer db relationship prepared require-generation?] (let [native (:native writer)] - (when-not (and (= (:identity writer) (.-writer-id ^PreparedQualifier prepared)) - (= relationship (.-relationship ^PreparedQualifier prepared))) + (when-not (and (= (:source writer) (.-source ^PreparedQualifier prepared)) + (or (instance? NativePlanner writer) + (= (:identity writer) (.-writer-id ^PreparedQualifier prepared))) + (= relationship (.-relationship ^PreparedQualifier prepared))) (error! :prepared-owner-mismatch)) (when (and require-generation? (not= ((:generation native) db) (.-generation ^PreparedQualifier prepared))) (error! :prepared-schema-changed)) @@ -156,7 +189,9 @@ (= "eacl" (namespace attribute)) (some-> (namespace attribute) (str/starts-with? "eacl.")))) -(defn- application-datoms [datoms reserved-qids] +(defn application-datoms + "Admits caller composition without allowing mutations of EACL-owned state." + [datoms reserved-qids] (when-not (vector? datoms) (error! :application-datoms)) (doseq [op datoms] (when-not (or (map? op) (vector? op)) (error! :application-datoms)) @@ -164,59 +199,256 @@ eid (if map-op? (:db/id op) (second op)) attrs (if map-op? (keys (dissoc op :db/id)) [(nth op 2 nil)])] (when-not (and (some? eid) (not (contains? reserved-qids eid)) - (or map-op? (and (vector? op) - (or (and (#{:db/add :db/retract} (first op)) (= 4 (count op))) - (and (= :db.fn/cas (first op)) (= 5 (count op)))))) - (not-any? protected-attribute? attrs)) + (or map-op? (and (vector? op) + (or (and (#{:db/add :db/retract} (first op)) (= 4 (count op))) + (and (= :db.fn/cas (first op)) (= 5 (count op)))))) + (not-any? protected-attribute? attrs)) (error! :application-datoms)))) + ;; Native ref-valued attributes may expand nested entity maps into writes. + ;; Validate those maps too; a harmless-looking application attribute cannot + ;; smuggle a qualifier edit or a new EACL entity into the publication tx. + (doseq [node (tree-seq coll? seq datoms) + :when (map? node)] + (when (or (contains? reserved-qids (:db/id node)) + (some #(and (keyword? %) (protected-attribute? %)) (keys (dissoc node :db/id)))) + (error! :application-datoms))) datoms) +(defn- plan-entry + [writer db {:keys [operation relationship value expected-value] :as entry} app-datoms include-fences?] + (when-not (contains? #{:create :replace :touch :delete} operation) (error! :operation)) + (let [native (:native writer) + _ (assert-source! writer db) + relation (selected-relation native db relationship) + prior (stored-pair native db relationship) + operation (if (= :touch operation) (if (:present? prior) :replace :create) operation) + old-qid (:qid prior) + _ (stored-qualifier native db old-qid) + _ (when (or (and (= :create operation) (:present? prior)) + (and (= :replace operation) (not (:present? prior)))) + (error! :relationship-conflict)) + prepared? (instance? PreparedQualifier value) + prepared (when prepared? (prepared-value writer db relationship value true)) + semantic (when-not (= :delete operation) + (admitted-value native db relation (if prepared? (:value prepared) value))) + _ (when (and (contains? entry :expected-value) + (not= semantic (admitted-value native db relation expected-value))) + (error! :prepared-value-mismatch)) + _ (when (and (= :prepared (:strategy native)) semantic (not prepared?)) + (error! :prepared-qualifier-required)) + inline? (and semantic (not prepared?)) + qid (when semantic (if inline? (temporary-id native) (:qid prepared))) + _ (when (and qid (= qid old-qid)) (error! :qualifier-reuse)) + _ (when (and prepared? semantic (not (concrete-eid? qid))) (error! :unresolved-qualifier)) + app-datoms (application-datoms app-datoms (set (remove nil? [qid old-qid]))) + [subject-type subject-id relation-id resource-type resource-id] relationship + [forward reverse] (values-for relationship qid) + ;; Datomic rejects retract/add of an identical tuple in one tx. A + ;; plain-to-plain replacement still fences and stamps the Relation + ;; and commits application datoms, while retaining its existing pair. + retain-pair? (and (= :replace operation) (nil? old-qid) (nil? qid)) + tx (vec + (concat + (when include-fences? + ((:fence native) db relation-id (boolean (and inline? (:caveat semantic))))) + (when (and prepared? qid) [((:assert-entity native) qid (:facts prepared))]) + (when old-qid [((:assert-entity native) old-qid ((:facts native) db old-qid))]) + (when inline? [(qualifier/entity-data qid semantic (parameters native db (:caveat semantic)))]) + (when (and (:present? prior) (not retain-pair?)) + (pair/retractions subject-type subject-id relation-id resource-type resource-id old-qid)) + (when old-qid [[:db/retractEntity old-qid]]) + (when (and (not= :delete operation) (or (not retain-pair?) (not (:forward? prior)))) + [[:db/add subject-id storage/forward-attribute forward]]) + (when (and (not= :delete operation) (or (not retain-pair?) (not (:reverse? prior)))) + [[:db/add resource-id storage/reverse-attribute reverse]]) + app-datoms))] + {:tx-data tx :operation operation :relationship relationship :qualifier-eid qid + :old-qualifier-eid old-qid :adds-caveat-reference? (boolean (and inline? (:caveat semantic)))})) + (defn plan - "Plans one native create/replace/delete plus application datoms. Prepared - backends require a handle from prepare!; certified inline backends allocate - inside this transaction. Every published half shares one resolved qid." + "Plans one native create/replace/touch/delete plus application datoms. + Prepared backends require a handle; inline backends allocate in this tx." ([writer db operation relationship value] (plan writer db operation relationship value [])) ([writer db operation relationship value app-datoms] - (when-not (contains? #{:create :replace :delete} operation) (error! :operation)) + (select-keys (plan-entry writer db {:operation operation :relationship relationship :value value} app-datoms true) + [:tx-data :operation :relationship :qualifier-eid]))) + +(defn- unique-batch! [entries] + (when-not (vector? entries) (error! :batch-shape)) + (when-not (= (count entries) (count (set (map :relationship entries)))) + (error! :duplicate-batch-identity)) + (doseq [{:keys [operation]} entries] + (when-not (#{:create :replace :touch :delete} operation) (error! :operation))) + entries) + +(defn- batch-fences! [native] + (when-not (and (ifn? (:schema-fence native)) (ifn? (:relation-fence native))) + (error! :unsupported-batch-fences))) + +(defn- unique-qualifiers! [qids] + (let [qids (remove nil? qids)] + (when-not (= (count qids) (count (set qids))) + (error! :qualifier-reuse)))) + +(defn prepare-batch! + "Admits the whole batch before allocating inert qualifiers in one native + transaction. Inline writers retain semantic values for final publication." + ([writer entries] (prepare-batch! writer entries false)) + ([writer entries force-prepared?] + (unique-batch! entries) (let [native (:native writer) - _ (assert-source! writer db) - relation (selected-relation native db relationship) - prior (stored-pair native db relationship) - old-qid (:qid prior) - _ (stored-qualifier native db old-qid) - _ (when (if (= :create operation) (:present? prior) (not (:present? prior))) - (error! :relationship-conflict)) - prepared? (instance? PreparedQualifier value) - prepared (when prepared? (prepared-value writer db relationship value true)) - semantic (when-not (= :delete operation) - (admitted-value native db relation (if prepared? (:value prepared) value))) - _ (when (and (= :prepared (:strategy native)) semantic (not prepared?)) - (error! :prepared-qualifier-required)) - inline? (and semantic (not prepared?)) - qid (when semantic (if inline? (temporary-id native) (:qid prepared))) - _ (when (and qid (= qid old-qid)) (error! :qualifier-reuse)) - _ (when (and prepared? semantic (not (concrete-eid? qid))) (error! :unresolved-qualifier)) - app-datoms (application-datoms app-datoms (set (remove nil? [qid old-qid]))) - [subject-type subject-id relation-id resource-type resource-id] relationship - [forward reverse] (values-for relationship qid) - ;; Datomic rejects retract/add of an identical tuple in one tx. A - ;; plain-to-plain replacement still fences and stamps the Relation - ;; and commits application datoms, while retaining its existing pair. - retain-pair? (and (= :replace operation) (nil? old-qid) (nil? qid)) - tx (vec - (concat - ((:fence native) db relation-id (boolean (and inline? (:caveat semantic)))) - (when (and prepared? qid) [((:assert-entity native) qid (:facts prepared))]) - (when old-qid [((:assert-entity native) old-qid ((:facts native) db old-qid))]) - (when inline? [(qualifier/entity-data qid semantic (parameters native db (:caveat semantic)))]) - (when (and (:present? prior) (not retain-pair?)) - (pair/retractions subject-type subject-id relation-id resource-type resource-id old-qid)) - (when old-qid [[:db/retractEntity old-qid]]) - (when-not (or (= :delete operation) retain-pair?) - [[:db/add subject-id storage/forward-attribute forward] - [:db/add resource-id storage/reverse-attribute reverse]]) - app-datoms))] - {:tx-data tx :operation operation :relationship relationship :qualifier-eid qid}))) + _ (batch-fences! native) + prepared? + (or force-prepared? (= :prepared (:strategy native))) + admitted + ((:with-snapshot native) + (fn [db] + (assert-source! writer db) + (let [items + (mapv (fn [{:keys [operation relationship value schema-generation] :as entry}] + (when (and (contains? entry :schema-generation) + (not= schema-generation ((:generation native) db))) + (error! :prepared-schema-changed)) + (let [relation (selected-relation native db relationship) + prior (stored-pair native db relationship) + _ (stored-qualifier native db (:qid prior)) + _ (when (or (and (= :create operation) (:present? prior)) + (and (= :replace operation) (not (:present? prior)))) + (error! :relationship-conflict)) + semantic (when-not (= :delete operation) (admitted-value native db relation value)) + tempid (when (and prepared? semantic) (temporary-id native))] + {:entry (assoc entry :value semantic) :tempid tempid + :parameters (when semantic (parameters native db (:caveat semantic)))})) + entries) + ids (keep :tempid items) + _ (when-not (= (count ids) (count (set ids))) (error! :duplicate-temporary-id)) + facts (into [] (keep (fn [{:keys [entry tempid parameters]}] + (when tempid (qualifier/entity-data tempid (:value entry) parameters)))) items)] + {:items items :generation ((:generation native) db) + :tx-data (when (seq facts) + (into ((:schema-fence native) db (boolean (some #(get-in % [:entry :value :caveat]) items))) + facts))}))) + {:keys [items generation tx-data]} admitted] + (if-not tx-data + (mapv :entry items) + (let [report ((:transact! native) tx-data) + _ (unique-qualifiers! (map #(get (:tempids report) (:tempid %)) items)) + generation (if-let [after-reference (:generation-after-reference native)] + (after-reference report) generation)] + ((:with-snapshot native) + (fn [db] + (assert-source! writer db) + (mapv (fn [{:keys [entry tempid parameters]}] + (if-not tempid + (assoc entry :schema-generation generation) + (let [qid (get (:tempids report) tempid) + value (:value entry)] + (when-not (concrete-eid? qid) (error! :unresolved-qualifier)) + (when-not (= value (qualifier/decode ((:entity native) db qid) parameters)) + (error! :prepared-qualifier-changed)) + (assoc entry :schema-generation generation :value + (PreparedQualifier. (:identity writer) (:source writer) (:relationship entry) qid value + ((:facts native) db qid) generation))))) + items)))))))) + +(defn plan-batch + "One schema fence and one Relation fence per affected identity partition. + All endpoint swaps and application datoms share the final atomic commit." + [writer db entries app-datoms] + (unique-batch! entries) + (let [native (:native writer) + _ (batch-fences! native) + plans (mapv (fn [{:keys [schema-generation] :as entry}] + (when (and (contains? entry :schema-generation) + (not= schema-generation ((:generation native) db))) + (error! :prepared-schema-changed)) + (plan-entry writer db entry [] false)) entries) + _ (unique-qualifiers! (mapcat (juxt :qualifier-eid :old-qualifier-eid) plans)) + qids (into #{} (remove nil?) (mapcat (juxt :qualifier-eid :old-qualifier-eid) plans)) + app-datoms (application-datoms app-datoms qids) + changed? (or (seq app-datoms) (some (comp seq :tx-data) plans)) + relations (when changed? (sort (set (map #(nth (:relationship %) 2) plans))))] + {:tx-data (vec (concat (when (and changed? (seq entries)) + ((:schema-fence native) db (boolean (some :adds-caveat-reference? plans)))) + (mapcat #((:relation-fence native) db %) relations) + (when changed? (distinct (mapcat :identity-guards entries))) + (mapcat :tx-data plans) + app-datoms)) + :plans (mapv #(dissoc % :tx-data :old-qualifier-eid :adds-caveat-reference?) plans)})) + +(defn relationship-retraction? + "Selects physical endpoint retractions from an adapter's deletion stream." + [op] + (and (vector? op) (= :db/retract (first op)) + (contains? storage/attributes (nth op 2 nil)))) + +(defn- retraction-identity [op] + (when-not (and (relationship-retraction? op) (= 4 (count op))) + (error! :relationship-retraction)) + (let [[_ owner attribute value] op + decoded ((if (= storage/forward-attribute attribute) pair/decode-forward pair/decode-reverse) owner value)] + (when-not decoded (error! :relationship-retraction)) + (let [identity (mapv decoded [:subject-type :subject-eid :relation-eid :resource-type :resource-eid]) + qid (:qualifier-eid decoded)] + (when-not (and (every? concrete-eid? (map #(nth identity %) [1 2 4])) + (or (nil? qid) (concrete-eid? qid))) + (error! :relationship-retraction)) + [identity qid]))) + +(defn- plan-native-retractions + "Expands a bounded slice of object-deletion halves to whole exact pairs. + Cleanup permits an already missing endpoint/peer, but never a conflicting + qualifier or duplicate logical identity. A native basis assertion and + schema/Relation fences protect the same transaction as qualifier removal." + [writer db retractions] + (assert-source! writer db) + (let [native (:native writer) + entries (vec (distinct (map retraction-identity retractions))) + identities (mapv first entries) + _ (when-not (= (count identities) (count (set identities))) + (error! :asymmetric-or-duplicate-relationship)) + _ (unique-qualifiers! (keep second entries)) + plans + (mapv (fn [[identity qid]] + (let [[st subject rid rt resource] identity + relation ((:entity native) db rid) + [forward reverse] (values-for identity qid)] + (when-not (and (= st (:eacl.relation/subject-type relation)) + (= rt (:eacl.relation/resource-type relation)) + (keyword? (:eacl.relation/relation-name relation))) + (error! :missing-relation)) + (let [halves [[subject storage/forward-attribute forward] + [resource storage/reverse-attribute reverse]] + actuals (mapv (fn [[owner attribute value]] + (vec (take 2 ((:rows native) db owner attribute value)))) halves)] + (when (every? empty? actuals) (error! :missing-relationship)) + (doseq [[[_ _ value] actual] (map vector halves actuals)] + (when-not (or (empty? actual) (= [value] (mapv :v actual))) + (error! :asymmetric-or-duplicate-relationship)))) + (stored-qualifier native db qid) + (vec (concat + (when qid [((:assert-entity native) qid ((:facts native) db qid))]) + (pair/retractions st subject rid rt resource qid) + (when qid [[:db/retractEntity qid]]))))) + entries) + relations (sort (set (map #(nth % 2) identities)))] + (if (empty? entries) + [] + (vec (concat [((:head-guard native) db)] + ((:schema-fence native) db false) + (mapcat #((:relation-fence native) db %) relations) + (mapcat identity plans)))))) + +(defn plan-retraction-batch + "Plans pair and qualifier cleanup within the caller's selected native basis. + Snapshot-owning backends unwrap that same selection for native reads." + [writer selected retractions] + (if-let [with-selected (get-in writer [:native :with-selected-snapshot])] + (with-selected selected #(plan-native-retractions writer % retractions)) + (plan-native-retractions writer selected retractions))) + +(defn plan-batch-current [writer entries app-datoms] + ((get-in writer [:native :with-snapshot]) #(plan-batch writer % entries app-datoms))) (defn plan-current "Plans against one owned native read snapshot and releases it before the diff --git a/modules/eacl/src/eacl/relationships/storage.cljc b/modules/eacl/src/eacl/relationships/storage.cljc index 1b6573d9..2ad0742e 100644 --- a/modules/eacl/src/eacl/relationships/storage.cljc +++ b/modules/eacl/src/eacl/relationships/storage.cljc @@ -9,7 +9,7 @@ (def identity-arity 4) (def tuple-types [:db.type/keyword :db.type/ref :db.type/keyword :db.type/ref :db.type/ref]) -(def qualifier-capability :nil-only) +(def qualifier-capability :qualified) (def format-id :eacl.relationship/endpoint-pair-v9-qualifier-ref) (def forward-attribute diff --git a/modules/eacl/src/eacl/relay.cljc b/modules/eacl/src/eacl/relay.cljc index a2acbe53..b7dd96d7 100644 --- a/modules/eacl/src/eacl/relay.cljc +++ b/modules/eacl/src/eacl/relay.cljc @@ -1,10 +1,15 @@ (ns eacl.relay "Portable opaque Relay cursor handling for synchronous v8 adapters." (:require [eacl.backend.source :as source] + [eacl.authorization.result :as authorization-result] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.temporal :as temporal] [eacl.backend.v8 :as backend] [eacl.cache :as cache] [eacl.consistency :as consistency] [eacl.core :as eacl :refer [spice-object]] + [eacl.relationships.mutations :as relationship-mutations] [eacl.cursor :as cursor] [eacl.execution :as execution] [eacl.proof-frame :as proof-frame] @@ -55,7 +60,7 @@ Change this value whenever edge identity, dependency-closure construction, or traversal boundary semantics change. Exact database identity alone does not make a boundary produced by a different evaluator ABI composable." - {:version 2 + {:version 3 :envelope 13 :edge-identity :external-object-id-v1 :dependency-context :relation-closure-v1 @@ -163,6 +168,14 @@ [query] (plain-page-query (normalized-cursor-query query))) +(defn temporal-mode [opts] + (if (= :snapshot (:authorization-target-kind opts)) :pinned :live)) + +(defn- qualification-scope [opts] + (when-let [request (:qualification opts)] + (let [[format _ _ context evaluator] (qualification/exact-reuse-identity request)] + [format context evaluator (temporal-mode opts) backend/adapter-version]))) + (defn- cursor-scope "Digest of immutable operation/query/principal/configuration identity. @@ -187,10 +200,8 @@ (get-in opts [:execution-contract :aggregate-limits]) :page-demand (select-keys query [:first :last]))) scope-input - [cursor-continuation-semantic-abi - operation - execution-scope - scoped-query]] + (cond-> [cursor-continuation-semantic-abi operation execution-scope scoped-query] + (:qualification opts) (conj [:qualification (qualification-scope opts)]))] (cursor/memoized-context! (or (:cursor-codec-cache opts) (:cursor-construction-cache opts)) @@ -330,7 +341,7 @@ (defn- request-dependency-context [adapter opts] - (if-let [relation-ids (request-relation-ids adapter opts)] + (if-let [relation-ids (when-not (:qualification opts) (request-relation-ids adapter opts))] (let [candidate (or (:request-proof-frame opts) (some-> (:request-proof-frame-delay opts) force)) frame @@ -449,7 +460,10 @@ envelope (:cursor decoded) _ (execution/check! (:execution-contract opts) :cursor-decoded)] (when-not (and (= 13 (:v envelope)) - (map? (:edge envelope))) + (map? (:edge envelope)) + (if (:qualification opts) + (temporal/cursor-certificate-valid? (:qualification-temporal envelope)) + (not (contains? envelope :qualification-temporal)))) (invalid-cursor! "Invalid Relay cursor envelope." {:reason :invalid-envelope} nil)) @@ -659,8 +673,21 @@ :cursor-graph 0 :exact nil})) +(defn- ensure-qualified-time! [opts envelope] + (when-let [request (:qualification opts)] + (let [certificate (:qualification-temporal envelope) + time (:time request)] + (when-not (and (= (temporal-mode opts) (:mode certificate)) + (temporal/cursor-time-valid? certificate time)) + (throw (ex-info "This cursor no longer describes the requested temporal view. Start a new lookup without the cursor." + {:type :eacl.pagination/restart-required :eacl/error :eacl.pagination/restart-required + :reason (if (and (= :live (:mode certificate)) (not (:complete? certificate)) + (not= time (:start-ms certificate))) + :temporal-certificate-incomplete :temporal-interval-exceeded)})))))) + (defn- ensure-cursor-satisfies-request! [opts envelope] + (ensure-qualified-time! opts envelope) (when-let [floor (:cursor-freshness-floor opts)] (let [cursor-order (get-in envelope [:native-revision :revision]) floor-order (:revision floor)] @@ -948,6 +975,15 @@ {:adapter page-adapter :selected-snapshot selected :continuation-context (:continuation-context page-context) + :qualification-certificate + (reduce (fn [prior [_ envelope]] + (if-let [certificate (:qualification-temporal envelope)] + (if prior + (-> prior + (update :valid-until-ms evidence/meet (:valid-until-ms certificate)) + (update :complete? #(and % (:complete? certificate)))) + certificate) + prior)) nil envelopes) ;; A token can carry an expiry even when the receiving client's ;; current minting policy has no TTL. Such a request must never become ;; a pre-decode transport hit after that authenticated input expires. @@ -984,13 +1020,32 @@ (contains? query :before) (update :before #(decode-page-edge adapter opts operation query %)))) +(defn- outgoing-qualified-certificate [opts operation query page] + (when-let [request (:qualification opts)] + (let [time (:time request) + ;; Stored physical inspection has no time-dependent denotation. + ;; Other paths must supply a certificate covering their retained work. + current (or (:page-qualification-certificate opts) (:qualification-certificate page) + (temporal/interval time nil + (and (= :read-relationships operation) + (nil? (:authorization query)) + (not= :expiry-active (:relationship-state query))))) + prior (:cursor-qualification-certificate opts)] + (when-not (and (temporal/interval-valid? current) + (temporal/reusable? current time true)) + (throw (ex-info "Invalid qualification interval on a completed page." + {:type :eacl.pagination/invalid-certificate :eacl/error :eacl.pagination/invalid-certificate}))) + (temporal/cursor-certificate (temporal-mode opts) time prior current)))) + (defn- externalize-page-cursors [adapter opts operation query page] (let [context (delay - (or (::retained-cursor-dependency-context opts) - (:cursor-dependency-context opts) - (request-dependency-context adapter opts))) + (cond-> (or (::retained-cursor-dependency-context opts) + (:cursor-dependency-context opts) + (request-dependency-context adapter opts)) + (:qualification opts) + (assoc :qualification-temporal (outgoing-qualified-certificate opts operation query page)))) scope (delay (or (::retained-cursor-scope opts) @@ -1003,6 +1058,7 @@ (encode-page-edge adapter opts @scope @context edge))))] (-> page + (dissoc :qualification-certificate) (update-in [:page-info :start-cursor] encode-edge) (update-in [:page-info :end-cursor] encode-edge)))) @@ -1045,7 +1101,8 @@ (defn externalize-page [adapter opts operation query page] - (let [objects (:data page) + (let [detailed? (= :detailed (authorization-result/result-policy query)) + objects (if detailed? (mapv :object (:data page)) (:data page)) identities (resolve-external-identities! adapter opts operation (map :id objects)) @@ -1083,6 +1140,7 @@ (externalize-page-cursors adapter (assoc opts + :page-qualification-certificate (:qualification-certificate page) ::retained-cursor-dependency-context context ::retained-cursor-scope @@ -1091,9 +1149,11 @@ operation query {:data (mapv - (fn [{:keys [type id]}] - (spice-object type (get identities id))) - objects) + (fn [item] + (let [{:keys [type id]} (if detailed? (:object item) item) + object (spice-object type (get identities id))] + (if detailed? (assoc item :object object) object))) + (:data page)) :page-info page-info})] (execution/check! (:execution-contract opts) :rendered-page-return) public-page)) @@ -1115,15 +1175,11 @@ page :data (mapv - (fn [{:keys [subject relation resource]}] - (eacl/->Relationship - (eacl/->SpiceObject - (:type subject) - (get identities (:id subject)) - (:relation subject)) - relation - (eacl/->SpiceObject - (:type resource) - (get identities (:id resource)) - (:relation resource)))) + (fn [{:keys [subject relation resource] :as relationship}] + (merge + (eacl/->Relationship + (eacl/->SpiceObject (:type subject) (get identities (:id subject)) (:relation subject)) + relation + (eacl/->SpiceObject (:type resource) (get identities (:id resource)) (:relation resource))) + (select-keys relationship relationship-mutations/qualifier-keys))) relationships))))) diff --git a/modules/eacl/src/eacl/request/context.cljc b/modules/eacl/src/eacl/request/context.cljc index 84ce0845..13a14b65 100644 --- a/modules/eacl/src/eacl/request/context.cljc +++ b/modules/eacl/src/eacl/request/context.cljc @@ -3,7 +3,9 @@ The context owns snapshot cleanup and request-invariant state. It is never a portable value and must not escape its constructing thread or request." - (:require [eacl.backend.source :as source] + (:require [eacl.authorization.context :as caveat-context] + [eacl.authorization.qualification :as qualification] + [eacl.backend.source :as source] [eacl.backend.v8 :as backend] [eacl.cache.derived-schema :as derived-schema] [eacl.engine.v8 :as engine] @@ -19,6 +21,8 @@ :selected-snapshot :basis-identity :contract + :caveat-context + :qualification-options :derived-registry :counter-ledger :proof-diagnostic-fn @@ -194,7 +198,7 @@ them; they share one schema-generation resolution." [{:keys [runtime adapter selected-snapshot basis-identity contract derived-registry counter-ledger proof-diagnostic-fn - maximum-proof-relation-count] + maximum-proof-relation-count caveat-context qualification-options] :as input}] (let [selected (when (and (map? input) @@ -229,6 +233,8 @@ (when-not (map? contract) (invalid-context! "Request context requires one execution contract." {:contract contract})) + (when (and (some? caveat-context) (not (caveat-context/prepared? caveat-context))) + (invalid-context! "Request Caveat context must be prepared before selecting a basis." {})) (let [selected-adapter (when selected (source/adapter selected)) @@ -251,6 +257,10 @@ {:basis-identity basis-identity :selected-basis-identity selected-identity})) lineage (lineage-for-validated-basis basis-identity) + qualification + (when qualification-options + (qualification/request-from-adapter + adapter (assoc qualification-options :basis basis-identity))) registry (or derived-registry (:derived-schema-caches runtime) @@ -294,6 +304,8 @@ :lineage lineage :schema-generation-delay schema-generation-delay :contract contract + :caveat-context caveat-context + :qualification qualification :proof-frame-delay proof-frame-delay :derived-delay derived-delay :memos-delay (delay (atom {})) @@ -387,11 +399,13 @@ ledger (:counter-ledger state) contract (:contract state)] (if (identical? ledger counters/*ledger*) - (binding [execution/*contract* contract] + (binding [execution/*contract* contract + engine/*qualification* (:qualification state)] (f context)) (counters/call-with-ledger ledger - #(binding [execution/*contract* contract] + #(binding [execution/*contract* contract + engine/*qualification* (:qualification state)] (f context)))))) (defn close! diff --git a/modules/eacl/src/eacl/schema/expression_resolver.cljc b/modules/eacl/src/eacl/schema/expression_resolver.cljc index b1c3de6f..e1787542 100644 --- a/modules/eacl/src/eacl/schema/expression_resolver.cljc +++ b/modules/eacl/src/eacl/schema/expression_resolver.cljc @@ -21,7 +21,7 @@ (into {} (for [[relation-name type-refs] relations] [(keyword relation-name) - (mapv (comp keyword :type) type-refs)])) + (vec (distinct (map (comp keyword :type) type-refs)))])) :permissions (set (map (comp keyword :name) permissions))}]))) (defn- issue @@ -52,10 +52,10 @@ (doseq [[resource-type {:keys [relations]}] (sort-by key definitions) [relation-name type-refs] (sort-by key relations) :let [_ (expression-limits/check-dimension! - :type-partition-count - :maximum-type-partitions - (count type-refs) - limits)] + :type-partition-count + :maximum-type-partitions + (count (distinct (map :type type-refs))) + limits)] {:keys [type]} (sort-by :type type-refs) :let [resource-type (keyword resource-type) relation-name (keyword relation-name) @@ -290,28 +290,32 @@ ([parse-tree] (resolve-parse-tree parse-tree {})) ([parse-tree limits] + (resolve-parse-tree parse-tree limits {})) + ([parse-tree limits admission] (let [transformed (parser/transform-schema parse-tree) - _ (parser/validate-eacl-restrictions parse-tree transformed) + _ (parser/validate-eacl-restrictions parse-tree transformed admission) relations - (vec - (for [[resource-type {:keys [relations]}] - (sort-by key (:definitions transformed)) - [relation-name type-refs] (sort-by key relations) - {:keys [type]} (sort-by :type type-refs)] - (model/Relation (keyword resource-type) - (keyword relation-name) - (keyword type)))) + (if (true? (:allow-caveats? admission)) + (parser/staged-relation-entities transformed) + (vec + (for [[resource-type {:keys [relations]}] + (sort-by key (:definitions transformed)) + [relation-name type-refs] (sort-by key relations) + {:keys [type]} (sort-by :type type-refs)] + (model/Relation (keyword resource-type) + (keyword relation-name) + (keyword type))))) {:keys [expressions metadata aggregate-metrics]} (resolve-definitions-with-metadata (:definitions transformed) limits) dependency-certificate (expression-graph/build-certificate expressions)] (cond-> {:definitions (mapv (comp keyword key) - (sort-by key (:definitions transformed))) - :expressions expressions - :relations relations - :expression-metadata metadata - :aggregate-expression-metrics aggregate-metrics - :dependency-certificate dependency-certificate} + (sort-by key (:definitions transformed))) + :expressions expressions + :relations relations + :expression-metadata metadata + :aggregate-expression-metrics aggregate-metrics + :dependency-certificate dependency-certificate} (seq (:caveats transformed)) (assoc :caveats (:caveats transformed)))))) (defn validate-schema @@ -321,6 +325,8 @@ ([schema-source] (validate-schema schema-source nil)) ([schema-source expression-limits] + (validate-schema schema-source expression-limits {})) + ([schema-source expression-limits admission] (let [expression-limits (expression-policy/normalize-client-limits expression-limits)] (assoc @@ -329,5 +335,5 @@ schema-source (select-keys expression-limits (keys expression-policy/schema-limits))) - expression-limits) + expression-limits admission) :expression-limits expression-limits)))) diff --git a/modules/eacl/src/eacl/schema/qualification_admission.cljc b/modules/eacl/src/eacl/schema/qualification_admission.cljc new file mode 100644 index 00000000..db075aa4 --- /dev/null +++ b/modules/eacl/src/eacl/schema/qualification_admission.cljc @@ -0,0 +1,35 @@ +(ns eacl.schema.qualification-admission + "Schema-level qualification admission, outside the per-edge fast path." + (:require [eacl.caveats.evaluator :as evaluator] + [eacl.relationships.qualifier :as qualifier] + [eacl.relationships.storage :as storage] + [eacl.schema.relation-allowance :as allowance])) + +(defn publication-descriptor + "The certified native publication contract. A backend advertises it only + for topologies that can execute its atomic inline or prepared-ref writer." + [strategy] + {:version 1 :strategy strategy :storage-version storage/version + :qualifier-format qualifier/format-version + :obligations #{:atomic-endpoint-pair :fresh-qualifier-reference + :relation-generation :identity-preconditions + :old-qualifier-cleanup :application-composition}}) + +(defn require-publication! [descriptor] + (when-not (and (#{:inline :prepared} (:strategy descriptor)) + (= descriptor (publication-descriptor (:strategy descriptor)))) + (throw (ex-info "Qualified authorization requires a certified native publication strategy." + {:type :eacl/unsupported-capability + :eacl/error :eacl/unsupported-capability + :capability :qualified-relationship-publication}))) + descriptor) + +(defn schema! + "Requires a matching evaluator for every Caveated Relation, including empty + and unvisited Relations. Unused definitions and expiry-only schemas do not + require an evaluator. This reads no program source and compiles nothing." + [schema implementation publication] + (require-publication! publication) + (when (some #(some some? (allowance/names %)) (:relations schema)) + (evaluator/require-matching! implementation evaluator/profile-fingerprint)) + schema) diff --git a/modules/eacl/src/eacl/schema/relation_allowance.cljc b/modules/eacl/src/eacl/schema/relation_allowance.cljc new file mode 100644 index 00000000..57799897 --- /dev/null +++ b/modules/eacl/src/eacl/schema/relation_allowance.cljc @@ -0,0 +1,124 @@ +(ns eacl.schema.relation-allowance + "Named Relation alternatives at schema admission and replacement boundaries." + (:require [clojure.set :as set] + [eacl.caveats.values :as values] + [eacl.caveats.definition :as definition] + [eacl.relationships.endpoint-pair :as pair] + [eacl.relationships.qualifier :as qualifier] + [eacl.relationships.storage :as storage])) + +(def attributes #{:eacl.relation/caveats :eacl.relation/allows-unqualified?}) + +(defn- invalid! [] + (throw (ex-info "Malformed Relation Caveat alternatives." + {:type :eacl.schema/invalid-relation-allowance + :eacl/error :eacl.schema/invalid-relation-allowance}))) + +(defn names + "Validates canonical schema alternatives and returns names plus optional nil." + [relation] + (let [refs? (contains? relation :eacl.relation/caveats) + plain? (contains? relation :eacl.relation/allows-unqualified?) + refs (:eacl.relation/caveats relation) + plain (:eacl.relation/allows-unqualified? relation)] + (when (or (not= refs? plain?) + (and refs? (not (and (boolean? plain) (vector? refs) (seq refs) + (every? #(and (vector? %) (= 2 (count %)) + (= :eacl.caveat/name (first %)) + (values/parameter-name? (second %))) refs) + (= refs (vec (sort (distinct refs)))))))) + (invalid!)) + (cond-> (set (map second refs)) (or (not refs?) plain) (conj nil)))) + +(defn canonicalize + "Converts a native pull's named Caveat entities into portable lookup refs." + [relation] + (let [result + (if (contains? relation :eacl.relation/caveats) + (update relation :eacl.relation/caveats + (fn [entities] + (let [names (mapv :eacl.caveat/name entities)] + (when-not (and (seq names) (every? values/parameter-name? names) + (= (count names) (count (set names)))) + (invalid!)) + (mapv #(vector :eacl.caveat/name %) (sort names))))) + relation)] + (names result) + result)) + +(defn changes [{:keys [additions retractions]}] + (let [before (into {} (map (juxt :eacl/id identity)) retractions)] + (into [] (keep (fn [after] + (when-let [prior (get before (:eacl/id after))] + {:before prior :after after}))) + (sort-by :eacl/id additions)))) + +(defn entity-deletions + "Only removed identities are entity retractions; allowance updates keep eids." + [{:keys [additions retractions]}] + (let [retained (set (map :eacl/id additions))] + (remove #(contains? retained (:eacl/id %)) retractions))) + +(defn attribute-retractions + "Removes replaced optional facts without retracting the retained Relation." + [deltas] + (vec + (mapcat + (fn [{:keys [before after]}] + (let [owner [:eacl/id (:eacl/id after)] + removed (set/difference (set (:eacl.relation/caveats before)) + (set (:eacl.relation/caveats after)))] + (concat + (map #(vector :db/retract owner :eacl.relation/caveats %) (sort removed)) + (when (and (contains? before :eacl.relation/allows-unqualified?) + (not (contains? after :eacl.relation/allows-unqualified?))) + [[:db/retract owner :eacl.relation/allows-unqualified? (:eacl.relation/allows-unqualified? before)]])))) + (changes deltas)))) + +(defn validate-existing! + "The native callback validates both stored streams and returns each retained + Relationship's Caveat name or nil. Expiry never removes stored identity." + [deltas referenced-caveats] + (doseq [{:keys [before after]} (changes deltas) + :let [allowed (names after)] + caveat (referenced-caveats before)] + (when-not (contains? allowed caveat) + (throw (ex-info "Relation alternatives would invalidate a stored Relationship." + {:type :eacl.schema/relationship-qualifier-in-use + :eacl/error :eacl.schema/relationship-qualifier-in-use + :relation (:eacl/id after) :caveat caveat})))) + true) + +(defn stored-caveats + "Reads only the changed Relation's two endpoint streams at one owned basis. + Every retained pair and non-nil qualifier is checked before an allowance + update can make that data authoritative under a new schema." + [{:keys [entid entity rows scan]} relation] + (let [rid (entid [:eacl/id (:eacl/id relation)]) + st (:eacl.relation/subject-type relation) + rt (:eacl.relation/resource-type relation) + allowed (names relation)] + (when rid + (mapcat + (fn [[attribute decode prefix]] + (map + (fn [datom] + (let [decoded (decode (:e datom) (:v datom)) + {:keys [subject-type subject-eid relation-eid resource-type resource-eid qualifier-eid]} decoded + forward (pair/forward-value st rid rt resource-eid qualifier-eid) + reverse (pair/reverse-value rt rid st subject-eid qualifier-eid)] + (when-not (and decoded (= st subject-type) (= rt resource-type) (= rid relation-eid) + (= [forward] (mapv :v (take 2 (rows subject-eid storage/forward-attribute forward)))) + (= [reverse] (mapv :v (take 2 (rows resource-eid storage/reverse-attribute reverse))))) + (qualifier/error! :asymmetric-or-duplicate-relationship)) + (let [value (when qualifier-eid (entity qualifier-eid)) + named (when-let [caveat (get value qualifier/caveat-attribute)] + (definition/decode-header (entity caveat))) + _ (when qualifier-eid (qualifier/decode value (:parameters named))) + caveat (:name named)] + (when-not (contains? allowed caveat) + (qualifier/error! :caveat-not-allowed)) + caveat))) + (scan attribute prefix))) + [[storage/forward-attribute pair/decode-forward [st rid rt]] + [storage/reverse-attribute pair/decode-reverse [rt rid st]]])))) diff --git a/modules/eacl/src/eacl/schema/replacement_plan.cljc b/modules/eacl/src/eacl/schema/replacement_plan.cljc index b06758bb..a2996edf 100644 --- a/modules/eacl/src/eacl/schema/replacement_plan.cljc +++ b/modules/eacl/src/eacl/schema/replacement_plan.cljc @@ -4,7 +4,8 @@ Native adapters add storage transaction forms and committed concurrency guards, but both committed and speculative replacement consume this exact semantic certificate." - (:require [clojure.set :as set])) + (:require [clojure.set :as set] + [eacl.schema.relation-allowance :as relation-allowance])) (def orphan-policies #{:error :retain-inert}) @@ -53,7 +54,7 @@ :option :orphan-policy :value orphan-policy :allowed orphan-policies}))) - (let [relation-retractions (get-in deltas [:relations :retractions]) + (let [relation-retractions (relation-allowance/entity-deletions (:relations deltas)) relationship-effects (changed-coordinates relation-coordinate (:relations deltas)) schema-components diff --git a/modules/eacl/src/eacl/secure_format.cljc b/modules/eacl/src/eacl/secure_format.cljc index 93c8795a..6e67d653 100644 --- a/modules/eacl/src/eacl/secure_format.cljc +++ b/modules/eacl/src/eacl/secure_format.cljc @@ -44,25 +44,34 @@ #?(:clj (int (.charAt ^String value index)) :cljs (.charCodeAt value index))) -(defn- well-formed-unicode? +(defn- unicode-utf8-size + "Counts UTF-8 bytes directly from UTF-16, rejecting unpaired surrogates." [value] - (loop [index 0] - (if (= index (count value)) - true - (let [code (string-code-unit-at value index)] - (cond - (<= 0xD800 code 0xDBFF) - (and (< (inc index) (count value)) - (<= 0xDC00 - (string-code-unit-at value (inc index)) - 0xDFFF) - (recur (+ index 2))) - - (<= 0xDC00 code 0xDFFF) - false - - :else - (recur (inc index))))))) + (let [length (count value)] + (loop [index 0 size 0] + (if (= index length) + size + (let [code (long (string-code-unit-at value index))] + (cond + (< code 0x80) (recur (inc index) (inc size)) + (< code 0x800) (recur (inc index) (+ size 2)) + (< code 0xD800) (recur (inc index) (+ size 3)) + (<= code 0xDBFF) + (when (< (inc index) length) + (let [next-code (long (string-code-unit-at value (inc index)))] + (when (and (<= 0xDC00 next-code) (<= next-code 0xDFFF)) + (recur (+ index 2) (+ size 4))))) + (<= code 0xDFFF) nil + :else (recur (inc index) (+ size 3)))))))) + +(defn- well-formed-unicode? [value] + (some? (unicode-utf8-size value))) + +(defn utf8-size + "Returns the exact UTF-8 byte count without allocating a byte collection." + [value] + (or (unicode-utf8-size (str value)) + (format-error! :invalid-unicode {}))) (defn- hidden-reader-input? [value] diff --git a/modules/eacl/src/eacl/spicedb/parser.cljc b/modules/eacl/src/eacl/spicedb/parser.cljc index 5a2abeb6..d419d943 100644 --- a/modules/eacl/src/eacl/spicedb/parser.cljc +++ b/modules/eacl/src/eacl/spicedb/parser.cljc @@ -413,7 +413,7 @@ (defn- collect-relation-issues "Check relations for EACL compatibility issues. Takes the transformed schema definitions map." - [definitions] + [definitions allow-caveats?] (let [issues (atom [])] (doseq [[res-type {:keys [relations]}] definitions [rel-name type-refs] relations @@ -421,31 +421,31 @@ ;; Check for wildcards (when (:wildcard? type-ref) (swap! issues conj - {:type :wildcard-relation - :resource-type res-type - :relation rel-name - :message (str "Unsupported feature: Wildcard relation '" (:type type-ref) ":*' in " - res-type "/" rel-name ". EACL does not support public/wildcard access.")})) + {:type :wildcard-relation + :resource-type res-type + :relation rel-name + :message (str "Unsupported feature: Wildcard relation '" (:type type-ref) ":*' in " + res-type "/" rel-name ". EACL does not support public/wildcard access.")})) ;; Check for subject relations (when (:subject-relation type-ref) (swap! issues conj - {:type :subject-relation - :resource-type res-type - :relation rel-name - :subject-relation (:subject-relation type-ref) - :message (str "Unsupported feature: Subject relation '" (:type type-ref) "#" (:subject-relation type-ref) - "' in " res-type "/" rel-name ". EACL does not support nested subject relations.")})) + {:type :subject-relation + :resource-type res-type + :relation rel-name + :subject-relation (:subject-relation type-ref) + :message (str "Unsupported feature: Subject relation '" (:type type-ref) "#" (:subject-relation type-ref) + "' in " res-type "/" rel-name ". EACL does not support nested subject relations.")})) ;; Check for caveats - (when (:caveat type-ref) + (when (and (:caveat type-ref) (not allow-caveats?)) (swap! issues conj - {:type :caveat - :resource-type res-type - :relation rel-name - :caveat (:caveat type-ref) - :message (str "Unsupported feature: Caveat 'with " (:caveat type-ref) "' in " - res-type "/" rel-name ". EACL does not support conditional access via caveats.")}))) + {:type :caveat + :resource-type res-type + :relation rel-name + :caveat (:caveat type-ref) + :message (str "Unsupported feature: Caveat 'with " (:caveat type-ref) "' in " + res-type "/" rel-name ". EACL does not support conditional access via caveats.")}))) @issues)) (defn validate-eacl-restrictions @@ -461,25 +461,27 @@ - No namespaced type paths (docs/document) - No wildcards (user:*) - No subject relations (group#member) - - No caveats (with caveatname) + - Caveated branches require explicit qualified schema admission Returns nil if valid, throws ex-info with :issues vector if invalid." - [parse-tree transformed-schema] - (let [parse-issues (collect-parse-tree-issues parse-tree) - relation-issues (collect-relation-issues (:definitions transformed-schema)) - all-issues (vec (concat parse-issues relation-issues))] - (when (seq all-issues) - (let [first-msg (:message (first all-issues)) - total (count all-issues) - summary (if (= 1 total) - first-msg - (str first-msg " (and " (dec total) " more issue(s))"))] - (throw (ex-info summary - {:type :eacl.schema/unsupported-feature - :eacl/error :eacl.schema/unsupported-feature - :issues all-issues - :issue-count total})))) - nil)) + ([parse-tree transformed-schema] + (validate-eacl-restrictions parse-tree transformed-schema {})) + ([parse-tree transformed-schema {:keys [allow-caveats?]}] + (let [parse-issues (collect-parse-tree-issues parse-tree) + relation-issues (collect-relation-issues (:definitions transformed-schema) (true? allow-caveats?)) + all-issues (vec (concat parse-issues relation-issues))] + (when (seq all-issues) + (let [first-msg (:message (first all-issues)) + total (count all-issues) + summary (if (= 1 total) + first-msg + (str first-msg " (and " (dec total) " more issue(s))"))] + (throw (ex-info summary + {:type :eacl.schema/unsupported-feature + :eacl/error :eacl.schema/unsupported-feature + :issues all-issues + :issue-count total})))) + nil))) ;; ============================================================================ ;; Permission Expression Transformation diff --git a/modules/eacl/src/eacl/subproblem_cache.cljc b/modules/eacl/src/eacl/subproblem_cache.cljc index 1662455f..1c5d709a 100644 --- a/modules/eacl/src/eacl/subproblem_cache.cljc +++ b/modules/eacl/src/eacl/subproblem_cache.cljc @@ -47,7 +47,7 @@ #{:denotation-max-entries :answer-max-entries :telemetry?}) -(def ^:private publication-option-keys #{:valid?}) +(def ^:private publication-option-keys #{:valid? :replace?}) (def snapshot-format "Version identifier for flat process-neutral subproblem snapshots." @@ -264,6 +264,8 @@ (when-not (ifn? (:valid? options)) (invalid-config! "Subproblem cache publication :valid? must be callable." {:valid? (:valid? options)})) + (when (and (contains? options :replace?) (not (ifn? (:replace? options)))) + (invalid-config! "Subproblem replacement requires an explicit predicate." {:key :replace?})) options) (defn- valid-value? @@ -411,7 +413,8 @@ "Publishes one already-computed value under an opaque storage key. Validation and page eligibility run once before the absent-key cache - insertion. A concurrent same-key publisher wins, while this request + insertion. An optional replacement predicate runs outside atomic scopes and + replaces only the same expected immutable mapping. Otherwise a concurrent same-key publisher wins, while this request still returns its own completed value." [store tier storage-key options value] (validate-tier! store tier) @@ -443,9 +446,16 @@ (if-let [final-rejection (request-publication-rejection)] (reject-publication! store final-rejection) (try - (let [published? - (lru/put-if-absent! (get (:tiers store) tier) - storage-key value)] + (let [tier-store (get (:tiers store) tier) + published? + (or (lru/put-if-absent! tier-store storage-key value) + (when-let [replace? (:replace? options)] + (let [prior (lru/peek-entry tier-store storage-key)] + ;; Compare eligibility outside the atomic data replacement. + ;; A concurrent publisher changes the expected identity. + (and (:found? prior) + (replace? (:value prior) value) + (lru/replace-if! tier-store storage-key (:value prior) value)))))] (if published? (do (record-metrics! store update :puts inc) diff --git a/modules/eacl/test/eacl/authorization/context_test.cljc b/modules/eacl/test/eacl/authorization/context_test.cljc new file mode 100644 index 00000000..5e032688 --- /dev/null +++ b/modules/eacl/test/eacl/authorization/context_test.cljc @@ -0,0 +1,52 @@ +(ns eacl.authorization.context-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.context :as context] + [eacl.caveats.values :as values])) + +(defn error-data [f] + (try (f) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error (ex-data error)))) + +(deftest whole-context-is-canonical-and-independent-of-one-parameter-set + (let [input {"flag" true "count" 7 "at" [:timestamp 99] + "names" ["Zoë" "Alice"] "roles" {"admin" false} "empty" []} + prepared (context/prepare input) + reversed (context/prepare (into (sorted-map-by #(compare %2 %1)) input))] + (is (= input (context/value prepared))) + (is (= (context/identity prepared) (context/identity reversed))) + (is (= {"flag" true} (context/project prepared [["flag" :bool] ["missing" :int]]))) + (is (not= (context/identity prepared) + (context/identity (context/prepare (assoc input "unused" false)))))) + (let [input (into {} (map (fn [n] [(str "field" n) true]) (range 64)))] + (is (= input (context/value (context/prepare input))))) + (is (identical? (context/prepare {}) (context/prepare {})))) + +(def invalid-contexts + [nil + [] + {"unused" nil} + {:unused true} + {"true" true} + {"unused" 1.5} + {"unused" 9007199254740992} + {"unused" #{1}} + {"unused" '(1 2)} + {"unused" [true 1]} + {"unused" {1 true}} + {"unused" {"nested" [1]}} + {"unused" [:timestamp 253402300800000]} + {"unused" (apply str (repeat 4097 "a"))} + {"unused" (apply str (repeat 2049 "é"))} + {"unused" (vec (range 129))} + (into {} (map (fn [n] [(str "field" n) (vec (range 128))]) (range 8))) + (into {} (map (fn [n] [(str "field" n) (apply str (repeat 4096 "a"))]) (range 4)))]) + +(deftest invalid-unused-fields-cannot-evade-request-admission + (doseq [input invalid-contexts] + (let [data (error-data #(context/prepare input))] + (is (= :eacl.caveat/invalid (:type data))) + (is (not (contains? data :value))))) + (is (= :unknown-parameter + (:reason (error-data #(values/merge-context [["flag" :bool]] {"unused" true} {}))))) + (is (= :unknown-parameter + (:reason (error-data #(values/merge-context [["flag" :bool]] {} {"unused" true})))))) diff --git a/modules/eacl/test/eacl/authorization/data_test.cljc b/modules/eacl/test/eacl/authorization/data_test.cljc new file mode 100644 index 00000000..6d9a0925 --- /dev/null +++ b/modules/eacl/test/eacl/authorization/data_test.cljc @@ -0,0 +1,53 @@ +(ns eacl.authorization.data-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.data :as data] + [eacl.execution :as execution] + [eacl.relationships.qualifier :as qualifier] + [eacl.request.counters :as counters])) + +(defn error-data [f] + (try (f) nil (catch #?(:clj Throwable :cljs :default) error (ex-data error)))) + +(defn row [attribute value] + {:e 10 :a attribute :v value :tx 7}) + +(deftest bounded-data-preserves-unknown-fields-and-same-read-version + (let [rows [(row qualifier/marker-attribute 1) (row qualifier/expiration-attribute 100) + (row :unexpected/field "retained")] + ledger (counters/make-ledger) + packet (counters/call-with-ledger ledger #(data/collect 10 rows identity true))] + (is (= {:entity {:db/id 10 qualifier/marker-attribute 1 qualifier/expiration-attribute 100 + :unexpected/field "retained"} :version 7 :fact-count 3} packet)) + (is (= :qualifier-unknown-field + (:reason (error-data #(qualifier/decode (:entity packet) []))))) + (is (= 3 (:fetched-values (counters/snapshot ledger)))) + (is (nil? (:version (data/collect 10 rows identity false)))) + (is (= {:entity nil :version nil :fact-count 0} (data/collect 10 [] identity true))))) + +(deftest qualified-data-keeps-cardinality-and-entity-boundaries + (is (= #{20 30} (get-in (data/collect 10 [(row :eacl.relation/caveats 20) + (row :eacl.relation/caveats 30)] identity true) + [:entity :eacl.relation/caveats]))) + (is (= :nonfunctional-qualification-entity + (:reason (error-data #(data/collect 10 [(row qualifier/marker-attribute 1) + (row qualifier/marker-attribute 2)] identity true))))) + (is (= :qualification-entity-bound + (:reason (error-data #(data/collect 11 [(row qualifier/marker-attribute 1)] identity true))))) + (is (= :qualification-entity-id + (:reason (error-data #(data/collect 0 [] identity true)))))) + +(deftest qualified-data-limits-and-cancellation-stop-decoding + (let [ledger (counters/make-ledger)] + (with-redefs [data/maximum-entity-facts 2] + (is (= :qualification-entity-limit + (:reason (error-data + #(counters/call-with-ledger + ledger (fn [] (data/collect 10 (repeat (row :unexpected/field 1)) identity true)))))))) + (is (= 3 (:fetched-values (counters/snapshot ledger))))) + (let [token (execution/cancellation-token) + contract (execution/normalize {} :check-permission {:cancellation-token token})] + (execution/cancel! token) + (binding [execution/*contract* contract] + (is (= :eacl.execution/cancelled + (:type (error-data #(data/collect 10 [(row qualifier/marker-attribute 1)] + (fn [_] (throw (ex-info "Must not decode after cancellation" {}))) true)))))))) diff --git a/modules/eacl/test/eacl/authorization/evidence_index_test.cljc b/modules/eacl/test/eacl/authorization/evidence_index_test.cljc new file mode 100644 index 00000000..b59385b0 --- /dev/null +++ b/modules/eacl/test/eacl/authorization/evidence_index_test.cljc @@ -0,0 +1,48 @@ +(ns eacl.authorization.evidence-index-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [clojure.set :as set] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.evidence-index :as index] + [eacl.authorization.evidence-test :as fixtures])) + +(defn denotation [op values] + (reduce (case op :union set/union :intersection set/intersection) + (if (= op :union) #{} #{0 1 2 3}) + (map #(fixtures/completions % [fixtures/x fixtures/y]) values))) + +(deftest incremental-joins-refine-independent-completion-sets + (doseq [op [:union :intersection] width (range 36)] + (let [values (mapv #(nth [false fixtures/x fixtures/y true] (mod % 4)) (range width)) + built (index/build op values (fn []))] + (is (= (denotation op values) (fixtures/completions (index/value built) [fixtures/x fixtures/y]))) + (loop [slot 0 values values tree built] + (when (< slot width) + (let [replacement (nth [fixtures/y false true fixtures/x] (mod slot 4)) + values (assoc values slot replacement) + tree (index/replace-slot tree slot replacement (fn []))] + (is (= (denotation op values) (fixtures/completions (index/value tree) [fixtures/x fixtures/y]))) + (recur (inc slot) values tree))))))) + +(deftest updates-retain-hidden-leaves-and-charge-only-an-ancestor-path + (let [work (atom 0) step! #(swap! work inc) + built (index/build :union (into [true] (repeat 63 false)) step!) + hidden (index/replace-slot built 63 fixtures/x step!)] + (is (true? (index/value hidden))) + (reset! work 0) + (let [revealed (index/replace-slot hidden 0 false step!)] + (is (= fixtures/x (index/value revealed))) + (is (<= @work 6))) + (reset! work 0) + (is (identical? hidden (index/replace-slot hidden 63 fixtures/x step!))) + (is (zero? @work)))) + +(deftest faults-and-certificates-survive-incremental-joins + (let [fault (evidence/fault :eacl.qualifier/invalid :missing-qualifier) + grant (evidence/with-certificate true 100 true) + tree (index/build :intersection [grant fixtures/x true] (fn [])) + result (index/value tree)] + (is (= :conditional-permission (evidence/permissionship result))) + (is (= 100 (evidence/valid-until result))) + (is (evidence/fault? (index/value (index/replace-slot tree 2 fault (fn []))))) + (is (not (evidence/complete? + (index/value (index/replace-slot tree 2 (evidence/with-certificate true nil false) (fn [])))))))) diff --git a/modules/eacl/test/eacl/authorization/evidence_test.cljc b/modules/eacl/test/eacl/authorization/evidence_test.cljc new file mode 100644 index 00000000..85980ca2 --- /dev/null +++ b/modules/eacl/test/eacl/authorization/evidence_test.cljc @@ -0,0 +1,99 @@ +(ns eacl.authorization.evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.evidence :as e] + [eacl.caveats.values :as values])) + +(def x (e/conditional ["c" :x] ["x"])) +(def y (e/conditional ["c" :y] ["y"])) +(def z (e/conditional ["c" :z] ["z"])) + +(defn error-data [f] + (try (f) nil + (catch #?(:clj Throwable :cljs :default) error (ex-data error)))) + +(defn evaluate-node + "Test-only Boolean completion interpreter, independent of BDD application." + [node assignment] + (if (boolean? node) node + (evaluate-node (nth node (if (get assignment (ffirst node)) 2 1)) assignment))) + +(defn completions [evidence atoms] + (set (for [world (range (bit-shift-left 1 (count atoms))) + :let [assignment (into {} (map-indexed (fn [i atom] [(ffirst (e/value atom)) (bit-test world i)]) atoms))] + :when (evaluate-node (e/value evidence) assignment)] world))) + +(deftest conditional-algebra-is-canonical-and-correlated + (is (true? (e/combine :union x (e/combine :exclusion true x)))) + (is (false? (e/combine :intersection x (e/combine :exclusion true x)))) + (doseq [op [:union :intersection :arrow]] + (is (= x (e/combine op x x))) + (is (= (e/combine op x y) (e/combine op y x)))) + (is (= (e/combine :union x (e/combine :union y z)) + (e/combine :union (e/combine :union z x) y))) + (is (= x (e/combine :union x (e/combine :intersection x y)))) + (is (= ["x" "y"] (e/missing-fields (e/combine :exclusion x y)))) + (is (= [] (e/missing-fields (e/combine :union x (e/combine :exclusion true x)))))) + +(deftest temporal-certificate-uses-decisive-evidence + (let [grant (e/with-certificate true 100 true) + other (e/with-certificate y 110 true) + ban (e/combine :exclusion true grant)] + (is (= :no-permission (e/permissionship ban))) + (is (= 100 (e/valid-until ban))) + (is (e/reusable? ban 90 99)) + (is (not (e/reusable? ban 90 100))) + (is (not (e/reusable? ban 90 89))) + (is (true? (e/combine :union true other))) + (is (= 100 (e/valid-until (e/combine :intersection grant other)))) + (is (false? (e/combine :intersection false other))) + (is (not (e/complete? (e/combine :intersection (e/with-certificate x 100 false) other)))) + (is (true? (e/complete? (e/combine :union true (e/with-certificate x 100 false))))))) + +(deftest authoritative-faults-never-become-boolean-absence + (let [fault (e/fault :eacl.qualifier/invalid :missing-qualifier)] + (doseq [op [:union :intersection :exclusion :arrow] other [true false x]] + (is (e/fault? (e/combine op fault other))) + (is (e/fault? (e/combine op other fault)))) + (is (not (e/has? fault))) + (is (not (e/reusable? fault 0 0))) + (is (= :evaluation-failure (e/permissionship fault))))) + +(deftest canonical-bounded-wire-round-trips + (doseq [evidence [true false x y + (e/combine :exclusion x y) + (e/with-certificate x 100 true) + (e/with-certificate false nil false) + (e/fault :eacl.qualifier/invalid :missing-qualifier)]] + (let [payload (e/encode evidence)] + (is (= evidence (e/decode payload))) + (is (= payload (e/encode (e/decode payload)))))) + (let [wire (fn [node] (values/encode-bounded [:eacl.authorization/evidence 1 node nil true] + {:maximum-size 65536 :maximum-depth 80 :maximum-entries 16384})) + atom (first (e/value x)) + invalid-nodes [nil [] [1 false true] [atom false false] + [atom [atom false true] true] + [:fault []] [:fault [["private exception" :reason]]]]] + (doseq [node invalid-nodes] + (is (some? (:eacl/error (error-data #(e/decode (wire node)))))) + (is (some? (:eacl/error (error-data #(e/encode (e/->Evidence node nil true)))))))) + (is (= :certificate (:reason (error-data #(e/encode (e/->Evidence true 1.5 true)))))) + (is (= :certificate (:reason (error-data #(e/with-certificate x 1.5 true))))) + (is (= :missing-fields (:reason (error-data #(e/conditional ["c"] [])))))) + +(deftest residual-work-and-depth-have-hard-bounds + (with-redefs [e/limits (assoc e/limits :work 1)] + (is (= :work-limit (:reason (error-data #(e/combine :union x y)))))) + (with-redefs [e/limits (assoc e/limits :nodes 1)] + (is (= :node-limit (:reason (error-data #(e/combine :union x false)))))) + (with-redefs [e/limits (assoc e/limits :depth 0)] + (is (= :depth-limit (:reason (error-data #(e/combine :intersection x y))))))) + +(deftest definite-certificates-retain-the-canonical-wire-format + (doseq [value [false true] + end [nil -62135596800000 -1 0 100 253402300799999] + complete? [false true]] + (let [proof (e/with-certificate value end complete?) + expected (values/encode-bounded [:eacl.authorization/evidence e/format-version value end complete?] + {:maximum-size 4194304 :maximum-entries 16384 :maximum-depth 80})] + (is (= expected (e/encode proof))) + (is (= proof (e/decode expected)))))) diff --git a/modules/eacl/test/eacl/authorization/inspection_test.cljc b/modules/eacl/test/eacl/authorization/inspection_test.cljc new file mode 100644 index 00000000..39e9c816 --- /dev/null +++ b/modules/eacl/test/eacl/authorization/inspection_test.cljc @@ -0,0 +1,31 @@ +(ns eacl.authorization.inspection-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as fixtures] + [eacl.caveats.plan :as plan] + [eacl.relationships.inspection :as inspection])) + +(deftest stored-inspection-never-evaluates-or-compiles-a-caveat + (doseq [time [99 100 101]] + (let [calls (atom 0) + reads (atom {}) + request (fixtures/request {:time time :calls calls :reads reads :evaluator nil})] + (with-redefs [plan/compile-plan (fn [& _] (throw (ex-info "Unexpected compilation" {})))] + (is (= {:caveat "enabled" :valid-until-ms 100} + (qualification/inspect request 1 3))) + (is (= {:caveat "enabled" :caveat-context {"flag" true} :valid-until-ms 100} + (qualification/inspect request 1 4))) + (is (= {:valid-until-ms 100} (qualification/inspect request 1 5))) + (is (= {} (qualification/inspect request nil nil))) + (is (= (if (< time 100) :active :expired) + (if (inspection/active? time (qualification/inspect request 1 3)) :active :expired)))) + (is (zero? @calls)) + (is (= {1 1, 2 1, 3 1, 4 1, 5 1} @reads))))) + +(deftest expiry-inspection-is-independent-of-caveat-outcomes + (doseq [context [{} {"flag" false} {"flag" true}] + time [99 100 101]] + (is (= (< time 100) + (inspection/active? time {:caveat "enabled" :caveat-context context :valid-until-ms 100})))) + (is (inspection/active? 100 {})) + (is (inspection/active? 100 {:caveat "enabled"}))) diff --git a/modules/eacl/test/eacl/authorization/qualification_test.cljc b/modules/eacl/test/eacl/authorization/qualification_test.cljc new file mode 100644 index 00000000..91d0b43c --- /dev/null +++ b/modules/eacl/test/eacl/authorization/qualification_test.cljc @@ -0,0 +1,289 @@ +(ns eacl.authorization.qualification-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.clock :as clock] + [eacl.authorization.data :as data] + [eacl.authorization.batch :as batch] + [eacl.backend.v8 :as backend] + [eacl.request.counters :as counters] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as q] + [eacl.cache.standard-lru :as lru] + [eacl.caveats.definition :as definition] + [eacl.caveats.evaluator :as evaluator] + [eacl.caveats.partial :as partial] + [eacl.caveats.plan :as plan] + [eacl.caveats.values :as values] + [eacl.execution :as execution] + [eacl.relationships.qualifier :as qualifier])) + +(def parameters [["flag" :bool]]) +(def named (definition/entity "enabled" parameters "flag")) +(def fixture + {1 {:db/id 1 :eacl.relation/caveats #{2} :eacl.relation/allows-unqualified? true} + 2 (assoc named :db/id 2) + 3 (qualifier/entity-data 3 {:caveat 2 :valid-until-ms 100} parameters) + 4 (qualifier/entity-data 4 {:caveat 2 :caveat-context {"flag" true} :valid-until-ms 100} parameters) + 5 (qualifier/entity-data 5 {:valid-until-ms 100} [])}) + +(defn portable-evaluator [calls] + (reify evaluator/Evaluator + (descriptor [_] {:profile values/profile-id :profile-fingerprint evaluator/profile-fingerprint + :capability-version 1 :fingerprint "test/portable-qualified-v1"}) + (-evaluate [_ entity request bound] + (swap! calls inc) + (let [{:keys [parameters plan]} (definition/decode-entity entity)] + (partial/evaluate parameters plan request (or bound {})))))) + +(defn request + ([] (request {})) + ([{:keys [db reads calls] :as options}] + (let [read-entity (fn [eid] + (when reads (swap! reads update eid (fnil inc 0))) + (get (or db fixture) eid)) + defaults {:time 99 :context {} + :evaluator (portable-evaluator (or calls (atom 0))) + :entity read-entity :version (constantly 7) + :basis {:source "s" :lifecycle "l" :revision 1}}] + (q/request (merge defaults (dissoc options :db :reads :calls)))))) + +(defn data-adapter [db reads] + (backend/make-adapter + {:id :qualification-test :runtime-guards? true + :capabilities {:qualification #{data/capability} + :qualified-publication #{:atomic-prepared-v1}} + :operations + (assoc (zipmap backend/required-snapshot-operations (repeat (constantly nil))) + :qualification-data + (fn [eid] + (swap! reads update eid (fnil inc 0)) + (data/collect + eid + (for [[attribute value] (dissoc (get db eid) :db/id) + item (if (set? value) value [value])] + {:e eid :a attribute :v item :tx 7}) + identity true)))})) + +(defn data-request [db reads] + (q/request-from-adapter + (data-adapter db reads) + {:time 99 :basis {:source "native-data" :revision 1} + :evaluator (portable-evaluator (atom 0))})) + +(deftest adapter-data-is-shared-and-metered-within-one-request + (let [reads (atom {}) ledger (counters/make-ledger) request (data-request fixture reads)] + (counters/call-with-ledger + ledger + (fn [] + (is (= {} @reads)) + (is (true? (q/qualify request 1 10))) + (is (every? zero? (vals (counters/snapshot ledger)))) + (is (= :conditional-permission (evidence/permissionship (q/qualify request 1 [10 3])))) + (let [before (counters/snapshot ledger)] + (is (= :conditional-permission (evidence/permissionship (q/qualify request 1 [11 3])))) + (is (= before (counters/snapshot ledger)))) + (is (= {1 1, 2 1, 3 1} @reads)) + (is (= 3 (:commands (counters/snapshot ledger)))) + (is (= 3 (:adapter-reads (counters/snapshot ledger)))) + (is (= (reduce + (map #(count (dissoc (get fixture %) :db/id)) [1 2 3])) + (:fetched-values (counters/snapshot ledger)))))))) + +(deftest adapter-data-faults-remain-visible-and-do-not-refetch + (doseq [db [(assoc-in fixture [3 :unknown/field] "private value") + (assoc-in fixture [3 qualifier/caveat-attribute] 3) + (dissoc fixture 3)]] + (let [reads (atom {}) request (data-request db reads)] + (is (evidence/fault? (q/qualify request 1 [10 3]))) + (let [before @reads] + (is (evidence/fault? (q/qualify request 1 [11 3]))) + (is (= before @reads))) + (is (= 1 (get @reads 3))))) + (let [reads (atom {}) request (data-request fixture reads) + token (execution/cancellation-token) + contract (execution/normalize {} :check-permission {:cancellation-token token})] + (execution/cancel! token) + (binding [execution/*contract* contract] + (is (= :eacl.execution/cancelled + (try (q/qualify request 1 [10 3]) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (:type (ex-data error))))))) + (is (= {} @reads)))) + +(deftest qualifier-data-consumes-aggregate-command-and-fact-budgets + (let [request (data-request fixture (atom {})) ledger (counters/make-ledger) + before (counters/snapshot ledger)] + (counters/call-with-ledger ledger #(q/qualify request 1 [10 3])) + (let [after (counters/snapshot ledger) + ;; Data resolution has no traversal queue or observer event. Its + ;; physical work still belongs to the public operation's budget. + consumed (batch/aggregate-counters {} {} before after 1)] + (is (= 3 (:commands consumed))) + (is (= (:fetched-values after) (:fetched-values consumed))) + (is (= (+ 3 (:fetched-values after) 1) (:allocation-proxy consumed))) + (doseq [[limit maximum kind] [[:max-commands 2 :commands] + [:max-fetched-values 1 :fetched-values] + [:max-allocation-proxy 3 :allocation-proxy]]] + (is (= kind + (try (batch/check-aggregate-limits! (batch/normalize-client-limits {limit maximum}) consumed 0) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (:limit-kind (ex-data error)))))))))) + +(deftest ordinary-edges-touch-no-request-state + (is (true? (q/qualify nil nil 123))) + (is (false? (q/qualify nil nil nil))) + (let [r (request)] + (dotimes [i 100] (is (true? (q/qualify r 1 i)))) + (is (not (realized? (:memos r)))))) + +(deftest request-cache-resolves-once-and-retains-faults + (let [reads (atom {}) calls (atom 0) r (request {:reads reads :calls calls})] + (dotimes [_ 5] + (is (= :conditional-permission (evidence/permissionship (q/qualify r 1 [10 3])))) + (is (= ["flag"] (evidence/missing-fields (q/qualify r 1 [10 3]))))) + (is (= {1 1 2 1 3 1} @reads)) + (is (= 10 @calls))) + (let [reads (atom {}) r (request {:reads reads})] + (dotimes [_ 5] (is (evidence/fault? (q/qualify r 1 [10 999])))) + (is (= {999 1} @reads))) + (with-redefs [q/maximum-request-entries 1] + (is (evidence/fault? (q/qualify (request) 1 [10 3]))))) + +(deftest exclusive-expiry-precedes-program-work + (doseq [time [99 100 101] qid [3 4 5]] + (let [calls (atom 0) r (request {:time time :context {"flag" true} :calls calls}) + result (q/qualify r 1 [10 qid])] + (is (= (< time 100) (evidence/has? result))) + (is (= (when (< time 100) 100) (evidence/valid-until result))) + (is (= (if (and (< time 100) (not= qid 5)) 1 0) @calls)))) + (with-redefs [plan/compile-plan (fn [& _] (throw (ex-info "Compilation must not run" {})))] + (is (false? (q/qualify (request {:time 100}) 1 [10 3]))))) + +(deftest contextual-results-do-not-enter-the-decode-cache + (let [cache (lru/store 16) + reads (atom {}) + cases [[99 {"flag" true} :has-permission] + [99 {"flag" false} :no-permission] + [99 {} :conditional-permission] + [100 {"flag" true} :no-permission]]] + (doseq [[time context expected] cases] + (let [r (request {:time time :context context :cache cache :reads reads})] + (is (= expected (evidence/permissionship (q/qualify r 1 [10 3])))))) + (is (= 1 (get @reads 3))) + (is (= 1 (lru/entry-count cache))) + (doseq [basis [{:source "s" :lifecycle "reset" :revision 1} + {:source "s" :lifecycle "l" :revision 2}]] + (let [db (assoc-in fixture [3 :eacl.relationship-qualifier/valid-until-ms] 98) + r (request {:db db :basis basis :cache cache :reads reads})] + (is (false? (q/qualify r 1 [10 3]))))) + (is (= 3 (get @reads 3)))) + (let [bound-wins (request {:context {"flag" false}}) + invalid-request (request {:context {"flag" "wrong-type"}})] + (is (evidence/has? (q/qualify bound-wins 1 [10 4]))) + (is (evidence/fault? (q/qualify invalid-request 1 [10 4]))))) + +(deftest each-caveat-projects-request-fields-without-weakening-bound-context + (let [db (-> fixture + (assoc-in [1 :eacl.relation/caveats] #{2 6}) + (assoc 6 (assoc (definition/entity "adult" [["age" :int]] "age >= 18") :db/id 6) + 7 (qualifier/entity-data 7 {:caveat 6} [["age" :int]]))) + r (request {:db db :context {"flag" true "age" 20 "unused" "kept in identity"}})] + (is (evidence/has? (q/qualify r 1 [10 3]))) + (is (evidence/has? (q/qualify r 1 [10 7]))) + (let [missing (request {:db db :context {"flag" true "unused" false}})] + (is (evidence/has? (q/qualify missing 1 [10 3]))) + (is (= ["age"] (evidence/missing-fields (q/qualify missing 1 [10 7]))))) + (let [wrong (request {:db db :context {"flag" true "age" "twenty"}})] + (is (evidence/has? (q/qualify wrong 1 [10 3]))) + (is (evidence/fault? (q/qualify wrong 1 [10 7])))) + (is (not= (q/exact-reuse-identity r) + (q/exact-reuse-identity + (request {:db db :context {"flag" true "age" 20 "unused" "different"}})))) + (let [bad-bound (assoc-in db [3 qualifier/context-attribute] + (values/encode-context [["unused" :bool]] {"unused" true}))] + (is (evidence/fault? (q/qualify (request {:db bad-bound}) 1 [10 3])))))) + +(deftest reconstructed-request-does-not-retain-stale-prepared-context + (let [original (request {:context {"flag" true}}) + later (q/request (assoc original :time 98)) + changed (q/request (assoc original :context {"flag" false})) + missing (q/request (assoc original :context nil))] + (is (identical? (:prepared-context original) (:prepared-context later))) + (is (evidence/has? (q/qualify later 1 [10 3]))) + (is (evidence/no? (q/qualify changed 1 [10 3]))) + (is (= ["flag"] (evidence/missing-fields (q/qualify missing 1 [10 3])))) + (is (not= (q/exact-reuse-identity original) (q/exact-reuse-identity changed))))) + +(deftest authoritative-errors-survive-expiry-and-exclusion + (doseq [db [(dissoc fixture 3) + (assoc-in fixture [3 qualifier/marker-attribute] 99) + (assoc-in fixture [3 qualifier/expiration-attribute] 1.5) + (assoc-in fixture [1 :eacl.relation/caveats] #{77}) + (dissoc fixture 2)]] + (let [result (q/qualify (request {:time 100 :db db}) 1 [10 3])] + (is (evidence/fault? result)) + (is (evidence/fault? (evidence/combine :exclusion true result))))) + (is (evidence/fault? (q/qualify (request {:evaluator nil}) 1 [10 3]))) + (is (false? (q/qualify (request {:time 100 :evaluator nil}) 1 [10 3]))) + (is (evidence/has? (q/qualify (request {:evaluator nil}) 1 [10 5])))) + +(deftest cancellation-is-never-converted-to-absence + (let [token (execution/cancellation-token)] + (execution/cancel! token) + (binding [execution/*contract* (execution/normalize {} :check-permission {:cancellation-token token})] + (is (= :eacl.execution/cancelled + (try (q/qualify (request) 1 [10 3]) nil + (catch #?(:clj Exception :cljs :default) error (:type (ex-data error))))))))) + +(deftest trusted-clock-captures-a-nondecreasing-time + (let [samples (atom [90 100 95 101]) calls (atom 0) + sample (clock/clock #(let [v (first @samples)] (swap! calls inc) (swap! samples subvec 1) v))] + (is (= [90 100 100 101] (vec (repeatedly 4 sample)))) + (is (= 4 @calls))) + (let [sample (clock/clock (constantly 1.5))] + (is (= :clock-time (try (sample) nil + (catch #?(:clj Exception :cljs :default) error (:reason (ex-data error)))))))) + +(deftest qualified-resolution-observes-deadline-and-cancellation-after-native-work + (doseq [stop [:deadline :cancel]] + (let [now (atom 0) + token (execution/cancellation-token) + reads (atom {}) + request (data-request fixture reads) + expected (if (= stop :deadline) :eacl.execution/deadline-exceeded :eacl.execution/cancelled)] + (binding [execution/*monotonic-nanos* #(deref now) + execution/*contract* (binding [execution/*monotonic-nanos* #(deref now)] + (execution/normalize {} :check-permission {:timeout-ms 1 :cancellation-token token})) + backend/*invoke-observer* + (fn [{:keys [phase operation]}] + (when (and (= phase :after) (= operation :qualification-data)) + (if (= stop :deadline) (reset! now 1000000) (execution/cancel! token))))] + (is (= expected + (try (q/qualify request 1 [10 3]) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (:type (ex-data error)))))) + (is (= {3 1} @reads) "stop immediately after the first native qualifier read") + (is (= expected + (try (q/qualify request 1 [11 3]) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (:type (ex-data error)))))) + (is (= {3 1} @reads) "memoized work cannot bypass an expired or cancelled request"))))) + +(deftest request-certificates-include-inspected-expiry-and-retained-evidence + (let [request (request)] + (is (= {:start-ms 99 :valid-until-ms nil :complete? true} (q/certificate request))) + (is (true? (q/qualify request 1 10))) + (is (not (realized? (:temporal-state request)))) + (q/inspect request 1 5) + (is (= {:start-ms 99 :valid-until-ms 100 :complete? true} (q/certificate request))) + (q/observe-evidence! request (evidence/with-certificate false 101 true)) + (is (= 100 (:valid-until-ms (q/certificate request)))) + (q/observe-evidence! request (evidence/with-certificate true nil false)) + (is (false? (:complete? (q/certificate request))))) + (let [request (request {:time 100})] + (q/inspect request 1 5) + (is (= {:start-ms 100 :valid-until-ms nil :complete? true} (q/certificate request))) + (doseq [invalid [{:start-ms 99 :valid-until-ms 100 :complete? true} + {:start-ms 101 :valid-until-ms nil :complete? true} + {:start-ms 99 :valid-until-ms nil :complete? false} + {:start-ms 100 :valid-until-ms nil}]] + (is (some? (try (q/observe-interval! request invalid) nil + (catch #?(:clj Exception :cljs :default) e (ex-data e)))))))) diff --git a/modules/eacl/test/eacl/authorization/qualifier_cache_test.cljc b/modules/eacl/test/eacl/authorization/qualifier_cache_test.cljc new file mode 100644 index 00000000..8b67d618 --- /dev/null +++ b/modules/eacl/test/eacl/authorization/qualifier_cache_test.cljc @@ -0,0 +1,81 @@ +(ns eacl.authorization.qualifier-cache-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as fixtures] + [eacl.authorization.qualifier-cache :as cache] + [eacl.cache.standard-lru :as lru] + [eacl.caveats.definition :as definition] + [eacl.relationships.qualifier :as qualifier])) + +(def basis {:backend :test :source-id "s" :source-lifecycle "l" :branch nil + :basis-kind :ordinary :revision 1 :exact-locator 1 :backend-snapshot-id "one"}) + +(defn request [store options] + (fixtures/request (merge {:cache store :basis basis} options))) + +(deftest exact-and-content-reuse-retain-data-and-reevaluate-each-request + (let [store (cache/cache nil) reads (atom {}) calls (atom 0) decodes (atom 0) + decode qualifier/decode] + (with-redefs [qualifier/decode (fn [& args] (swap! decodes inc) (apply decode args))] + (doseq [[time context expected] [[99 {"flag" true} :has-permission] + [99 {} :conditional-permission] + [99 {"flag" false} :no-permission] + [100 {"flag" true} :no-permission]]] + (is (= expected (evidence/permissionship + (qualification/qualify (request store {:time time :context context :reads reads :calls calls}) 1 [10 3]))))) + (is (= 1 @decodes)) + (is (= 1 (get @reads 3))) + (is (= 3 @calls)) + (let [next-basis (assoc basis :revision 2 :exact-locator 2 :backend-snapshot-id "two")] + (is (evidence/has? (qualification/qualify (request store {:basis next-basis :context {"flag" true} :reads reads}) 1 [10 3]))) + (is (= 1 @decodes) "complete content equality permits decode reuse on a later basis") + (is (= 2 (get @reads 3)) "unknown-writer reuse still reads current qualifier content")) + (doseq [changed [(assoc basis :source-lifecycle "reset") + (assoc basis :source-id "other") + (assoc basis :branch "other") + (assoc basis :speculative-id "prospective")]] + (qualification/qualify (request store {:basis changed}) 1 [10 3])) + (is (= 5 @decodes)) + (qualification/qualify (request store {:basis (assoc basis :revision 3) :version (constantly 8)}) 1 [10 3]) + (is (= 6 @decodes) "assertion version remains a decode identity dimension") + (qualification/qualify (request store {:basis (assoc basis :revision 4) + :db (assoc-in fixtures/fixture [1 :eacl/relation-version] 8)}) 1 [10 3]) + (is (= 7 @decodes) "owning Relation content remains part of the reuse proof")))) + +(deftest complete-content-proofs-detect-unstamped-mutations-and-deletion + (doseq [db [(assoc-in fixtures/fixture [3 qualifier/expiration-attribute] 98) + (assoc-in fixtures/fixture [3 qualifier/marker-attribute] 99) + (assoc-in fixtures/fixture [3 :unknown/field] true) + (dissoc fixtures/fixture 3) + (-> fixtures/fixture (assoc-in [3 qualifier/marker-attribute] 99) (assoc-in [1 :eacl.relation/caveats] #{})) + (assoc fixtures/fixture 2 (assoc (definition/entity "enabled" fixtures/parameters "!flag") :db/id 2)) + (assoc-in fixtures/fixture [1 :eacl.relation/caveats] #{})]] + (let [store (cache/cache nil) + warm (request store {:context {"flag" true}}) + options {:db db :basis (assoc basis :revision 2) :context {"flag" true}} + _ (is (evidence/has? (qualification/qualify warm 1 [10 3]))) + cached (qualification/qualify (request store options) 1 [10 3]) + uncached (qualification/qualify (request nil options) 1 [10 3])] + (is (= cached uncached)) + (is (not (evidence/has? cached))))) + (let [store (cache/cache nil) + _ (qualification/qualify (request store {}) 1 [10 3]) + replacement (assoc-in fixtures/fixture [3 qualifier/expiration-attribute] 98)] + (is (false? (qualification/qualify (request store {:db replacement :basis (assoc basis :source-lifecycle "reset")}) 1 [10 3]))))) + +(deftest decode-cache-publication-and-capacity-remain-optional-and-bounded + (let [store (cache/cache {:max-entries 3})] + (qualification/qualify (request store {:populate-cache? false}) 1 [10 3]) + (is (zero? (lru/entry-count (:entries store)))) + (doseq [revision (range 20)] + (qualification/qualify (request store {:basis (assoc basis :revision revision)}) 1 [10 3]) + (is (<= (lru/entry-count (:entries store)) 3))) + (let [before (lru/entry-count (:entries store))] + (qualification/qualify (request store {:basis (assoc basis :revision 50) :populate-cache? false}) 1 [10 3]) + (is (= before (lru/entry-count (:entries store)))))) + (is (nil? (cache/cache false))) + (doseq [option [true {} {:max-entries 0} {:max-entries 100001} {:max-entries 2 :writer :trusted}]] + (is (= :eacl/invalid-config + (try (cache/cache option) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error (:type (ex-data error)))))))) diff --git a/modules/eacl/test/eacl/authorization/result_test.cljc b/modules/eacl/test/eacl/authorization/result_test.cljc new file mode 100644 index 00000000..4b87636e --- /dev/null +++ b/modules/eacl/test/eacl/authorization/result_test.cljc @@ -0,0 +1,54 @@ +(ns eacl.authorization.result-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.result :as result])) + +(deftest detailed-results-preserve-membership-and-residuals + (doseq [end [nil 100]] + (is (= {:allowed? true :permissionship :has-permission} + (result/check-result (evidence/with-certificate true end true)))) + (is (= {:allowed? false :permissionship :no-permission} + (result/check-result (evidence/with-certificate false end true)))) + (let [conditional (evidence/with-certificate + (evidence/conditional [:a] ["country" "age"]) end true) + decision (result/check-result conditional)] + (is (false? (:allowed? decision))) + (is (= :conditional-permission (:permissionship decision))) + (is (= ["age" "country"] (:missing-fields decision))) + (is (= conditional (evidence/decode (:residual decision))))))) + +(deftest detailed-results-preserve-authoritative-failure + (let [value (evidence/fault :eacl.qualifier/invalid :missing-qualifier) + data (try (result/check-result value) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (ex-data error)))] + (is (= {:type :eacl.authorization/evaluation-failure + :eacl/error :eacl.authorization/evaluation-failure + :faults [[:eacl.qualifier/invalid :missing-qualifier]]} + data)))) + +(deftest qualified-cache-values-retain-evidence-kind-and-reject-faults + (doseq [value [true false (evidence/conditional [:a] ["flag"])]] + (is (result/cache-value? (evidence/encode value)))) + (doseq [value [nil true false {} "malformed" (str (evidence/encode true) " ") + (evidence/encode (evidence/fault :invalid :qualifier))]] + (is (false? (result/cache-value? value))))) + +(deftest detailed-lookup-items-validate-the-whole-decision + (let [object {:type :folder :id "folder" :relation nil} + object? #(= object %) + conditional (result/lookup-result object (evidence/conditional [:a] ["flag"])) + definite (result/lookup-result object true)] + (doseq [valid [conditional definite]] + (is (result/lookup-result-valid? object? valid))) + (doseq [invalid [nil true object + (assoc conditional :allowed? true) + (assoc conditional :missing-fields []) + (assoc conditional :residual (evidence/encode true)) + (assoc conditional :permissionship :has-permission) + (assoc conditional :residual (str (:residual conditional) " ")) + (assoc definite :residual (:residual conditional)) + (assoc definite :permissionship :no-permission) + (assoc definite :object nil) + (result/lookup-result object false)]] + (is (false? (result/lookup-result-valid? object? invalid)))))) diff --git a/modules/eacl/test/eacl/authorization/temporal_test.cljc b/modules/eacl/test/eacl/authorization/temporal_test.cljc new file mode 100644 index 00000000..9f02ec3e --- /dev/null +++ b/modules/eacl/test/eacl/authorization/temporal_test.cljc @@ -0,0 +1,121 @@ +(ns eacl.authorization.temporal-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.temporal :as temporal] + [eacl.cache :as cache])) + +(def basis-key + {:key-version 2 :backend :test :adapter-fingerprint :temporal-test :identity-contract :identity-v1 + :basis-identity {:backend :test :source-id :source :branch nil :source-lifecycle :one + :basis-kind :ordinary :revision 1 :exact-locator 1 + :backend-snapshot-id {:basis 1}}}) +(def semantic-key + {:operation :can? :query {:public [:user :view :doc]} + :qualification [1 :basis :context :evaluator] + :temporal-answer-format temporal/point-format}) + +(defn resolve! [store time proof options] + (cache/resolve-basis! store (merge {:exact-basis-key basis-key :evaluation-time-ms time} options) + semantic-key #(temporal/point-answer time proof))) + +(deftest collection-certificates-guard-resident-answers-and-replacement + (let [store (cache/basis-cache {:max-entries 8}) + key {:operation :count-resources :query {:public {}} + :qualification [1 :basis :context :evaluator] + :qualification-certificate-format temporal/collection-format} + resolve (fn [time n end complete? options] + (cache/resolve-basis! store (merge {:exact-basis-key basis-key :evaluation-time-ms time} options) + key #(hash-map :count n :limit -1 + :qualification-certificate (temporal/interval time end complete?))))] + (is (false? (:cached? (resolve 99 1 100 true {})))) + (is (:cached? (resolve 99 1 100 true {}))) + (is (= 0 (get-in (resolve 100 0 nil true {:populate-cache? false}) [:value :count]))) + (is (false? (:cached? (resolve 100 0 nil true {:populate-cache? false})))) + (is (false? (:cached? (resolve 101 0 nil true {})))) + (is (:cached? (resolve 102 0 nil true {}))) + (is (false? (:cached? (resolve 99 1 100 true {})))) + (is (:cached? (resolve 103 0 nil true {}))))) + +(deftest certificate-boundaries-and-completeness-are-independent-of-retention + (doseq [value [true false (evidence/conditional [:test] ["field"])] + start [98 99 100] end [101 nil] complete? [true false] + time [97 98 99 100 101 102] exact? [true false]] + (let [proof (evidence/with-certificate value end complete?) + answer (temporal/point-answer start proof)] + (is (temporal/point-answer-valid? answer)) + (is (= (and (<= start time) (or (nil? end) (< time end)) + (or complete? (and exact? (= start time)))) + (temporal/reusable? answer time exact?))))) + (let [answer (temporal/point-answer 99 (evidence/with-certificate true 100 true))] + (doseq [bad [(assoc answer :kind :conditional-permission) + (assoc answer :valid-until-ms 200) + (assoc answer :complete? false) + (assoc answer :start-ms 100) + (assoc answer :extra true) + (assoc answer :value (evidence/encode (evidence/fault :test/fault :invalid)))]] + (is (false? (temporal/point-answer-valid? bad)))))) + +(deftest resident-expiration-is-a-miss-and-later-publication-replaces-the-interval + (let [store (cache/basis-cache {:max-entries 8}) + grant (evidence/with-certificate true 100 true)] + (is (false? (:cached? (resolve! store 99 grant {})))) + (is (:cached? (resolve! store 99 grant {}))) + (let [resident (cache/export-basis-snapshot store {:max-entries 8}) + expired (resolve! store 100 false {:populate-cache? false})] + (is (false? (:cached? expired))) + (is (= :no-permission (get-in expired [:value :kind]))) + (is (= resident (cache/export-basis-snapshot store {:max-entries 8})) + "a rejected interval can remain physically resident without changing authorization")) + (is (false? (:cached? (resolve! store 101 false {})))) + (is (:cached? (resolve! store 102 false {}))) + (is (false? (:cached? (resolve! store 99 grant {}))) "older pinned computation cannot reuse a future interval") + (is (:cached? (resolve! store 102 false {})) "older publication did not displace the newer interval"))) + +(deftest expired-bans-and-incomplete-certificates-never-reuse-a-stale-decision + (let [store (cache/basis-cache {:max-entries 8}) + denial (evidence/with-certificate false 100 true)] + (is (= :no-permission (get-in (resolve! store 99 denial {}) [:value :kind]))) + (is (:cached? (resolve! store 99 denial {}))) + (let [allowed (resolve! store 100 true {})] + (is (false? (:cached? allowed))) + (is (= :has-permission (get-in allowed [:value :kind]))))) + (let [store (cache/basis-cache {:max-entries 8}) + unknown (evidence/with-certificate true nil false)] + (resolve! store 99 unknown {}) + (is (:cached? (resolve! store 99 unknown {}))) + (is (false? (:cached? (resolve! store 100 unknown {})))) + (is (false? (:cached? (resolve! store 101 unknown {})))))) + +(deftest cursor-certificates-are-closed-and-never-extend-prior-deadlines + (doseq [mode [:live :pinned] start [98 99] end [100 nil] complete? [true false]] + (let [certificate (temporal/cursor-certificate mode start nil (temporal/interval start end complete?))] + (is (temporal/cursor-certificate-valid? certificate)) + (doseq [time [97 98 99 100 101]] + (is (= (if (= :pinned mode) (= time start) + (and (<= start time) (or (nil? end) (< time end)) (or complete? (= time start)))) + (temporal/cursor-time-valid? certificate time)))))) + (let [prior (temporal/cursor-certificate :live 98 nil (temporal/interval 98 100 true)) + resumed (temporal/cursor-certificate :live 99 prior (temporal/interval 99 nil true))] + (is (= {:mode :live :original-time-ms 98 :start-ms 99 :valid-until-ms 100 :complete? true} resumed)) + (doseq [bad [(assoc resumed :mode :unknown) (assoc resumed :mode :pinned) + (assoc resumed :start-ms 100) (assoc resumed :original-time-ms 100) + (assoc resumed :complete? nil) (assoc resumed :extra true) + (dissoc resumed :valid-until-ms) (dissoc resumed :complete?)]] + (is (false? (temporal/cursor-certificate-valid? bad)))))) + +(deftest qualified-collection-ingress-requires-a-certificate-for-its-exact-time + (let [key {:operation :count-resources :query {:public {}} + :qualification [1 :basis 99 :context :evaluator] + :qualification-certificate-format temporal/collection-format} + answer {:count 2 :limit -1 :qualification-certificate (temporal/interval 99 100 true)} + valid? #(cache/completed-answer-value-valid? :count-resources key %)] + (is (valid? answer)) + (is (valid? (assoc-in answer [:qualification-certificate :complete?] false))) + (doseq [bad [(dissoc answer :qualification-certificate) + (assoc-in answer [:qualification-certificate :start-ms] 100) + (assoc-in answer [:qualification-certificate :valid-until-ms] 99) + (assoc-in answer [:qualification-certificate :extra] true) + (assoc answer :extra true)]] + (is (false? (valid? bad)))) + (is (false? (cache/completed-answer-value-valid? :count-resources + (dissoc key :qualification-certificate-format) answer))))) diff --git a/modules/eacl/test/eacl/backend/v8_test.cljc b/modules/eacl/test/eacl/backend/v8_test.cljc index 8cf1a94b..245fe312 100644 --- a/modules/eacl/test/eacl/backend/v8_test.cljc +++ b/modules/eacl/test/eacl/backend/v8_test.cljc @@ -1,6 +1,7 @@ (ns eacl.backend.v8-test (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is testing]] + [eacl.authorization.data :as qualification-data] [eacl.backend.v8 :as backend] [eacl.cache.derived-schema :as derived-schema] [eacl.cache.key :as cache-key] @@ -81,6 +82,51 @@ (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error (ex-data error)))) +(deftest compact-scan-and-direct-guards-preserve-endpoint-order + (let [make (fn [values] + (backend/make-adapter + {:id :test :runtime-guards? true :capabilities {} + :operations (assoc (operation-map) + :subject->resources (fn [& _] values) + :direct-edge (fn [& _] [2 100]))})) + values [1 [2 100] 3] + adapter (make values) + options {:direction :asc :include-qualifier? true} + scan! (backend/scan-invoker adapter :subject->resources)] + (is (= values (scan! :user 10 20 :doc options))) + (is (= values (backend/reduce-scan adapter :subject->resources + [:user 10 20 :doc options] [] {:step conj}))) + (is (= [2 100] (backend/invoke adapter :direct-edge :user 10 20 :doc 2))) + (is (= :eacl/backend-contract-violation + (:eacl/error (error-data #(scan! :user 10 20 :doc {}))))) + (doseq [bad [[[2 100] [2 101]] [[3 100] 1] [[]] [[2 nil]]]] + (is (= :eacl/backend-contract-violation + (:eacl/error (error-data #((backend/scan-invoker (make bad) :subject->resources) + :user 10 20 :doc options))))) + (is (= :eacl/backend-contract-violation + (:eacl/error (error-data #(backend/reduce-scan (make bad) :subject->resources + [:user 10 20 :doc options] [] {:step conj})))))))) + +(deftest qualification-data-capability-is-paired-and-guarded + (let [make (fn [capabilities operation guards?] + (backend/make-adapter + {:id :test :runtime-guards? guards? :capabilities capabilities + :operations (cond-> (operation-map) + operation (assoc :qualification-data operation))})) + capabilities {:qualification #{qualification-data/capability}} + packet {:entity {:db/id 1 :private/field "secret"} :version 7 :fact-count 1}] + (doseq [guards? [true false]] + (is (some? (error-data #(make capabilities nil guards?)))) + (is (some? (error-data #(make {} (constantly packet) guards?))))) + (is (some? (error-data #(make {:qualification #{:unknown-contract}} (constantly packet) true)))) + (is (= packet (backend/invoke (make capabilities (constantly packet) true) :qualification-data 1))) + (doseq [invalid [(assoc packet :extra true) (assoc packet :fact-count 4097) + (assoc packet :version -1) (assoc packet :entity [])]] + (let [error (error-data #(backend/invoke (make capabilities (constantly invalid) true) + :qualification-data 1))] + (is (= :eacl/backend-contract-violation (:eacl/error error))) + (is (not (re-find #"secret" (pr-str error)))))))) + #?(:clj (deftest default-schema-warning-dedupe-is-concurrent-and-bounded-test (let [warning-var @@ -326,7 +372,7 @@ (backend/certification-obligations :subject->resources) :strict-order)) - (is (= #{:schema-generation :direct-match-many?} + (is (= #{:schema-generation :direct-match-many? :direct-edge :qualification-data} backend/optional-snapshot-operations)) (is (every? (backend/certification-obligations :schema-generation) @@ -1040,3 +1086,9 @@ (update :reverse inc)) @calls) "direct projection helpers do not retain host-owned scan chunks")))))))) + +(deftest qualified-publication-contract-is-closed-and-unambiguous + (doseq [contract [#{:atomic-inline-v1} #{:atomic-prepared-v1} #{}]] + (is (= contract (:qualified-publication (backend/normalize-capabilities :test {:qualified-publication contract}))))) + (doseq [contract [#{:unknown} #{:atomic-inline-v1 :atomic-prepared-v1} [:atomic-inline-v1]]] + (is (some? (error-data #(backend/normalize-capabilities :test {:qualified-publication contract})))))) diff --git a/modules/eacl/test/eacl/caveats/cache_trace_contract.cljc b/modules/eacl/test/eacl/caveats/cache_trace_contract.cljc new file mode 100644 index 00000000..08919fde --- /dev/null +++ b/modules/eacl/test/eacl/caveats/cache_trace_contract.cljc @@ -0,0 +1,114 @@ +(ns eacl.caveats.cache-trace-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl])) + +(defn schema [expression] + (str "caveat enabled(flag bool) { " expression " }\n" + "definition user {}\n" + "definition doc {\n" + " relation member: user | user with enabled\n" + " relation writer: user\n" + " relation banned: user\n" + " relation parent: doc\n" + " permission direct = member\n" + " permission view = member - banned\n" + " permission walk = member + parent->walk\n" + " permission via = parent->direct\n" + " permission either = member + writer\n}")) + +(defn outcome [f] + (try (f) + (catch #?(:clj Exception :cljs :default) error + {:fault (select-keys (ex-data error) [:type :reason :eacl/error])}))) + +(defn drain [operation client query] + (loop [boundary nil pages 0 values []] + (when (> pages 20) (throw (ex-info "Cache trace lookup did not progress" {:type ::no-progress}))) + (let [page (operation client (cond-> (assoc query :first 1) boundary (assoc :after boundary))) + values (into values (:data page))] + (if (get-in page [:page-info :has-next-page?]) + (recur (get-in page [:page-info :end-cursor]) (inc pages) values) + values)))) + +(defn compare-operation! [label read] + (let [cold (outcome #(read {:cache? false})) + warm (outcome #(read {})) + repeated (outcome #(read {})) + read-only (outcome #(read {:populate-cache? false}))] + (is (not (:fault cold)) (pr-str label)) + (is (= cold warm repeated read-only) (pr-str label)) + cold)) + +(defn sample! [client subject resource label] + (doseq [context [{} {"flag" true} {"flag" false}] + permission [:direct :view :walk :via :either]] + (let [check {:subject subject :resource resource :permission permission :caveat-context context}] + (compare-operation! [label :point permission context] + #(dissoc (eacl/check-permission client (merge check %)) :cached? :cache-basis :evaluation)) + (doseq [direction [:forward :reverse] policy [:definite :detailed]] + (let [query (merge {:permission permission :caveat-context context :result-policy policy + :evaluation :complete-denotation} + (if (= :forward direction) + {:subject subject :resource/type :doc} + {:resource resource :subject/type :user})) + lookup (if (= :forward direction) eacl/lookup-resources eacl/lookup-subjects) + count! (if (= :forward direction) eacl/count-resources eacl/count-subjects)] + (compare-operation! [label :lookup direction policy permission context] + #(drain lookup client (merge query %))) + (doseq [limit [nil 1]] + (compare-operation! [label :count direction policy permission context limit] + #(dissoc (count! client (cond-> (merge query %) limit (assoc :count-limit limit))) + :cached? :cache-basis)))))))) + +(defn check! [{:keys [client writer now expire-cache! rotate-client!]}] + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-schema! client {:schema (schema "flag")}) + (let [active-client (atom client) + native (:native (writer)) + subject (eacl/spice-object :user "trace/u") + resources (mapv #(eacl/spice-object :doc %) ["trace/a" "trace/b" "trace/c"]) + [a b c] resources + relationship #(eacl/->Relationship subject %1 %2) + member (relationship :member a) + ban (relationship :banned a) + touch! #(eacl/write-relationships! @active-client [{:operation :touch :relationship %}]) + point #(get (eacl/check-permission @active-client {:subject subject :resource a :permission % + :caveat-context {"flag" true}}) :permissionship) + sample #(sample! @active-client subject a %)] + ((:transact! native) (mapv #(hash-map :eacl/id (:id %)) (into [subject] resources))) + (eacl/write-relationships! @active-client + (mapv #(hash-map :operation :create :relationship %) + [(assoc member :caveat "enabled" :valid-until-ms 200) + (assoc (relationship :member b) :caveat "enabled" :caveat-context {"flag" false} :valid-until-ms 200) + (relationship :member c) (relationship :writer a) + (assoc ban :valid-until-ms 100) + (eacl/->Relationship b :parent a) + (eacl/->Relationship c :parent b)])) + (sample :initial) + (is (= :no-permission (point :view))) + (reset! now 100) + (sample :expired-ban) + (is (= :has-permission (point :view))) + (reset! now 200) + (sample :expired-grant) + (is (= :no-permission (point :direct))) + (touch! (assoc member :caveat "enabled" :caveat-context {"flag" true} :valid-until-ms 300)) + (sample :renewed-bound-context) + (is (= :has-permission (point :direct))) + (eacl/write-schema! @active-client {:schema (schema "!flag")}) + (sample :changed-caveat-definition) + (is (= :no-permission (point :direct))) + (if rotate-client! + (reset! active-client (rotate-client! @active-client "qualified-cache-trace-reset")) + (expire-cache! @active-client "qualified-cache-trace-reset")) + (sample :new-source-lifecycle) + (touch! (assoc ban :valid-until-ms 250)) + (touch! member) + (sample :removed-qualifier) + (is (= :has-permission (point :direct))) + (is (= :no-permission (point :view))) + (reset! now 250) + (sample :second-ban-expiry) + (is (= :has-permission (point :view))) + {:subject subject :resource a :member member :ban ban}))) diff --git a/modules/eacl/test/eacl/caveats/deletion_contract.cljc b/modules/eacl/test/eacl/caveats/deletion_contract.cljc new file mode 100644 index 00000000..e3715494 --- /dev/null +++ b/modules/eacl/test/eacl/caveats/deletion_contract.cljc @@ -0,0 +1,80 @@ +(ns eacl.caveats.deletion-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.backend.writer :as backend-writer] + [eacl.caveats.cache-trace-contract :as trace] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl] + [eacl.relationships.qualifier-integrity :as integrity] + [eacl.relationships.staged :as staged] + [eacl.relationships.endpoint-pair :as pair] + [eacl.relationships.storage :as storage])) + +(defn check! [{:keys [client writer]}] + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-schema! client {:schema (trace/schema "flag")}) + (let [w (writer) native (:native w) with-db (:with-snapshot native) + subject (eacl/spice-object :user "delete/u") + resources (mapv #(eacl/spice-object :doc (str "delete/" %)) (range 8)) + tx! (:transact! native) + capture #(with-db (fn [db] (integrity/proof-input native db))) + operation backend-writer/operation + submitted (atom [])] + (tx! (mapv #(hash-map :eacl/id (:id %)) (into [subject] resources))) + (eacl/write-relationships! client + (mapv (fn [i resource] + {:operation :create + :relationship (cond-> (eacl/->Relationship subject :member resource) + (even? i) (assoc :caveat "enabled" :valid-until-ms 100))}) + (range) resources)) + (eacl/write-relationships! client [{:operation :create + :relationship (assoc (eacl/->Relationship (first resources) :parent (first resources)) + :valid-until-ms 100)}]) + (eacl/prepare-relationship! client (assoc (eacl/->Relationship subject :member (first resources)) :valid-until-ms 500)) + (let [before (capture) + orphan-ids (set (remove (set (keys (:references before))) (keys (:qualifiers before))))] + (is (= 1 (count orphan-ids))) + (with-redefs [backend-writer/max-transaction-size (constantly 12) + backend-writer/operation + (fn [writer op] + (let [f (operation writer op)] + (if (= op :transact!) + (fn [conn request] + (is (<= (count (:tx-data request)) 12)) + (let [report (f conn request) + frame (capture) + status (integrity/report frame)] + (swap! submitted conj (:tx-data request)) + (is (= :healthy (:status status))) + (is (= 1 (get-in status [:counts :unattached-qualifier]))) + report)) + f)))] + (eacl/delete-object! client subject) + (eacl/delete-object! client (first resources))) + (is (> (count @submitted) 1)) + (let [after (capture)] + (is (empty? (:references after))) + (is (= orphan-ids (set (keys (:qualifiers after))))) + (is (= :healthy (:status (integrity/report after))))) + (is (empty? (:data (eacl/read-relationships client {:resource/type :doc :first 20})))) + (is (zero? (:retracted-datoms (eacl/delete-object! client subject)))) + (let [relationship (assoc (eacl/->Relationship subject :member (first resources)) :valid-until-ms 300)] + (eacl/create-relationship! client relationship) + (let [{:keys [tx-data subject-id]} + (with-db + (fn [db] + (let [[qid {:keys [forward]}] (first (:references (integrity/proof-input native db))) + [st subject rid rt resource] (first forward)] + {:subject-id subject + :tx-data (staged/plan-retraction-batch w db + [[:db/retract subject storage/forward-attribute + (pair/forward-value st rid rt resource qid)]])})))] + (tx! [[:db/add subject-id :eacl/id "delete/renamed"]]) + (is (contains? (trace/outcome #(tx! tx-data)) :fault)) + (is (= 1 (count (:references (capture))))) + (tx! [[:db/add subject-id :eacl/id "delete/u"]]) + (eacl/write-relationships! client [{:operation :touch :relationship (assoc relationship :valid-until-ms 400)}]) + (is (contains? (trace/outcome #(tx! tx-data)) :fault)) + (is (= :healthy (:status (integrity/report (capture))))) + (is (= 1 (count (:references (capture))))) + (eacl/delete-object! client subject) + (is (= orphan-ids (set (keys (:qualifiers (capture)))))))))))) diff --git a/modules/eacl/test/eacl/caveats/inspection_contract.cljc b/modules/eacl/test/eacl/caveats/inspection_contract.cljc new file mode 100644 index 00000000..76b0dfa9 --- /dev/null +++ b/modules/eacl/test/eacl/caveats/inspection_contract.cljc @@ -0,0 +1,60 @@ +(ns eacl.caveats.inspection-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.caveats.plan :as plan] + [eacl.caveats.schema-admission-test :as schemas] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl])) + +(defn- check-renewal! [client relationship] + (let [query {:subject/type :user :subject/id "inspection/u" + :resource/type :doc :resource/id "inspection/0" :first 1} + active #(seq (:data (eacl/read-relationships client (assoc query :relationship-state :expiry-active)))) + stored #(first (:data (eacl/read-relationships client query)))] + (is (nil? (active))) + (doseq [[deadline expected-active?] [[500 true] [200 false] [nil true]]] + (let [replacement (cond-> (dissoc relationship :valid-until-ms) + deadline (assoc :valid-until-ms deadline))] + (eacl/write-relationship! client (assoc replacement :operation :touch)) + (is (= expected-active? (boolean (active)))) + (is (= replacement (stored))))))) + +(defn check! [{:keys [client writer now]}] + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-schema! client (schemas/source "user | user with enabled")) + (let [native (:native (writer)) + subject (eacl/spice-object :user "inspection/u") + resources (mapv #(eacl/spice-object :doc (str "inspection/" %)) (range 4)) + qualifiers [{:valid-until-ms 100} + {:caveat "enabled" :valid-until-ms 200} + {:caveat "enabled" :caveat-context {"flag" false}} + {}] + relationships (mapv (fn [resource qualifier] + (merge (eacl/->Relationship subject :viewer resource) qualifier)) + resources qualifiers)] + ((:transact! native) (mapv #(hash-map :eacl/id (:id %)) (cons subject resources))) + (eacl/write-relationships! client (mapv #(hash-map :operation :create :relationship %) relationships)) + (with-redefs [plan/compile-plan (fn [& _] (throw (ex-info "Inspection must not compile a Caveat" {})))] + (doseq [[time active-ids] [[99 #{"inspection/0" "inspection/1" "inspection/2" "inspection/3"}] + [100 #{"inspection/1" "inspection/2" "inspection/3"}] + [200 #{"inspection/2" "inspection/3"}]]] + (reset! now time) + (doseq [anchor [{:subject/type :user :subject/id "inspection/u"} + {:subject/type :user} + {:resource/type :doc} + {:resource/type :doc :resource/id "inspection/1"} + {:resource/type :doc :resource/id "inspection/1" + :subject/type :user :subject/id "inspection/u"}] + paging [{:first 20} {:last 20}]] + (let [query (merge anchor paging) + expected (if (:resource/id anchor) #{"inspection/1"} (set (map :id resources))) + expected-active (if (:resource/id anchor) + (if (contains? active-ids "inspection/1") #{"inspection/1"} #{}) active-ids) + stored (eacl/read-relationships client (assoc query :relationship-state :stored)) + active (eacl/read-relationships client (assoc query :relationship-state :expiry-active))] + (is (= expected (set (map (comp :id :resource) (:data stored))))) + (is (= expected-active (set (map (comp :id :resource) (:data active))))) + (is (= (set (filter #(contains? expected (get-in % [:resource :id])) relationships)) (set (:data stored)))) + (is (= :expiry-active (:relationship-state active))) + (is (= time (:evaluation-time-ms active))) + (is (= (:data active) (:data (eacl/read-relationships client (assoc query :relationship-state :expiry-active :cache? false))))))))) + (check-renewal! client (first relationships))))) diff --git a/modules/eacl/test/eacl/caveats/public_write_contract.cljc b/modules/eacl/test/eacl/caveats/public_write_contract.cljc new file mode 100644 index 00000000..87e0d615 --- /dev/null +++ b/modules/eacl/test/eacl/caveats/public_write_contract.cljc @@ -0,0 +1,156 @@ +(ns eacl.caveats.public-write-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [clojure.set :as set] + [eacl.caveats.persistence-contract :as persistence] + [eacl.caveats.publication-contract :as publication] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl] + [eacl.relationships.storage :as storage])) + +(defn check! [{:keys [client writer entid now allowance-stamps cas-attribute speculative?] + :or {speculative? true}}] + (eacl/write-schema! client persistence/first-schema) + (let [native (:native (writer)) + with-db (:with-snapshot native) + tx! (:transact! native) + relation (with-db #(entid % [:eacl.relation/resource-type+relation-name+subject-type [:doc :viewer :user]])) + caveat (with-db #(entid % [:eacl.caveat/name "enabled"]))] + (tx! (into [{:eacl/id "public/u"} {:eacl/id "public/a"} {:eacl/id "public/b"} + {:db/id relation :eacl.relation/caveats [caveat] :eacl.relation/allows-unqualified? true}] + (when allowance-stamps (with-db allowance-stamps)))) + (let [subject (eacl/spice-object :user "public/u") + resource (eacl/spice-object :doc "public/a") + other (eacl/spice-object :doc "public/b") + relationship (assoc (eacl/->Relationship subject :viewer resource) + :caveat "enabled" :valid-until-ms 100) + request {:subject subject :resource resource :permission :view} + rows #(with-db (fn [db] (mapv :v ((:all-rows native) db storage/forward-attribute)))) + qids #(with-db (fn [db] (set (map :e ((:all-rows native) db :eacl.relationship-qualifier/format-version))))) + error-data publication/error-data] + (is (= :eacl/unsupported-capability + (:type (error-data #(binding [orchestration/*qualified-authorization-enabled?* false] + (eacl/write-relationships! client [{:operation :create :relationship relationship}])))))) + (is (empty? (rows))) + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-relationships! client [{:operation :create :relationship relationship} + {:operation :create :relationship relationship}]) + (is (= 1 (count (rows)) (count (qids)))) + (is (= :conditional-permission (:permissionship (eacl/check-permission client request)))) + (is (true? (eacl/can? client (assoc request :caveat-context {"flag" true})))) + (reset! now 100) + (is (false? (eacl/can? client (assoc request :caveat-context {"flag" true})))) + (is (= :eacl/relationship-conflict + (:type (error-data #(eacl/create-relationship! client relationship))))) + (let [before (qids)] + (is (= :eacl/invalid-relationship-update-batch + (:type (error-data #(eacl/write-relationships! + client [{:operation :touch :relationship relationship} + {:operation :touch :relationship (assoc relationship :valid-until-ms 200)}]))))) + (is (= before (qids))) + (eacl/write-relationship! client (assoc relationship :operation :touch + :caveat-context {"flag" true} :valid-until-ms 300)) + (is (true? (eacl/can? client request))) + (is (= 1 (count (qids)))) + (is (empty? (set/intersection before (qids))))) + (let [subject-id (with-db #(entid % [:eacl/id "public/u"])) + flag-attribute (if cas-attribute (with-db #(cas-attribute % :app/flag)) :app/flag) + before (rows)] + (is (some? (error-data #(eacl/write-relationships! + client {:updates [{:operation :touch :relationship (assoc relationship :valid-until-ms 200)} + {:operation :create :relationship (assoc relationship :resource other)}] + :tx-data [[:db.fn/cas subject-id flag-attribute -1 1]]})))) + (is (= before (rows))) + (is (true? (eacl/can? client request))) + (eacl/write-relationships! client {:updates [{:operation :touch :relationship (dissoc relationship :caveat :valid-until-ms)}] + :tx-data [[:db/add subject-id :app/flag 42]]}) + (is (nil? (nth (first (rows)) 4))) + (is (= 42 (with-db #(:app/flag ((:entity native) % subject-id))))) + (is (true? (eacl/can? client request)))) + (eacl/write-relationship! client (assoc relationship :operation :delete :caveat "unused_name")) + (is (empty? (rows))) + (is (false? (eacl/can? client request))) + (eacl/write-relationship! client (assoc relationship :operation :delete)) + (is (empty? (rows))) + (let [qualified (assoc relationship :caveat-context {"flag" true} :valid-until-ms 300) + before (qids) + handle (eacl/prepare-relationship! client qualified) + snapshot (eacl/snapshot client) + update {:operation :create :relationship qualified :prepared-qualifier handle} + tx-data + (try + (is (some? handle)) + (is (empty? (rows)) "inert preparation cannot publish either endpoint") + (is (= (inc (count before)) (count (qids)))) + (is (= :prepared-value-mismatch + (:reason (error-data #(eacl/tx-relationship snapshot (assoc update :prepared-qualifier nil)))))) + (is (= :prepared-value-mismatch + (:reason (error-data #(eacl/tx-relationship snapshot (assoc-in update [:relationship :valid-until-ms] 400)))))) + (let [tx (eacl/tx-relationship snapshot update)] + (when speculative? + (let [prospective (eacl/with snapshot tx)] + (try + (is (true? (eacl/can? prospective request))) + (is (false? (eacl/can? client request))) + (finally (eacl/release! prospective))))) + tx) + (finally (eacl/release! snapshot)))] + (tx! tx-data) + (is (true? (eacl/can? client request))) + (is (= :qualifier-attached + (:reason (error-data #(eacl/discard-prepared-relationship! client handle))))) + (let [orphan (eacl/prepare-relationship! client qualified) + allocated (qids)] + (eacl/discard-prepared-relationship! client orphan) + (is (= (dec (count allocated)) (count (qids))))) + (let [orphan (eacl/prepare-relationship! client qualified) + snapshot (eacl/snapshot client) + pending (try (eacl/tx-relationship snapshot {:operation :touch :relationship qualified + :prepared-qualifier orphan}) + (finally (eacl/release! snapshot))) + subject-id (with-db #(entid % [:eacl/id "public/u"])) + before (rows)] + (tx! [[:db/add subject-id :eacl/id "public/reassigned"]]) + (is (some? (error-data #(tx! pending))) "identity CAS rejects reassigned endpoints") + (is (= before (rows))) + (tx! [[:db/add subject-id :eacl/id "public/u"]]) + (eacl/discard-prepared-relationship! client orphan)) + (let [resource-id (with-db #(entid % [:eacl/id "public/a"])) + reverse (with-db #(first (filter (fn [row] (= resource-id (:e row))) + ((:all-rows native) % storage/reverse-attribute)))) + before (rows)] + (tx! [[:db/retract resource-id storage/reverse-attribute (:v reverse)] + [:db/add relation (:relation-version-attribute native) + (if (= :datomic (:backend native)) "datomic.tx" :db/current-tx)]]) + (doseq [operation [:touch :create]] + (is (= :asymmetric-or-duplicate-relationship + (:reason (error-data #(eacl/write-relationships! + client [{:operation operation :relationship qualified}])))))) + (is (= before (rows))) + (tx! [[:db/add resource-id storage/reverse-attribute (:v reverse)] + [:db/add relation (:relation-version-attribute native) + (if (= :datomic (:backend native)) "datomic.tx" :db/current-tx)]])) + (let [relationships [(assoc qualified :valid-until-ms 400) + (assoc qualified :resource other)] + handles (if (= :prepared (:strategy native)) + (mapv #(eacl/prepare-relationship! client %) relationships) + [nil nil]) + snapshot (eacl/snapshot client) + pending + (try + (when (= :inline (:strategy native)) + (let [inline-ids (mapv (fn [relationship] + (->> (eacl/tx-relationship snapshot {:operation :touch :relationship relationship}) + (filter #(and (map? %) (:eacl.relationship-qualifier/format-version %))) + first :db/id)) relationships)] + (is (= 2 (count (set inline-ids))) "separate pure plans allocate distinct native temporary ids"))) + (eacl/tx-relationships + snapshot {:updates (mapv (fn [relationship handle] + (cond-> {:operation :touch :relationship relationship} + handle (assoc :prepared-qualifier handle))) + relationships handles)}) + (finally (eacl/release! snapshot)))] + (is (= 1 (count (rows)))) + (tx! pending) + (is (= 2 (count (rows)) (count (set (map #(nth % 4) (rows)))))) + (is (true? (eacl/can? client request))) + (is (true? (eacl/can? client (assoc request :resource other)))))))))) diff --git a/modules/eacl/test/eacl/caveats/publication_batch_contract.cljc b/modules/eacl/test/eacl/caveats/publication_batch_contract.cljc new file mode 100644 index 00000000..34d9bda0 --- /dev/null +++ b/modules/eacl/test/eacl/caveats/publication_batch_contract.cljc @@ -0,0 +1,89 @@ +(ns eacl.caveats.publication-batch-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.caveats.persistence-contract :as persistence] + [eacl.caveats.publication-contract :as publication] + [eacl.relationships.staged :as staged] + [eacl.relationships.storage :as storage])) + +(defn check! [{:keys [write-schema! writer entid strategy allowance-stamps cas-attribute]}] + (write-schema! persistence/first-schema) + (let [w (writer) native (:native w) with-db (:with-snapshot native) tx! (:transact! native) + relation (with-db #(entid % [:eacl.relation/resource-type+relation-name+subject-type [:doc :viewer :user]])) + caveat (with-db #(entid % [:eacl.caveat/name "enabled"]))] + (tx! (into [{:eacl/id "batch/u"} {:eacl/id "batch/a"} {:eacl/id "batch/b"} + {:db/id relation :eacl.relation/caveats [caveat] :eacl.relation/allows-unqualified? true}] + (when allowance-stamps (with-db allowance-stamps)))) + (let [subject (with-db #(entid % [:eacl/id "batch/u"])) + a (with-db #(entid % [:eacl/id "batch/a"])) + b (with-db #(entid % [:eacl/id "batch/b"])) + identity-a [:user subject relation :doc a] + identity-b [:user subject relation :doc b] + rows (fn [attribute] (with-db #(mapv (juxt :e :v) ((:all-rows native) % attribute)))) + qualifier-count #(with-db (fn [db] (count (seq ((:all-rows native) db :eacl.relationship-qualifier/format-version))))) + cas-attribute (if cas-attribute (with-db #(cas-attribute % :app/flag)) :app/flag) + value {:caveat caveat :caveat-context {"flag" true} :valid-until-ms 1000} + updates [{:operation :create :relationship identity-a :value value} + {:operation :create :relationship identity-b :value (assoc value :valid-until-ms 2000)}] + prepared (staged/prepare-batch! w updates) + plan (staged/plan-batch-current w prepared [[:db/add subject :app/flag 1]])] + (is (empty? (rows storage/forward-attribute))) + (is (empty? (rows storage/reverse-attribute))) + (is (= (if (= :prepared strategy) 2 0) (qualifier-count))) + (is (some? (publication/error-data + #(tx! (conj (:tx-data plan) [:db.fn/cas subject cas-attribute -1 1]))))) + (is (empty? (rows storage/forward-attribute))) + (is (empty? (rows storage/reverse-attribute))) + (tx! (:tx-data plan)) + (let [forward (rows storage/forward-attribute) + reverse (rows storage/reverse-attribute) + qids (set (map #(nth (second %) 4) forward))] + (is (= 2 (count forward) (count reverse) (count qids) (qualifier-count))) + (is (every? #(and (integer? %) (pos? %)) qids)) + (is (= qids (set (map #(nth (second %) 4) reverse)))) + (is (= 1 (with-db #(:app/flag ((:entity native) % subject))))) + (let [changes (staged/prepare-batch! + w [{:operation :touch :relationship identity-a :value {:valid-until-ms 3000}} + {:operation :delete :relationship identity-b :value nil}])] + (tx! (:tx-data (staged/plan-batch-current w changes [])))) + (let [forward (rows storage/forward-attribute) + reverse (rows storage/reverse-attribute) + qid (nth (second (first forward)) 4)] + (is (= 1 (count forward) (count reverse) (qualifier-count))) + (is (= [subject [:user relation :doc a qid]] (first forward))) + (is (= [a [:doc relation :user subject qid]] (first reverse))) + (is (not (contains? qids qid))) + (is (= 3000 (with-db #(get ((:entity native) % qid) :eacl.relationship-qualifier/valid-until-ms)))) + (doseq [old qids] (is (nil? (with-db #((:entity native) % old))))) + (let [clear (staged/prepare-batch! w [{:operation :touch :relationship identity-a :value nil}])] + (tx! (:tx-data (staged/plan-batch-current w clear [])))) + (is (= [[subject [:user relation :doc a nil]]] (rows storage/forward-attribute))) + (is (zero? (qualifier-count))))) + (let [next-updates [{:operation :touch :relationship identity-a :value value} + {:operation :create :relationship identity-b :value value}] + reused-id (if (#{:datomic :datascript} (:backend native)) "duplicate" -900000) + invalid-writer (assoc-in w [:native :tempid] (constantly reused-id))] + (is (= (if (= :prepared strategy) :duplicate-temporary-id :qualifier-reuse) + (:reason + (publication/error-data + #(staged/plan-batch-current invalid-writer + (staged/prepare-batch! invalid-writer next-updates) []))))) + (is (= :unsupported-batch-fences + (:reason (publication/error-data + #(staged/prepare-batch! (update w :native dissoc :schema-fence) next-updates))))) + (let [stale (staged/prepare-batch! w next-updates) + stale-plan (staged/plan-batch-current w stale [])] + (staged/write! w :touch identity-a {:valid-until-ms 4000}) + (is (some? (publication/error-data #(tx! (:tx-data stale-plan))))) + (is (= 1 (count (rows storage/forward-attribute)))) + (when (= :prepared strategy) + (doseq [entry stale] (staged/cleanup! w (:value entry))))) + (let [stale (staged/prepare-batch! w next-updates) + stale-plan (staged/plan-batch-current w stale [])] + (staged/write! w :delete identity-a nil) + (is (some? (publication/error-data #(tx! (:tx-data stale-plan))))) + (is (empty? (rows storage/forward-attribute))) + (is (empty? (rows storage/reverse-attribute))) + (when (= :prepared strategy) + (doseq [entry stale] (staged/cleanup! w (:value entry)))))) + (is (= :duplicate-batch-identity + (:reason (publication/error-data #(staged/prepare-batch! w [(first updates) (first updates)])))))))) diff --git a/modules/eacl/test/eacl/caveats/publication_contract.cljc b/modules/eacl/test/eacl/caveats/publication_contract.cljc index 04388aac..0f6c82aa 100644 --- a/modules/eacl/test/eacl/caveats/publication_contract.cljc +++ b/modules/eacl/test/eacl/caveats/publication_contract.cljc @@ -1,5 +1,8 @@ (ns eacl.caveats.publication-contract (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as qualification-fixtures] [eacl.caveats.persistence-contract :as persistence] [eacl.relationships.staged :as staged] [eacl.relationships.qualifier-integrity :as integrity] @@ -61,6 +64,18 @@ (tx! (:tx-data planned)) (is (= [[:user relation :doc resource qid]] (forward))) (is (= [[:doc relation :user subject qid]] (reverse))) + ((:with-snapshot native) + (fn [db] + (doseq [time [999 1000]] + (let [calls (atom 0) + request (qualification/request + {:time time :context {"flag" false} + :evaluator (qualification-fixtures/portable-evaluator calls) + :basis {:source ((:source native) db) :revision ((:revision native) db)} + :entity #(entity db %) :version #((:qualifier-version native) db %)}) + result (qualification/qualify request relation [resource qid])] + (is (= (< time 1000) (evidence/has? result))) + (is (= (if (< time 1000) 1 0) @calls)))))) (is (= 1 (:app/flag (entity (snapshot) subject))) "caller datoms publish with the pair") (let [frame (proof)] (is (= :healthy (:status (integrity/report frame)))) diff --git a/modules/eacl/test/eacl/caveats/schema_admission_test.cljc b/modules/eacl/test/eacl/caveats/schema_admission_test.cljc new file mode 100644 index 00000000..dd5c0322 --- /dev/null +++ b/modules/eacl/test/eacl/caveats/schema_admission_test.cljc @@ -0,0 +1,33 @@ +(ns eacl.caveats.schema-admission-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.caveats.definition-test :as errors] + [eacl.schema.expression-resolver :as resolver])) + +(defn source [branches] + (str "caveat enabled(flag bool) { flag }\n" + "definition user {}\ndefinition doc {\n relation viewer: " branches + "\n permission view = viewer\n}")) + +(deftest qualified-syntax-requires-explicit-schema-admission + (is (= :eacl.schema/unsupported-feature + (errors/error-type #(resolver/validate-schema (source "user with enabled"))))) + (doseq [[branches plain?] [["user with enabled" false] + ["user | user with enabled" true]]] + (let [schema (resolver/validate-schema (source branches) nil {:allow-caveats? true}) + relation (first (:relations schema))] + (is (= 1 (count (:relations schema)))) + (is (= plain? (:eacl.relation/allows-unqualified? relation))) + (is (= [[:eacl.caveat/name "enabled"]] (:eacl.relation/caveats relation))) + (is (= 1 (count (:expressions schema))))))) + +(deftest qualified-admission-retains-reference-and-branch-validation + (doseq [[branches error] [["user with missing" :eacl.schema/invalid-caveat-reference] + ["user with enabled | user with enabled" :eacl.schema/duplicate-relation-branch] + ["user | user" :eacl.schema/duplicate-relation-branch] + ["user:* with enabled" :eacl.schema/unsupported-feature] + ["user#member with enabled" :eacl.schema/unsupported-feature]]] + (is (= error (errors/error-type #(resolver/validate-schema (source branches) nil {:allow-caveats? true})))))) + +(deftest ordinary-schema-shape-does-not-gain-qualifier-fields + (is (= (select-keys (resolver/validate-schema (source "user")) [:relations :expressions]) + (select-keys (resolver/validate-schema (source "user") nil {:allow-caveats? true}) [:relations :expressions])))) diff --git a/modules/eacl/test/eacl/caveats/schema_allowance_contract.cljc b/modules/eacl/test/eacl/caveats/schema_allowance_contract.cljc new file mode 100644 index 00000000..35bd3cb0 --- /dev/null +++ b/modules/eacl/test/eacl/caveats/schema_allowance_contract.cljc @@ -0,0 +1,59 @@ +(ns eacl.caveats.schema-allowance-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.caveats.definition-test :as errors] + [eacl.caveats.schema-admission-test :as schemas] + [eacl.client.orchestration :as orchestration] + [eacl.core :as eacl])) + +(defn check! [{:keys [client writer read-schema entid interleave!]}] + (let [write-schema! (fn [source] + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-schema! client {:schema source})))] + (write-schema! (schemas/source "user")) + (let [native (:native (writer)) + with-db (:with-snapshot native) + tx! (:transact! native) + relation-id #(with-db (fn [db] (entid db [:eacl.relation/resource-type+relation-name+subject-type [:doc :viewer :user]]))) + generation #(with-db (:generation native)) + relation #(first (:relations (with-db read-schema))) + original-id (relation-id) + subject (eacl/spice-object :user "allowance/u") + resource (eacl/spice-object :doc "allowance/doc") + relationship (eacl/->Relationship subject :viewer resource) + qualified (assoc relationship :caveat "enabled" :caveat-context {"flag" true})] + (tx! [{:eacl/id "allowance/u"} {:eacl/id "allowance/doc"}]) + (eacl/create-relationship! client relationship) + (write-schema! (schemas/source "user | user with enabled")) + (is (= original-id (relation-id))) + (is (true? (:eacl.relation/allows-unqualified? (relation)))) + (is (= [[:eacl.caveat/name "enabled"]] (:eacl.relation/caveats (relation)))) + (let [before (generation)] + (is (= :eacl.schema/relationship-qualifier-in-use + (errors/error-type #(write-schema! (schemas/source "user with enabled"))))) + (is (= before (generation)))) + (binding [orchestration/*qualified-authorization-enabled?* true] + (eacl/write-relationships! client [{:operation :touch :relationship qualified}]) + (when interleave! + (is (contains? #{:eacl.schema/relationship-qualifier-in-use :eacl.schema/concurrent-write} + (errors/error-type + #(interleave! + (fn [] (eacl/write-relationships! client [{:operation :touch :relationship relationship}])) + (fn [] (write-schema! (schemas/source "user with enabled"))))))) + (is (true? (:eacl.relation/allows-unqualified? (relation))))) + (eacl/write-relationships! client [{:operation :touch :relationship qualified}]) + (write-schema! (schemas/source "user with enabled")) + (is (= original-id (relation-id))) + (is (false? (:eacl.relation/allows-unqualified? (relation)))) + (is (= :eacl.qualifier/staged-write + (errors/error-type #(eacl/write-relationships! client [{:operation :touch :relationship relationship}])))) + (write-schema! (schemas/source "user | user with enabled")) + (let [before (generation)] + (is (= :eacl.schema/relationship-qualifier-in-use + (errors/error-type #(write-schema! (schemas/source "user"))))) + (is (= before (generation)))) + (eacl/write-relationships! client [{:operation :touch :relationship relationship}]) + (write-schema! (schemas/source "user")) + (is (= original-id (relation-id))) + (is (not (contains? (relation) :eacl.relation/caveats))) + (is (not (contains? (relation) :eacl.relation/allows-unqualified?))) + (is (true? (eacl/can? client {:subject subject :permission :view :resource resource}))))))) diff --git a/modules/eacl/test/eacl/caveats/values_test.cljc b/modules/eacl/test/eacl/caveats/values_test.cljc index ef32da6a..4a5dfd6a 100644 --- a/modules/eacl/test/eacl/caveats/values_test.cljc +++ b/modules/eacl/test/eacl/caveats/values_test.cljc @@ -42,3 +42,29 @@ (is (= {"region" "za" "active" false} (values/merge-context parameters {"region" "us" "active" false} {"region" "za"}))) (is (= :context-type (reason #(values/merge-context parameters {"active" "bad"} {"active" true}))))) + +(deftest host-context-normalization-refines-the-wire-round-trip + (let [scalars [[:bool [false true]] + [:int [-9007199254740991 0 9007199254740991]] + [:string ["" "za" "hé😀中"]] + [:timestamp [[:timestamp -62135596800000] [:timestamp 0] [:timestamp 253402300799999]]]]] + (doseq [[type examples] scalars + [type examples] [[type examples] + [[:list type] [[] (vec examples)]] + [[:map :string type] [{} (zipmap ["a" "😀" "￿"] examples)]]] + value examples + context [{} {"value" value}] + parameters [[["value" type]] {"value" type}]] + (is (= (values/decode-context parameters (values/encode-context parameters context)) + (values/normalize-context parameters context)))) + (doseq [[type examples] scalars + :let [parameters [["a" type] ["b" type]] + contexts [{} {"a" (first examples)} {"a" (last examples)} {"b" (first examples)}]] + request contexts bound contexts] + (is (= (values/decode-context parameters (values/encode-context parameters (merge request bound))) + (values/merge-context parameters request bound)))) + (doseq [[type examples] scalars + invalid [nil [] {"unknown" (first examples)} {"value" #{}}]] + (let [parameters [["value" type]]] + (is (= (reason #(values/decode-context parameters (values/encode-context parameters invalid))) + (reason #(values/normalize-context parameters invalid)))))))) diff --git a/modules/eacl/test/eacl/caveats/write_contention_contract.cljc b/modules/eacl/test/eacl/caveats/write_contention_contract.cljc new file mode 100644 index 00000000..b6fc668d --- /dev/null +++ b/modules/eacl/test/eacl/caveats/write_contention_contract.cljc @@ -0,0 +1,47 @@ +(ns eacl.caveats.write-contention-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.core :as eacl] + [eacl.relationships.staged :as staged])) + +(defn check! [client writer] + (eacl/write-schema! client "definition user {} + definition doc { + relation viewer: user + permission view = viewer + }") + ((get-in (writer) [:native :transact!]) [{:eacl/id "race/user"} {:eacl/id "race/a"} {:eacl/id "race/b"}]) + (let [subject (eacl/spice-object :user "race/user") + a (eacl/spice-object :doc "race/a") b (eacl/spice-object :doc "race/b") + original staged/plan-batch-current + interleave? (atom true) calls (atom 0)] + (with-redefs [staged/plan-batch-current + (fn [writer prepared app-data] + (swap! calls inc) + (let [plan (original writer prepared app-data)] + (when (compare-and-set! interleave? true false) + (eacl/create-relationship! client (eacl/->Relationship subject :viewer b))) + plan))] + (eacl/create-relationship! client (eacl/->Relationship subject :viewer a))) + (is (= 3 @calls) "one outer plan, one competing commit, and exactly one fresh retry") + (is (true? (eacl/can? client {:subject subject :permission :view :resource a}))) + (is (true? (eacl/can? client {:subject subject :permission :view :resource b}))) + (reset! calls 0) + (let [failure (with-redefs [staged/plan-batch-current + (fn [& _] (swap! calls inc) + (throw (ex-info "Concurrent native write" {:error :transact/cas})))] + (try (eacl/delete-relationship! client (eacl/->Relationship subject :viewer a)) nil + (catch #?(:clj Throwable :cljs :default) error (ex-data error))))] + (is (= 8 @calls) "contention retries have a fixed upper bound") + (is (= :eacl/relationship-contention (:type failure))) + (is (true? (eacl/can? client {:subject subject :permission :view :resource a})))))) + +(defn terminal-validation-check! [client] + (let [calls (atom 0) + failure (with-redefs [staged/plan-batch-current + (fn [& _] (swap! calls inc) (staged/error! :qualifier-changed-at-commit))] + (try (eacl/delete-relationship! + client (eacl/->Relationship (eacl/spice-object :user "race/user") + :viewer (eacl/spice-object :doc "race/a"))) nil + (catch #?(:clj Throwable :cljs :default) error (ex-data error))))] + (is (= 1 @calls) "validation faults are not retried as contention") + (is (= :qualifier-changed-at-commit (:reason failure))))) diff --git a/modules/eacl/test/eacl/client/range_reuse_test.cljc b/modules/eacl/test/eacl/client/range_reuse_test.cljc index 58277644..4438013a 100644 --- a/modules/eacl/test/eacl/client/range_reuse_test.cljc +++ b/modules/eacl/test/eacl/client/range_reuse_test.cljc @@ -209,3 +209,21 @@ (is (= (vec (range 20)) (:data (range-reuse/derive-page [:first 30] (page 0 20 {:previous? true}))))) (is (nil? (range-reuse/derive-page [:first 10] {:data [1] :page-info {}}))))) + +(deftest qualified-range-slices-and-composition-preserve-the-entire-retained-certificate + (let [certificate {:start-ms 99 :valid-until-ms 100 :complete? true} + first-page (assoc (page 0 5 {:next? true}) :qualification-certificate certificate) + tier (tier-with (first-window 5) first-page) + hit (range-reuse/lookup! tier :walk (first-window 2)) + partial-hit (range-reuse/lookup! tier :walk (first-window 3 3)) + remainder (assoc (page 5 7 {:next? true :previous? true}) + :qualification-certificate {:start-ms 99 :valid-until-ms 200 :complete? true}) + composed (range-reuse/compose (:partial partial-hit) (:continuation partial-hit) remainder)] + (is (= certificate (:qualification-certificate hit) (get-in hit [:page :qualification-certificate]))) + (is (= certificate (:qualification-certificate partial-hit) (:qualification-certificate composed))) + (is (= certificate (:qualification-certificate (range-reuse/lookup! tier :walk (first-window 2 4))))) + (range-reuse/publish! tier :walk (first-window 2 4) remainder) + (is (= certificate (get-in (range-reuse/lookup! tier :walk (first-window 7)) [:page :qualification-certificate]))) + (is (nil? (:qualification-certificate + (range-reuse/compose (:partial partial-hit) (:continuation partial-hit) + (dissoc remainder :qualification-certificate))))))) diff --git a/modules/eacl/test/eacl/contract_support.cljc b/modules/eacl/test/eacl/contract_support.cljc index 47a3b2de..57df1176 100644 --- a/modules/eacl/test/eacl/contract_support.cljc +++ b/modules/eacl/test/eacl/contract_support.cljc @@ -16,6 +16,13 @@ [eacl.request.counters :as request-counters] [eacl.spicedb.consistency :as consistency])) +(defn without-cache-provenance + [value] + (if (map? value) + (cond-> (dissoc value :cached? :cache-basis :evaluation-time-ms) + (:page-info value) (update :page-info dissoc :start-cursor :end-cursor)) + value)) + (def ->user (partial eacl/spice-object :user)) (def ->platform (partial eacl/spice-object :platform)) (def ->account (partial eacl/spice-object :account)) @@ -512,25 +519,25 @@ (when (eacl/acl? client) (testing "a composed read-only snapshot view preserves every aggregate route" (eacl/with-snapshot [snapshot (eacl/snapshot client)] - (is (= expected - (relationship-resource-ids - (walk-pages #(eacl/read-relationships snapshot %) - scan-query)))) - (is (= expected - (object-ids - (walk-pages #(eacl/lookup-resources snapshot %) - enumerate-query)))) - (let [checks [{:subject subject - :permission :view - :resource (->server "server-1")} - {:subject denied - :permission :view - :resource (->server "server-1")}]] - (is (= (mapv #(eacl/check-permission - snapshot (assoc % :cache? false)) - checks) - (eacl/check-permissions - snapshot {:checks checks :cache? false}))))))))) + (is (= expected + (relationship-resource-ids + (walk-pages #(eacl/read-relationships snapshot %) + scan-query)))) + (is (= expected + (object-ids + (walk-pages #(eacl/lookup-resources snapshot %) + enumerate-query)))) + (let [checks [{:subject subject + :permission :view + :resource (->server "server-1")} + {:subject denied + :permission :view + :resource (->server "server-1")}]] + (is (= (mapv #(eacl/check-permission + snapshot (assoc % :cache? false)) + checks) + (eacl/check-permissions + snapshot {:checks checks :cache? false}))))))))) (def plan-invalidation-schema "definition user {} @@ -584,14 +591,14 @@ repeated-stats (atom {}) changed-stats (atom {})] (with-redefs - [sealed-plan/seal-plan - (fn [& args] - (swap! seals inc) - (apply original-seal args)) - stable-route/check-eids - (fn [options] - (swap! evaluated-plans conj (:plan options)) - (original-check options))] + [sealed-plan/seal-plan + (fn [& args] + (swap! seals inc) + (apply original-seal args)) + stable-route/check-eids + (fn [options] + (swap! evaluated-plans conj (:plan options)) + (original-check options))] (is (false? (binding [backend/*backend-op-stats* first-stats] (uncached-reboot-decision client)))) @@ -772,7 +779,6 @@ (is (true? existing-ghost-preserved?) "bounded safe retraction does not scan for a pre-existing peer-only ghost")) - (defn- read-relationships-data [client query] (:data (eacl/read-relationships client query))) @@ -1464,10 +1470,11 @@ (defn- ordered-generation-proofs? [reader] (letfn [(supported? [snapshot] - (backend/supports? - (get-in snapshot [:basis :adapter]) - :cache-proofs - :ordered-generations))] + (and (not orchestration/*qualified-authorization-enabled?*) + (backend/supports? + (get-in snapshot [:basis :adapter]) + :cache-proofs + :ordered-generations)))] (if (eacl/snapshot? reader) (supported? reader) (eacl/with-snapshot [snapshot (eacl/snapshot reader)] @@ -1648,7 +1655,11 @@ client reverse-query reverse-cursor {})) after-checkpoints (continuation-stats client) after-range (range-stats client)] - (when (and before-checkpoints after-checkpoints) + ;; Qualified exact reconstruction may preserve the checkpoint identity or + ;; select an as-of adapter with a distinct exact scope. Both must reproduce + ;; the original stream; the data assertions below cover either outcome. + (when (and before-checkpoints after-checkpoints + (not (and exact? orchestration/*qualified-authorization-enabled?*))) (if (and exact? (ordered-generation-proofs? client)) (is (or (> (:hits after-checkpoints) (:hits before-checkpoints)) (> (:hits after-range 0) (:hits before-range 0))) @@ -1699,13 +1710,17 @@ (eacl/create-relationship! client denied :auditor (folder 1)) (let [before-checkpoints (continuation-stats client) backend-work (atom {}) - second-page + outcome (binding [backend/*backend-op-stats* backend-work] - (eacl/lookup-subjects client (assoc query :after cursor))) + (continuation-outcome #(eacl/lookup-subjects client (assoc query :after cursor)))) after-checkpoints (continuation-stats client)] - (is (= oracle (into (:data first-page) (:data second-page)))) - (is (= (if (ordered-generation-proofs? client) 1 0) - (get @backend-work :proof-frame 0)) + (if (and orchestration/*qualified-authorization-enabled?* + (not (exact-selection-capable-reader? client))) + (is (= :eacl.pagination/stale-cursor (get-in outcome [:error :type]))) + (do + (is (nil? (:error outcome))) + (is (= oracle (into (:data first-page) (get-in outcome [:value :data])))))) + (is (<= (get @backend-work :proof-frame 0) 1) "reverse cursor, answer, and checkpoint share one request frame") (when (and before-checkpoints after-checkpoints (ordered-generation-proofs? client)) @@ -1737,11 +1752,16 @@ (let [first-page (eacl/lookup-subjects client query) cursor (get-in first-page [:page-info :end-cursor]) before-checkpoints (continuation-stats client) - second-page - (eacl/lookup-subjects older (assoc query :after cursor)) + outcome + (continuation-outcome #(eacl/lookup-subjects older (assoc query :after cursor))) after-checkpoints (continuation-stats client)] (is (some? cursor)) - (is (= oracle (into (:data first-page) (:data second-page)))) + (if orchestration/*qualified-authorization-enabled?* + (is (= :eacl.pagination/invalid-cursor (get-in outcome [:error :type])) + "a live cursor cannot become a pinned cursor") + (do + (is (nil? (:error outcome))) + (is (= oracle (into (:data first-page) (get-in outcome [:value :data])))))) (when (and before-checkpoints after-checkpoints (ordered-generation-proofs? client)) (is (> (:hits after-checkpoints) (:hits before-checkpoints)) @@ -1860,20 +1880,18 @@ (dissoc count-bypass :cached? :cache-basis)) "recursive counts are cache-free equivalent") (is (= [subject] (:data subject-page))) - (is (= (dissoc subject-page :cached? :cache-basis) - (dissoc subject-bypass :cached? :cache-basis)) - "recursive reverse pages, including cursors, are cache-free equivalent")) - (is (= (dissoc forward-page :cached? :cache-basis) - (dissoc - (eacl/lookup-resources client (assoc query :cache? false)) - :cached? :cache-basis)) - "recursive forward pages, including cursors, are cache-free equivalent") - (is (= (dissoc reverse-page :cached? :cache-basis) - (dissoc + (is (= (without-cache-provenance subject-page) + (without-cache-provenance subject-bypass)) + "recursive reverse page contents are cache-free equivalent")) + (is (= (without-cache-provenance forward-page) + (without-cache-provenance + (eacl/lookup-resources client (assoc query :cache? false)))) + "recursive forward page contents are cache-free equivalent") + (is (= (without-cache-provenance reverse-page) + (without-cache-provenance (eacl/lookup-resources - client (assoc reverse-query :cache? false)) - :cached? :cache-basis)) - "recursive backward pages, including cursors, are cache-free equivalent") + client (assoc reverse-query :cache? false)))) + "recursive backward page contents are cache-free equivalent") (is (= recursive-connected-folder-count (:count (eacl/count-resources @@ -1901,7 +1919,9 @@ (is (= (mapv folder (range recursive-connected-folder-count)) (:data after-unrelated-write)))) - (assert-proof-equivalent-write-continuation! + ((if orchestration/*qualified-authorization-enabled?* + assert-relevant-write-continuation! + assert-proof-equivalent-write-continuation!) client query oracle-stream forward-page reverse-page) ;; Re-establish adjacent frontiers at the current basis. The preceding @@ -2048,12 +2068,6 @@ (get-in hard-outcome [:value :allowed?])] (mapv :allowed? (:value batch-outcome)))))))) -(defn- without-cache-provenance - [value] - (if (map? value) - (dissoc value :cached? :cache-basis) - value)) - (defn- operation-outcome [call request] (try @@ -2069,8 +2083,9 @@ (defn- assert-cache-differential-call! [label call request] - ;; The semantic comparison includes authenticated response tokens. Keep - ;; their issuance time equal even when the three calls cross a second. + ;; Keep causal token issuance time equal. Live cursors separately capture + ;; trusted evaluation time, so compare their page bounds rather than bytes; + ;; dedicated continuation contracts validate the authenticated cursors. (let [issued-at (causal-token/now-seconds)] (with-redefs [causal-token/now-seconds (constantly issued-at)] (let [enabled (operation-outcome call request) @@ -2079,7 +2094,7 @@ (is (= (semantic-outcome enabled) (semantic-outcome repeated) (semantic-outcome bypassed)) - (str label " preserves value, order, cursors, and errors")) + (str label " preserves value, order, page bounds, and errors")) (when-let [bypassed-value (:value bypassed)] (when (and (map? bypassed-value) (contains? bypassed-value :cached?)) diff --git a/modules/eacl/test/eacl/core_test.cljc b/modules/eacl/test/eacl/core_test.cljc index d1df8bb6..3fcbd3c8 100644 --- a/modules/eacl/test/eacl/core_test.cljc +++ b/modules/eacl/test/eacl/core_test.cljc @@ -8,6 +8,39 @@ (swap! calls conj [operation request]) response) +(deftest boolean-compatibility-requires-definite-grants-and-preserves-operational-errors + (let [respond (atom (constantly nil)) + target (reify eacl/IAuthorizationReader + (-check-permission [_ _] (@respond))) + requests [#(eacl/can? target {}) + #(eacl/can? target {} :view {}) + #(eacl/can? target {} :view {} :fully-consistent)]] + (doseq [[decision expected] + [[{:allowed? true} true] + [{:allowed? true :permissionship :has-permission} true] + [{:allowed? false :permissionship :no-permission} false] + [{:allowed? false :permissionship :conditional-permission} false] + [{:allowed? true :permissionship :conditional-permission} false] + [{:allowed? :conditional} false] + [{:allowed? nil} false]] + request requests] + (reset! respond (constantly decision)) + (is (= expected (request)))) + (doseq [request requests] + (reset! respond #(throw (ex-info "Qualified failure" + {:type :eacl.authorization/evaluation-failure}))) + (is (false? (request)))) + (doseq [type [:eacl.execution/cancelled :eacl.execution/deadline-exceeded + :eacl.execution/resource-limit-exceeded :eacl.caveat/invalid + :backend/failure] + request requests] + (let [error (ex-info "Must propagate" {:type type})] + (reset! respond #(throw error)) + (is (identical? error + (try (request) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) caught + caught)))))))) + (defrecord RecordingSnapshot [calls released basis token] eacl/IAuthorizationReader (-check-permission [_ request] diff --git a/modules/eacl/test/eacl/engine/scan_cache_test.cljc b/modules/eacl/test/eacl/engine/scan_cache_test.cljc index 1e67b3de..5b91e613 100644 --- a/modules/eacl/test/eacl/engine/scan_cache_test.cljc +++ b/modules/eacl/test/eacl/engine/scan_cache_test.cljc @@ -36,6 +36,38 @@ (is (= [5 3 1] (scan-cache/serve descending 7 64 :desc))) (is (= [3 1] (scan-cache/serve descending 4 64 :desc))))))) +(deftest compact-prefixes-keep-qualifiers-and-use-endpoint-bounds-test + (let [edges [1 [3 103] 5 [7 107] [9 109]] + entry {:prefix edges :exhausted? true}] + (is (= [[7 107] [9 109]] (scan-cache/serve entry 5 10 :asc))) + (is (= [5 [7 107]] (scan-cache/serve entry 3 2 :asc))) + (is (= [1] (scan-cache/serve (assoc entry :prefix (vec (rseq edges))) + 3 10 :desc))) + (is (= {:prefix [1 [3 103] 5 [7 107]] :exhausted? true} + (scan-cache/extend-entry {:prefix [1 [3 103]] :exhausted? false} + 3 [5 [7 107]] 3 :asc 8))) + (is (= (scan-cache/descriptor-key descriptor) + (scan-cache/descriptor-key (assoc descriptor :include-qualifier? false)))) + (is (not= (scan-cache/descriptor-key descriptor) + (scan-cache/descriptor-key (assoc descriptor :include-qualifier? true)))))) + +(deftest compact-and-ordinary-responses-never-share-memo-or-resident-prefixes-test + (let [calls (atom []) + tier (scan-cache/tier {}) + inner (fn [d] + (swap! calls conj d) + (if (:include-qualifier? d) [[1 101] 3] [1 3])) + options {:tier tier :scope-fn (constantly [:basis 1])} + fetch (scan-cache/caching-fetch-fn inner (assoc options :memo (scan-cache/memo))) + d (assoc descriptor :limit 8)] + (is (= [1 3] (fetch d))) + (is (= [[1 101] 3] (fetch (assoc d :include-qualifier? true)))) + (is (= [[1 101] 3] (fetch (assoc d :include-qualifier? true)))) + (let [fresh (scan-cache/caching-fetch-fn inner (assoc options :memo (scan-cache/memo)))] + (is (= [1 3] (fresh d))) + (is (= [[1 101] 3] (fresh (assoc d :include-qualifier? true))))) + (is (= 2 (count @calls))))) + (deftest extend-entry-keeps-prefixes-from-the-scan-start-test (testing "first chunk from the start" (is (= {:prefix [1 2 3] :exhausted? false} diff --git a/modules/eacl/test/eacl/engine/stable_page_evidence_test.cljc b/modules/eacl/test/eacl/engine/stable_page_evidence_test.cljc new file mode 100644 index 00000000..25a12717 --- /dev/null +++ b/modules/eacl/test/eacl/engine/stable_page_evidence_test.cljc @@ -0,0 +1,154 @@ +(ns eacl.engine.stable-page-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.evidence :as evidence] + [eacl.backend.v8 :as backend] + [eacl.engine.stable-page :as page] + [eacl.engine.stable-reducer-evidence-test :as fixture])) + +(defn page! [env options] + (with-redefs [qualification/qualify (:qualify env)] + (page/edge-page (merge (:options env) + {:direction :forward :anchor-eid 1 :page-size 1 :checkpoint-key ::pages} + options)))) + +(deftest qualified-page-evidence-survives-lookahead-replay-and-backward-windows + (doseq [chunk [1 3] size [1 2 3] retained? [false true]] + (let [env (fixture/environment fixture/rows fixture/leaves {:physical-chunk-size chunk}) + full (fixture/run env) + options {:page-size size :checkpoints (when retained? (page/make-checkpoint-store))} + pages (loop [after nil pages []] + (let [result (page! env (assoc options :after after)) + pages (conj pages result)] + (if (:has-next? result) + (recur {:ordinal (+ (:start-ordinal result) (count (:eids result))) + :eid (peek (:eids result))} pages) + pages)))] + (is (= (:results full) (into [] (mapcat :eids) pages))) + (is (= (:result-evidence full) (apply merge (map :result-evidence pages)))) + (doseq [result pages] + (is (= (set (:eids result)) (set (keys (:result-evidence result))))) + (when (pos? (:start-ordinal result)) + (let [after {:ordinal (:start-ordinal result) + :eid (nth (:results full) (dec (:start-ordinal result)))}] + (is (= result (page! env (assoc options :after after))))))) + (doseq [ordinal [2 3 4]] + (let [result (page! env (assoc options :before {:ordinal ordinal :eid (nth (:results full) (dec ordinal))})) + expected (subvec (:results full) (max 0 (- ordinal 1 size)) (dec ordinal))] + (is (= expected (:eids result))) + (is (= (select-keys (:result-evidence full) expected) (:result-evidence result))))) + (let [result (page! env (assoc options :last-window? true)) + expected (subvec (:results full) (- (count (:results full)) size))] + (is (= expected (:eids result))) + (is (= (select-keys (:result-evidence full) expected) (:result-evidence result))))))) + +(deftest checkpoint-retention-partitions-qualified-request-scope + (let [store (page/make-checkpoint-store) + env (fixture/environment fixture/rows fixture/leaves {}) + _ (page! env {:checkpoints store}) + request (qualification/request (assoc (get-in env [:options :qualification]) :context {"changed" true})) + changed (fixture/environment fixture/rows (assoc fixture/leaves 101 true 102 true) + {:qualification request}) + options {:after {:ordinal 1 :eid 100}} + uncached (page! changed options) + cached (page! changed (assoc options :checkpoints store))] + (is (= [300] (:eids cached))) + (is (= uncached cached)) + (is (empty? (:result-evidence cached))) + (reset! (:commands env) []) + (let [original (page! env {:checkpoints store :after {:ordinal 1 :eid 100} :raw-candidates? true})] + (is (= #{300} (set (keys (:result-evidence original))))) + (is (empty? @(:commands env)))))) + +(defn identity-adapter [] + (backend/make-adapter + {:id :qualified-pages :capabilities {} + :operations (merge (zipmap backend/required-snapshot-operations + (repeat (fn [& _] (throw (ex-info "Unexpected page adapter read" {}))))) + {:native-revision (constantly 1) + :object-id->internal identity + :internal-id->object identity})})) + +(deftest standalone-qualified-tokens-bind-time-context-and-policy + (let [env (fixture/environment fixture/rows fixture/leaves {}) + options (merge (:options env) + {:adapter (identity-adapter) :direction :forward :anchor [:user 1] + :page-size 1 :security-key "qualified-page-key-0123456789abcdef"}) + first-page (with-redefs [qualification/qualify (:qualify env)] (page/page options)) + token (get-in first-page [:page-info :end-cursor]) + options (assoc options :after token)] + (is (= [100] (:data first-page))) + (is (= #{100} (set (keys (:result-evidence first-page))))) + (doseq [changed [(dissoc options :qualification) + (assoc options :result-policy :definite) + (update options :qualification #(qualification/request (assoc % :time 100))) + (update options :qualification #(qualification/request (assoc % :context {"unused" true})))]] + (reset! (:commands env) []) + (is (= :eacl.page/invalid-cursor (:eacl/error (fixture/error-data #(page/page changed))))) + (is (empty? @(:commands env)))) + (let [next-page (with-redefs [qualification/qualify (:qualify env)] (page/page options))] + (is (= [300] (:data next-page))) + (is (= #{300} (set (keys (:result-evidence next-page)))))))) + +(deftest pending-qualified-lookahead-needs-no-repeat-probe + (let [env (fixture/environment fixture/rows fixture/leaves {}) + store (page/make-checkpoint-store) + _ (page! env {:checkpoints store}) + _ (reset! (:commands env) []) + result (page! env {:checkpoints store :after {:ordinal 1 :eid 100} :raw-candidates? true})] + (is (= [300] (:eids result))) + (is (= #{300} (set (keys (:result-evidence result))))) + (is (empty? @(:commands env))))) + +(deftest qualified-checkpoints-reuse-only-inside-the-certified-interval + (doseq [start [99 120] time [150 200]] + (let [env (fixture/environment fixture/rows fixture/leaves {}) + store (page/make-checkpoint-store) + put page/checkpoint-put! + _ (with-redefs [page/checkpoint-put! + (fn [store key checkpoint] + (put store key (assoc checkpoint :qualification-certificate + {:start-ms start :valid-until-ms 200 :complete? true})))] + (page! env {:checkpoints store})) + current (assoc-in env [:options :qualification] + (qualification/request (assoc (get-in env [:options :qualification]) :time time))) + options {:after {:ordinal 1 :eid 100} :raw-candidates? true} + _ (reset! (:commands env) []) + cached (page! current (assoc options :checkpoints store))] + (is (= (and (= start 99) (< time 200)) (empty? @(:commands env)))) + (is (= [300] (:eids cached))) + (is (= cached (page! current options)))))) + +(deftest definite-lookahead-has-the-same-sparse-shape-as-replay + (let [env (fixture/environment (mapv #(assoc % 3 nil) fixture/rows) {} {}) + store (page/make-checkpoint-store) + _ (page! env {:checkpoints store}) + options {:after {:ordinal 1 :eid 100}} + cached (page! env (assoc options :checkpoints store)) + replayed (page! env options)] + (is (= cached replayed)) + (is (empty? (:result-evidence cached))))) + +(def corruptions + [#(dissoc % :qualification-certificate) + #(assoc % :qualification-certificate {:start-ms 99 :valid-until-ms 99 :complete? true}) + #(assoc % :qualification-certificate {:start-ms 98 :valid-until-ms nil :complete? false}) + #(dissoc % :pending-evidence) + #(assoc % :pending-evidence {}) + #(assoc % :pending-evidence {300 false}) + #(assoc % :pending-evidence {300 (evidence/fault :test/failure :corrupt)}) + #(update % :state dissoc :qualified)]) + +(deftest incomplete-or-faulty-qualified-checkpoints-fall-back-to-replay + (doseq [corrupt corruptions] + (let [env (fixture/environment fixture/rows fixture/leaves {}) + store (page/make-checkpoint-store) + put page/checkpoint-put! + _ (with-redefs [page/checkpoint-put! (fn [store key checkpoint] (put store key (corrupt checkpoint)))] + (page! env {:checkpoints store})) + _ (reset! (:commands env) []) + options {:after {:ordinal 1 :eid 100}} + cached (page! env (assoc options :checkpoints store))] + (is (seq @(:commands env))) + (is (= cached (page! env options))) + (is (= #{300} (set (keys (:result-evidence cached)))))))) diff --git a/modules/eacl/test/eacl/engine/stable_reducer_evidence_test.cljc b/modules/eacl/test/eacl/engine/stable_reducer_evidence_test.cljc new file mode 100644 index 00000000..aefbd97a --- /dev/null +++ b/modules/eacl/test/eacl/engine/stable_reducer_evidence_test.cljc @@ -0,0 +1,198 @@ +(ns eacl.engine.stable-reducer-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.evidence-test :as completions] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as fixture] + [eacl.engine.stable-reducer :as reducer] + [eacl.engine.stable-route :as route] + [eacl.backend.v8 :as backend] + [eacl.relationships.edge :as edge])) + +(def root [:folder :view]) +(def direct {:rule :relation :ordinal 0 :node root :subject-type :user + :resource-type :folder :relation-eid 10}) +(def arrow {:rule :arrow-permission :ordinal 1 :node root :target-node root + :resource-type :folder :intermediate-type :folder :via-relation-eid 20}) +(def plan {:root root :indexes {:forward-seeds {:user [direct]} + :forward-consumers {root [arrow]} + :reverse-rules {root [direct arrow]}}}) +(def rows [[1 10 100 101] [1 10 200 102] + [100 20 300 nil] [200 20 300 nil] [300 20 400 103] [400 20 100 nil]]) +(def leaves {101 completions/x 102 completions/y + 103 (evidence/combine :exclusion true completions/x)}) + +(defn environment [rows leaves extra] + (let [commands (atom []) probes (atom []) reads (atom []) + fetch (fn [{:keys [operation subject-eid resource-eid relation-eid bound-eid limit include-qualifier?] + :as command}] + (swap! commands conj command) + (->> rows + (keep (fn [[s r o q]] + (when (and (= r relation-eid) + (= (if (= operation :subject->resources) s o) + (if (= operation :subject->resources) subject-eid resource-eid))) + (let [eid (if (= operation :subject->resources) o s)] + (when (or (nil? bound-eid) (> eid bound-eid)) + (if include-qualifier? (edge/pack eid q) eid)))))) + (sort-by edge/endpoint) (take limit) vec)) + options (merge {:plan plan :fetch-fn fetch :subject-type :user :subject-eid 1 + :resource-eid 400 :target reducer/exhaustion-target + :qualification (fixture/request) :physical-chunk-size 1 + :result-policy :detailed} extra) + complete (:candidate-evidence-fn (route/discovery-options options (:direction extra :forward))) + options (assoc options :candidate-evidence-fn + (fn [eid item] + (swap! probes conj eid) + (complete eid item)))] + {:options options :commands commands :probes probes :reads reads + :qualify (fn [_ relation row] + (swap! reads conj [relation row]) + (if-let [qid (edge/qualifier-id row)] (get leaves qid) (some? row)))})) + +(defn run [env] + (with-redefs [qualification/qualify (:qualify env)] + ((if (= :reverse (:direction (:options env))) reducer/run-reverse reducer/run-forward) + (:options env)))) + +(defn value-at [result eid] + (get-in result [:result-evidence eid] true)) + +(deftest conditional-prefixes-revisit-cycles-without-duplicate-discoveries + (doseq [chunk [1 2 64] cap [0 1 16]] + (let [env (environment rows leaves {:physical-chunk-size chunk :sidecar-cap cap}) + full (run env)] + (is (= [100 300 200 400] (:results full))) + (doseq [eid [100 300]] + (is (= #{1 2 3} (completions/completions (value-at full eid) [completions/x completions/y])))) + (is (= #{2 3} (completions/completions (value-at full 200) [completions/x completions/y]))) + (is (= #{2} (completions/completions (value-at full 400) [completions/x completions/y]))) + (is (= [100 300 200 400] @(:probes env))) + (is (every? :include-qualifier? @(:commands env))) + (is (< (:transitions full) 100)) + (doseq [target [1 2 3]] + (let [prefix (run (assoc-in env [:options :target] target)) + suffix (with-redefs [qualification/qualify (:qualify env)] + (reducer/resume (:options env) (reducer/history-free prefix)))] + (is (= (:results full) (into (:results prefix) (:results suffix)))) + (is (= (:result-evidence full) (merge (:result-evidence prefix) (:result-evidence suffix))))))))) + +(deftest reverse-prefixes-conjoin-and-complete-the-whole-root + (doseq [chunk [1 2 64]] + (let [env (environment rows leaves {:direction :reverse :physical-chunk-size chunk}) + result (run env)] + (is (= [1] (:results result))) + (is (= #{2} (completions/completions (value-at result 1) [completions/x completions/y]))) + (is (= [1] @(:probes env)))))) + +(deftest ordinary-qualified-traversal-retains-the-existing-fast-path + (let [ordinary (mapv #(assoc % 3 nil) rows) + env (environment ordinary {} {}) + qualified (run env) + plain (reducer/run-forward (dissoc (:options env) :qualification :candidate-evidence-fn))] + (is (= (:results plain) (:results qualified))) + (is (= (:admissions plain) (:admissions qualified))) + (is (= (:transitions plain) (:transitions qualified))) + (is (empty? (:result-evidence qualified))) + (is (empty? @(:probes env))) + (is (empty? (get-in (reducer/history-free qualified) [:qualified :weights]))))) + +(deftest detailed-counts-and-windows-share-definite-filtering + (doseq [policy [:definite :detailed] sink [:collect :count :window]] + (let [env (environment rows leaves {:result-policy policy :result-sink sink :result-window-size 2}) + result (run env)] + (is (= (if (= :detailed policy) 4 0) (:discovered result))) + (is (zero? (:definite-count result))) + (is (= (:discovered result) (:conditional-count result))) + (is (= (if (and (= :detailed policy) (not= :count sink)) + (if (= :window sink) [200 400] [100 300 200 400]) []) + (:results result))) + (is (= (set (:results result)) (set (keys (:result-evidence result)))))) + (let [env (environment rows (assoc leaves 101 true) {:result-policy policy :result-sink sink :result-window-size 2}) + result (run env)] + (is (= 2 (:definite-count result))) + (is (= (if (= :detailed policy) 2 0) (:conditional-count result)))))) + +(defn error-data [f] + (try (f) nil (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error (ex-data error)))) + +(def bounded-options + [[:max-admissions 2] + [:max-transitions 2] + [:max-commands 1] + [:max-values 1] + [:max-stack 1] + [:max-evidence-size 1]]) + +(deftest weighted-admission-and-resume-keep-operational-bounds + (doseq [[limit maximum] bounded-options] + (let [env (environment rows leaves {limit maximum})] + (is (= limit (:limit (error-data #(run env))))))) + (let [env (environment rows leaves {}) + prefix (run (assoc-in env [:options :target] 1)) + checkpoint (reducer/history-free prefix)] + (is (not-any? #(contains? (:qualified checkpoint) %) + [:request :candidate-evidence-fn :result-evidence])) + (doseq [options [(dissoc (:options env) :qualification) + (assoc (:options env) :result-policy :definite) + (update (:options env) :qualification #(qualification/request (assoc % :time 100)))]] + (reset! (:commands env) []) + (is (= :eacl.reducer/checkpoint-scope-mismatch + (:type (error-data #(reducer/resume options checkpoint))))) + (is (empty? @(:commands env))))) + (let [env (environment rows (assoc leaves 101 (evidence/fault :test/fault :invalid)) {})] + (is (= :eacl.authorization/evaluation-failure (:type (error-data #(run env)))))) + (let [env (environment rows leaves {:result-policy nil})] + (is (= :eacl.reducer/invalid-result-policy (:type (error-data #(run env))))))) + +(deftest excluded-prefixes-consume-bounded-physical-work + (let [env (environment (mapv (fn [eid] [1 10 eid 101]) (range 100 200)) {101 false} + {:max-values 7 :physical-chunk-size 1})] + (is (= :max-values (:limit (error-data #(run env))))) + (is (= 8 (count @(:commands env)))) + (is (= 7 (count @(:reads env)))) + (is (empty? @(:probes env))))) + +(deftest direct-discovery-witnesses-avoid-repeating-the-known-tuple + (doseq [direction [:forward :reverse]] + (let [env (environment [[1 10 100 101]] {101 completions/x} + {:direction direction :resource-eid 100}) + result (run env)] + (is (= (if (= :forward direction) [100] [1]) (:results result))) + (is (= 1 (count (filter #(= 101 (edge/qualifier-id (second %))) @(:reads env)))))))) + +(deftest capped-count-categories-exclude-the-lookahead + (doseq [limit [0 1 2 3 4 5] policy [:definite :detailed] ordinary? [false true]] + (let [env (environment rows (cond-> leaves ordinary? (assoc 101 true)) + {:result-policy policy :count-limit limit :subject-id 1}) + order (run env) + prefix (take limit (:results order)) + definite (count (filter #(evidence/has? (value-at order %)) prefix)) + adapter (backend/make-adapter + {:id :discovery-count :capabilities {} + :operations (assoc (zipmap backend/required-snapshot-operations + (repeat (fn [& _] (throw (ex-info "Unexpected count adapter read" {}))))) + :object-id->internal identity)}) + result (with-redefs [qualification/qualify (:qualify env)] + (route/count-resources (assoc (:options env) :adapter adapter)))] + (is (= (count prefix) (:count result))) + (is (= definite (:definite-count result))) + (is (= (- (count prefix) definite) (:conditional-count result))) + (is (= (> (:discovered order) limit) (:truncated? result)))))) + +(deftest temporal-revisions-replay-a-buffered-prefix + (let [rows [[1 10 100 101] [100 20 200 nil] [100 20 300 nil] [200 20 100 102]] + leaves {101 (evidence/with-certificate completions/x 200 true) + 102 (evidence/with-certificate true 100 true)} + narrow (run (environment rows leaves {:physical-chunk-size 1})) + wide (run (environment rows leaves {:physical-chunk-size 64}))] + (is (= [100 200 300] (:results narrow) (:results wide))) + (is (= (:result-evidence narrow) (:result-evidence wide))) + (is (= (get-in narrow [:qualified :weights]) (get-in wide [:qualified :weights]))) + (doseq [target [1 2]] + (let [env (environment rows leaves {:physical-chunk-size 64}) + prefix (run (assoc-in env [:options :target] target)) + suffix (with-redefs [qualification/qualify (:qualify env)] + (reducer/resume (:options env) (reducer/history-free prefix)))] + (is (= (:result-evidence narrow) (merge (:result-evidence prefix) (:result-evidence suffix)))) + (is (= (get-in narrow [:qualified :weights]) (get-in suffix [:qualified :weights]))))))) diff --git a/modules/eacl/test/eacl/engine/stable_route_evidence_test.cljc b/modules/eacl/test/eacl/engine/stable_route_evidence_test.cljc new file mode 100644 index 00000000..fc9ae361 --- /dev/null +++ b/modules/eacl/test/eacl/engine/stable_route_evidence_test.cljc @@ -0,0 +1,141 @@ +(ns eacl.engine.stable-route-evidence-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is testing]] + [eacl.authorization.evidence :as evidence] + [eacl.authorization.evidence-test :as completions] + [eacl.authorization.qualification :as qualification] + [eacl.authorization.qualification-test :as qualification-fixture] + [eacl.engine.stable-route :as route] + [eacl.relationships.edge :as edge])) + +(def root [:doc :view]) +(def target [:group :member]) +(defn direct [node relation] + {:rule :relation :resource-type (first node) :subject-type :user + :node node :relation-eid relation}) +(defn arrow [node via target-node] + {:rule :arrow-permission :resource-type (first node) :node node + :via-relation-eid via :intermediate-type (first target-node) + :target-node target-node}) +(defn sealed [rules] {:root root :indexes {:reverse-rules rules}}) + +(defn run + "Physical fixture contains [subject relation resource qualifier] rows. + The independent qualification injection keeps this test about traversal." + [plan rows leaves & [extra]] + (let [calls (atom []) + reads (atom []) + fetch (fn [{:keys [operation subject-eid resource-eid relation-eid + bound-eid limit include-qualifier?] :as command}] + (swap! calls conj command) + (->> rows + (keep (fn [[s r o q]] + (when (and (= r relation-eid) + (= (if (= operation :subject->resources) s o) + (if (= operation :subject->resources) subject-eid resource-eid))) + (let [eid (if (= operation :subject->resources) o s)] + (when (or (nil? bound-eid) (> eid bound-eid)) + (if include-qualifier? (edge/pack eid q) eid)))))) + (sort-by edge/endpoint) (take limit) vec)) + answer (with-redefs [qualification/qualify + (fn [_ relation value] + (swap! reads conj [relation value]) + (if-let [qid (edge/qualifier-id value)] + (get leaves qid) + (some? value)))] + (route/check-eids + (merge {:plan plan :fetch-fn fetch :subject-type :user + :subject-eid 1 :resource-eid 100 + :qualification ::injected :physical-chunk-size 1} + extra)))] + {:answer answer :commands @calls :qualification-reads @reads})) + +(defn known-options [rule value intermediate] + (let [request (qualification-fixture/request)] + {:qualification request + :known-witness (cond-> {:point [root :user 1 100] :rule rule :evidence value + :scope (qualification/exact-reuse-identity request)} + intermediate (assoc :intermediate intermediate))})) + +(deftest known-direct-and-child-witnesses-complete-only-remaining-alternatives + (doseq [rule [(direct root 10) {:rule :self-permission :node root :target-node target}]] + (let [plan (sealed {root [rule (direct root 11)] target [(direct target 10)]}) + rows [[1 10 100 101] [1 11 100 102]] + x (evidence/with-certificate completions/x 110 true) + y (evidence/with-certificate completions/y 120 true) + result (run plan rows {101 x 102 y} (known-options rule x nil))] + (is (= #{1 2 3} (completions/completions (:answer result) [completions/x completions/y]))) + (is (= 110 (evidence/valid-until (:answer result)))) + (is (= [11] (mapv :relation-eid (:commands result)))) + (doseq [value [(evidence/with-certificate true 110 true) (evidence/fault :test/failure :invalid)]] + (let [result (run plan rows {101 value 102 y} (known-options rule value nil))] + (is (= value (:answer result))) + (is (= [] (:commands result)))))))) + +(deftest known-arrow-binding-is-not-requalified-or-reprobed + (doseq [rule [(arrow root 20 target) + {:rule :arrow-relation :node root :resource-type :doc :via-relation-eid 20 + :intermediate-type :group :target-relation-eid 30 :target-subject-type :user}] + chunk [1 2]] + (let [plan (sealed {root [rule] target [(direct target 30)]}) + rows [[2 20 100 201] [3 20 100 202] [1 30 2 301] [1 30 3 302]] + result (run plan rows {201 completions/x 202 completions/y 301 true 302 true} + (assoc (known-options rule completions/x 2) :physical-chunk-size chunk))] + (is (= #{1 2 3} (completions/completions (:answer result) [completions/x completions/y]))) + (is (not-any? #(= 1 (:bound-eid %)) (:commands result))) + (is (not-any? #(contains? #{201 301} (edge/qualifier-id (second %))) + (:qualification-reads result)))))) + +(deftest known-witness-scope-is-validated-before-a-definite-shortcut + (let [rule (direct root 10) plan (sealed {root [rule]}) + options (known-options rule true nil)] + (doseq [invalid [(assoc-in options [:known-witness :point] [root :user 2 100]) + (assoc-in options [:known-witness :scope] [:other-basis]) + (assoc-in options [:known-witness :rule] (direct root 11)) + (assoc-in options [:known-witness :extra] true) + (assoc-in options [:known-witness :evidence] (evidence/with-certificate true 99 true)) + (assoc options :qualification nil)]] + (is (= :eacl.route/invalid-witness + (try (run plan [] {} invalid) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error + (:type (ex-data error))))))))) + +(deftest direct-union-retains-residuals-deadlines-and-failures + (let [plan (sealed {root [(direct root 10) (direct root 11)]}) + rows [[1 10 100 101] [1 11 100 102]] + x (evidence/with-certificate completions/x 110 true) + y (evidence/with-certificate completions/y 120 true) + answer (:answer (run plan rows {101 x 102 y}))] + (is (= #{1 2 3} (completions/completions answer [completions/x completions/y]))) + (is (= 110 (evidence/valid-until answer))) + (is (= ["x" "y"] (evidence/missing-fields answer))) + (is (evidence/fault? (:answer (run plan rows {101 (evidence/fault :test/failure :invalid) + 102 true})))) + (is (true? (:answer (run plan rows {101 true 102 (evidence/fault :test/failure :invalid)})))))) + +(deftest bidirectional-arrows-continue-past-conditional-and-expired-candidates + (doseq [rule [(arrow root 20 target) + {:rule :arrow-relation :resource-type :doc :via-relation-eid 20 + :intermediate-type :group :target-relation-eid 30 :target-subject-type :user}]] + (let [plan (sealed {root [rule] target [(direct target 30)]}) + rows [[2 20 100 201] [3 20 100 202] [1 30 2 301] [1 30 3 302]] + result (run plan rows {201 completions/x 202 completions/y 301 true 302 true})] + (is (= #{1 2 3} (completions/completions (:answer result) [completions/x completions/y]))) + (is (every? :include-qualifier? (:commands result))) + (is (true? (:answer (run plan rows {201 completions/x 202 true 301 true 302 true})))) + (let [expired (run plan rows {201 false 202 false + 301 (evidence/fault :test/failure :invalid) + 302 (evidence/fault :test/failure :invalid)})] + (is (false? (:answer expired))) + (is (not-any? #(= 30 (first %)) (:qualification-reads expired))))))) + +(deftest recursive-prefixes-join-alternate-paths-and-stop-on-cycles + (let [plan (sealed {root [(arrow root 20 root) (direct root 10)]}) + rows [[2 20 100 201] [3 20 100 202] [4 20 2 nil] [4 20 3 nil] + [2 20 4 nil] [1 10 4 nil]] + x (evidence/with-certificate completions/x 110 true) + y (evidence/with-certificate completions/y 120 true) + result (run plan rows {201 x 202 y})] + (is (= #{1 2 3} (completions/completions (:answer result) [completions/x completions/y]))) + (is (= 110 (evidence/valid-until (:answer result)))) + (is (evidence/complete? (:answer result))) + (is (< (count (:commands result)) 50)))) diff --git a/modules/eacl/test/eacl/formal/executed_mutation_controls.cljc b/modules/eacl/test/eacl/formal/executed_mutation_controls.cljc index ea2f8999..a6235207 100644 --- a/modules/eacl/test/eacl/formal/executed_mutation_controls.cljc +++ b/modules/eacl/test/eacl/formal/executed_mutation_controls.cljc @@ -682,8 +682,8 @@ [] (let [args [{:advanced-datoms 0 :queued-work 0 :fetched-values 0} {:advanced-datoms 6 :queued-work 4 :fetched-values 2} - {:candidates-examined 0 :probes 0 :publications 0} - {:candidates-examined 3 :probes 2 :publications 1} + {:commands 0 :fetched-values 0 :candidates-examined 0 :probes 0 :publications 0} + {:commands 6 :fetched-values 2 :candidates-examined 3 :probes 2 :publications 1} 2] gate #(= 6 (:commands (apply batch/aggregate-counters args))) original batch/aggregate-counters] diff --git a/modules/eacl/test/eacl/formal/public_source_closure_test.clj b/modules/eacl/test/eacl/formal/public_source_closure_test.clj index b33b4db6..084f77f8 100644 --- a/modules/eacl/test/eacl/formal/public_source_closure_test.clj +++ b/modules/eacl/test/eacl/formal/public_source_closure_test.clj @@ -51,6 +51,14 @@ (def invoke-symbols '#{backend/invoke eacl.backend.v8/invoke}) +(def captured-direct-invokers + ;; These fixed-operation helpers preserve the same metering, observer, and + ;; runtime-guard boundary as invoke. Their callers supply no operation key. + {'backend/direct-match-invoker :direct-match? + 'eacl.backend.v8/direct-match-invoker :direct-match? + 'backend/direct-edge-invoker :direct-edge + 'eacl.backend.v8/direct-edge-invoker :direct-edge}) + (defn- invoke-calls [[file source] features] (mapcat @@ -59,12 +67,13 @@ (walk/prewalk (fn [node] (when (and (seq? node) - (contains? invoke-symbols (first node))) + (or (contains? invoke-symbols (first node)) + (contains? captured-direct-invokers (first node)))) (swap! calls conj {:file (.getPath file) - :operation (nth node 2 ::missing) + :operation (get captured-direct-invokers (first node) (nth node 2 ::missing)) :form node})) node) form) diff --git a/modules/eacl/test/eacl/relationships/edge_contract.cljc b/modules/eacl/test/eacl/relationships/edge_contract.cljc new file mode 100644 index 00000000..0b64d6a2 --- /dev/null +++ b/modules/eacl/test/eacl/relationships/edge_contract.cljc @@ -0,0 +1,88 @@ +(ns eacl.relationships.edge-contract + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is]] + [eacl.authorization.evidence :as evidence] + [eacl.request.counters :as counters] + [eacl.authorization.qualification :as qualification] + [eacl.relationships.edge :as edge] + [eacl.relationships.staged :as staged] + [eacl.relationships.storage :as storage])) + +(def schema "definition user {}\ndefinition doc {\n relation viewer: user\n permission view = viewer\n}") + +(defn error-type [f] + (try (f) nil + (catch #?(:clj Throwable :cljs :default) error (:eacl/error (ex-data error))))) + +(defn check! [{:keys [write-schema! writer entid forward reverse direct adapter with-basis]}] + (write-schema! schema) + (let [w (writer) native (:native w) snapshot (:snapshot native) tx! (:transact! native) + objects (mapv #(hash-map :eacl/id (str "edge/" %)) (range 12))] + (tx! objects) + (let [db (snapshot) + ids (mapv #(entid db [:eacl/id (str "edge/" %)]) (range 12)) + subject (nth ids 0) other (nth ids 1) resources (vec (sort (subvec ids 2))) + relation (entid db [:eacl.relation/resource-type+relation-name+subject-type [:doc :viewer :user]])] + (doseq [[i resource] (map-indexed vector resources)] + (staged/write! w :create [:user subject relation :doc resource] + (when (even? i) {:valid-until-ms (+ 100 i)}))) + (staged/write! w :create [:user other relation :doc (first resources)] nil) + ((or with-basis + (fn [f] ((:with-snapshot native) #(f % (adapter %))))) + (fn [database native-adapter] + (let [opts {:direction :asc :include-qualifier? true} + edges (vec (forward database :user subject relation :doc opts)) + selected (first resources) + first-q (edge/qualifier-id (first edges))] + (is (= resources (mapv edge/endpoint edges))) + (is (= 5 (count (filter vector? edges)))) + (doseq [time [99 104]] + (let [reads (atom {}) packets (atom {}) ledger (counters/make-ledger) + base (qualification/request-from-adapter + native-adapter + {:time time :basis {:source ((:source native) database) + :revision ((:revision native) database)}}) + request (assoc base :lookup + (fn [eid] + (swap! reads update eid (fnil inc 0)) + (let [packet ((:lookup base) eid)] + (swap! packets assoc eid packet) + packet)))] + (counters/call-with-ledger + ledger + (fn [] + (is (true? (qualification/qualify request relation selected))) + (is (= {} @reads)) + (is (every? zero? (vals (counters/snapshot ledger)))) + (doseq [[i e] (map-indexed vector edges)] + (let [expected (or (odd? i) (< time (+ 100 i)))] + (is (= expected (evidence/has? (qualification/qualify request relation e)))) + (is (= expected (evidence/has? (qualification/qualify request relation e)))))))) + (is (every? #(= 1 (get @reads %)) (keep edge/qualifier-id edges))) + (is (= 6 (count @reads))) + (is (= 6 (:commands (counters/snapshot ledger)))) + (is (= 6 (:adapter-reads (counters/snapshot ledger)))) + (is (= (reduce + (map :fact-count (vals @packets))) + (:fetched-values (counters/snapshot ledger)))) + (is (every? #(= ((:qualifier-version native) database %) + (:version (get @packets %))) + (keep edge/qualifier-id edges))))) + (is (every? edge/valid? edges)) + (is (= (vec (rseq edges)) (vec (forward database :user subject relation :doc (assoc opts :direction :desc))))) + (doseq [direction [:asc :desc] inclusive? [true false] bound resources] + (let [expected (filterv (fn [eid] + ((if (= direction :asc) + (if inclusive? >= >) (if inclusive? <= <)) eid bound)) + (if (= direction :asc) resources (vec (rseq resources)))) + actual (forward database :user subject relation :doc + (assoc opts :direction direction :bound-eid bound :inclusive-bound? inclusive?))] + (is (= expected (mapv edge/endpoint actual))))) + (doseq [[resource e] (map vector resources edges)] + (is (= e (direct database :user subject relation :doc resource)))) + (let [back (vec (reverse database :doc selected relation :user opts))] + (is (= (vec (sort [subject other])) (mapv edge/endpoint back))) + (is (= first-q (edge/qualifier-id (first (filter #(= subject (edge/endpoint %)) back)))))) + (is (nil? (direct database :user subject relation :doc other))) + (is (= :eacl/unsupported-qualifier + (error-type #(vec (forward database :user subject relation :doc {:direction :asc}))))) + (is (= first-q (nth (:v (first ((:rows native) database subject storage/forward-attribute + [:user relation :doc selected nil]))) 4))))))))) diff --git a/modules/eacl/test/eacl/relationships/edge_test.cljc b/modules/eacl/test/eacl/relationships/edge_test.cljc new file mode 100644 index 00000000..c32661c5 --- /dev/null +++ b/modules/eacl/test/eacl/relationships/edge_test.cljc @@ -0,0 +1,20 @@ +(ns eacl.relationships.edge-test + (:require [eacl.relationships.edge :as edge] + #?(:clj [clojure.test :refer [deftest is]] + :cljs [cljs.test :refer-macros [deftest is]]))) + +(deftest sparse-values-preserve-the-ordered-identity-stream + (doseq [density [0 5 10 100]] + (let [ids (vec (range 100)) + qids (mapv #(when (< % density) (+ 1000 %)) ids) + rows (mapv (fn [eid qid] {:v [:user 1 :document eid qid]}) ids qids) + values (mapv edge/from-datom rows)] + (is (= ids (mapv edge/endpoint values))) + (is (= qids (mapv edge/qualifier-id values))) + (is (= density (count (filter vector? values)))) + (is (every? edge/valid? values)) + (is (= (reverse ids) (map edge/endpoint (reverse values))))))) + +(deftest malformed-compact-values-cannot-alias-an-ordinary-edge + (doseq [value [nil false -1 1.5 [] [1] [1 nil] [1 0] [1 -2] [1 "qid"] [1 2 3] {:eid 1}]] + (is (not (edge/valid? value))))) diff --git a/modules/eacl/test/eacl/relationships/endpoint_pair_test.cljc b/modules/eacl/test/eacl/relationships/endpoint_pair_test.cljc index f21eb8f1..9a3b16e4 100644 --- a/modules/eacl/test/eacl/relationships/endpoint_pair_test.cljc +++ b/modules/eacl/test/eacl/relationships/endpoint_pair_test.cljc @@ -25,7 +25,7 @@ (is (= 9 storage/version)) (is (= 5 pair/value-arity storage/value-arity)) (is (= 4 storage/identity-arity)) - (is (= :nil-only storage/qualifier-capability)) + (is (= :qualified storage/qualifier-capability)) (is (= [:db.type/keyword :db.type/ref :db.type/keyword :db.type/ref :db.type/ref] storage/tuple-types)) (is (= :eacl.v9.relationship/subject-type+relation+resource-type+resource+qualifier diff --git a/modules/eacl/test/eacl/relationships/mutations_test.cljc b/modules/eacl/test/eacl/relationships/mutations_test.cljc new file mode 100644 index 00000000..37484552 --- /dev/null +++ b/modules/eacl/test/eacl/relationships/mutations_test.cljc @@ -0,0 +1,71 @@ +(ns eacl.relationships.mutations-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.authorization.context-test :as errors] + [eacl.caveats.values :as values] + [eacl.relationships.mutations :as mutations] + [eacl.relationships.staged :as staged])) + +(def relationship {:subject {:type :user :id "u"} + :relation :member :resource {:type :folder :id "f"}}) + +(deftest application-composition-protects-nested-native-entity-maps + (let [safe [{:db/id 1 :app/value {"nested" [1 2]}} + [:db/add 1 :app/reference {:db/id 2 :app/flag true}]]] + (is (= safe (staged/application-datoms safe #{99})))) + (doseq [tx [[{:db/id 1 :app/reference {:db/id 99 :app/flag true}}] + [[:db/add 1 :app/reference {:db/id 99 :app/flag true}]] + [{:db/id 1 :app/references [{:eacl.relationship-qualifier/valid-until-ms 100}]}] + [[:db/add 1 :app/reference {:db/id "hidden" :eacl.relation/relation-name :viewer}]]]] + (is (= :application-datoms + (:reason (errors/error-data #(staged/application-datoms tx #{99}))))))) + +(deftest ordinary-write-normalization-retains-the-original-relationship + (is (identical? relationship (mutations/normalize-relationship relationship))) + (is (= relationship (mutations/normalize-relationship + (assoc relationship :caveat nil :valid-until-ms nil)))) + (is (= (assoc relationship :caveat "enabled") + (mutations/normalize-relationship (assoc relationship :caveat "enabled" :caveat-context {}))))) + +(deftest qualified-write-input-is-one-named-caveat-with-bounded-context-and-expiry + (let [qualified (assoc relationship :caveat "enabled" :caveat-context {"flag" true} + :valid-until-ms 100)] + (is (= qualified (mutations/normalize-relationship qualified))) + (doseq [fields [{:caveat ["a" "b"]} {:caveat :enabled} {:caveat ""} + {:caveat {"enabled" {}}} {:caveat-context {"flag" true}} + {:caveat "enabled" :caveat-context false} + {:caveat "enabled" :caveat-context {"flag" 1.5}} + {:caveat "enabled" :caveat-context {(apply str (repeat 65 "a")) true}} + {:caveat "enabled" :caveat-context {"flag" (apply str (repeat 4097 "a"))}} + {:valid-until-ms (dec (:timestamp-min-ms values/limits))} + {:valid-until-ms 1.5} {:valid-until-ms "100"} + {:valid-until-ms 9007199254740992} {:qualifier-eid 123}]] + (is (some? (errors/error-data #(mutations/normalize-relationship (merge relationship fields)))))))) + +(deftest batch-identity-excludes-qualifiers-but-update-intent-does-not + (let [a (assoc relationship :caveat "enabled" :caveat-context {"flag" true} :valid-until-ms 100) + b (assoc a :valid-until-ms 200) + update-a {:operation :touch :relationship a} + update-b {:operation :touch :relationship b}] + (is (= [update-a] (mutations/normalize-updates [update-a update-a]))) + (doseq [updates [[update-a update-b] + [update-a (assoc update-a :operation :create)] + [(assoc update-a :operation :create) (assoc update-b :operation :create)]]] + (is (= :eacl/invalid-relationship-update-batch + (:type (errors/error-data #(mutations/normalize-updates updates)))))) + (is (= 1 (count (mutations/normalize-updates + [{:operation :delete :relationship a} {:operation :delete :relationship b}])))) + (is (= [{:operation :touch :relationship relationship}] + (mutations/normalize-updates + [{:operation :touch :relationship relationship} + {:operation :touch :relationship (assoc relationship :caveat nil :valid-until-ms nil)}]))))) + +(deftest cached-qualifier-metadata-must-be-closed-bounded-and-canonical + (doseq [metadata [{} {:caveat "enabled"} {:valid-until-ms 100} + {:caveat "enabled" :caveat-context {"flag" false} :valid-until-ms 100}]] + (is (mutations/canonical-qualifier-metadata? (merge relationship metadata)))) + (doseq [metadata [{:caveat nil} {:caveat ""} {:valid-until-ms nil} + {:valid-until-ms 253402300800000} {:valid-until-ms "100"} + {:caveat-context {"flag" true}} + {:caveat "enabled" :caveat-context {}} + {:caveat "enabled" :caveat-context nil}]] + (is (false? (mutations/canonical-qualifier-metadata? (merge relationship metadata)))))) diff --git a/modules/eacl/test/eacl/relationships/storage_contract.cljc b/modules/eacl/test/eacl/relationships/storage_contract.cljc index 1f23ac50..a5c92edb 100644 --- a/modules/eacl/test/eacl/relationships/storage_contract.cljc +++ b/modules/eacl/test/eacl/relationships/storage_contract.cljc @@ -1,6 +1,7 @@ (ns eacl.relationships.storage-contract (:require [#?(:clj clojure.test :cljs cljs.test) :refer [is testing]] [eacl.core :as eacl] + [eacl.client.orchestration :as orchestration] [eacl.relationships.endpoint-pair :as pair] [eacl.relationships.storage :as storage] [eacl.relationships.upgrade-test :refer [error-data]])) @@ -12,7 +13,7 @@ permission view = viewer }") -(defn exercise-qualified-corruption! +(defn- exercise-v8-qualified-corruption! "One native graph exercises unsupported data, duplicate identity, exact identity-only deletion, and qualifier-preserving object cleanup." [{:keys [client snapshot transact! entid rows safe-retract! stamp direct-probe plan-create read-identity]}] @@ -32,7 +33,7 @@ q (entid db [:eacl/id "qualifier"]) q2 (entid db [:eacl/id "qualifier-2"]) native-relationship (eacl/->Relationship (eacl/spice-object :user a) :viewer - (eacl/spice-object :document doc)) + (eacl/spice-object :document doc)) forward (:v (first (rows db storage/forward-attribute))) relation (nth forward 1) reverse (pair/reverse-value :document relation :user a) @@ -92,3 +93,10 @@ (safe-retract! a) (is (empty? (rows (snapshot) storage/forward-attribute))) (is (empty? (rows (snapshot) storage/reverse-attribute)))))) + +(defn exercise-qualified-corruption! + "The v8 storage-9 compatibility contract rejects non-nil qualifier refs. + Qualified serving is covered separately by the v9 native contracts." + [system] + (binding [orchestration/*qualified-authorization-enabled?* false] + (exercise-v8-qualified-corruption! system))) diff --git a/modules/eacl/test/eacl/schema/relation_allowance_test.cljc b/modules/eacl/test/eacl/schema/relation_allowance_test.cljc new file mode 100644 index 00000000..6fa6c39a --- /dev/null +++ b/modules/eacl/test/eacl/schema/relation_allowance_test.cljc @@ -0,0 +1,37 @@ +(ns eacl.schema.relation-allowance-test + (:require [#?(:clj clojure.test :cljs cljs.test) :refer [deftest is]] + [eacl.caveats.definition-test :as errors] + [eacl.schema.model :as model] + [eacl.schema.relation-allowance :as allowance])) + +(def plain (model/Relation :doc :viewer :user)) +(def optional (assoc plain :eacl.relation/caveats [[:eacl.caveat/name "enabled"]] + :eacl.relation/allows-unqualified? true)) +(def required (assoc optional :eacl.relation/allows-unqualified? false)) +(defn delta [before after] {:retractions #{before} :additions #{after}}) + +(deftest native-relation-alternatives-have-one-canonical-schema-shape + (is (= plain (allowance/canonicalize plain))) + (is (= optional (allowance/canonicalize (assoc optional :eacl.relation/caveats [{:eacl.caveat/name "enabled"}])))) + (is (= #{nil "enabled"} (allowance/names optional))) + (is (= #{"enabled"} (allowance/names required))) + (doseq [bad [(dissoc optional :eacl.relation/caveats) + (dissoc optional :eacl.relation/allows-unqualified?) + (assoc optional :eacl.relation/caveats []) + (assoc optional :eacl.relation/caveats [[:eacl.caveat/name "enabled"] [:eacl.caveat/name "enabled"]])]] + (is (= :eacl.schema/invalid-relation-allowance (errors/error-type #(allowance/names bad)))))) + +(deftest allowance-updates-retain-logical-relation-identity + (is (empty? (allowance/entity-deletions (delta plain optional)))) + (is (= [plain] (vec (allowance/entity-deletions {:retractions #{plain} :additions #{}})))) + (is (= [] (allowance/attribute-retractions (delta optional required)))) + (is (= #{[:db/retract [:eacl/id (:eacl/id plain)] :eacl.relation/caveats [:eacl.caveat/name "enabled"]] + [:db/retract [:eacl/id (:eacl/id plain)] :eacl.relation/allows-unqualified? true]} + (set (allowance/attribute-retractions (delta optional plain)))))) + +(deftest tightening-relation-alternatives-validates-retained-relationships + (is (true? (allowance/validate-existing! (delta plain optional) (constantly [nil])))) + (is (true? (allowance/validate-existing! (delta optional required) (constantly ["enabled"])))) + (doseq [[before after references] [[plain required [nil]] [optional plain ["enabled"]] [optional required [nil "enabled"]]]] + (is (= :eacl.schema/relationship-qualifier-in-use + (errors/error-type #(allowance/validate-existing! (delta before after) (constantly references))))))) diff --git a/modules/eacl/test/eacl/secure_format_test.cljc b/modules/eacl/test/eacl/secure_format_test.cljc index f3f031dd..8f3792ba 100644 --- a/modules/eacl/test/eacl/secure_format_test.cljc +++ b/modules/eacl/test/eacl/secure_format_test.cljc @@ -1045,3 +1045,21 @@ (is (= (sign (reference left right)) (sign (comparator left right))) (str "ordering differs for " left " and " right))) (is (= (sort reference corpus) (sort comparator corpus))))) + +(deftest utf8-size-matches-every-bmp-scalar-and-surrogate-boundaries + (doseq [code (range 65536) + :when (or (< code 0xD800) (> code 0xDFFF))] + (let [s #?(:clj (str (char code)) :cljs (.fromCharCode js/String code))] + (is (= (count (secure/utf8-bytes s)) (secure/utf8-size s)) (str code)))) + (doseq [high [0xD800 0xD801 0xDBFE 0xDBFF] + low [0xDC00 0xDC01 0xDFFE 0xDFFF]] + (let [s #?(:clj (str (char high) (char low)) :cljs (.fromCharCode js/String high low))] + (is (= 4 (secure/utf8-size s))) + (is (= (count (secure/utf8-bytes (str "aé" s "中"))) + (secure/utf8-size (str "aé" s "中")))))) + (doseq [units [[0xD800] [0xDBFF] [0xDC00] [0xDFFF] [0xD800 0x61] + [0xD800 0xD800] [0xDC00 0xD800] [0x61 0xDFFF]]] + (let [s #?(:clj (apply str (map char units)) :cljs (.apply (.-fromCharCode js/String) nil (to-array units)))] + (is (= :invalid-unicode + (:reason (try (secure/utf8-size s) nil + (catch #?(:clj clojure.lang.ExceptionInfo :cljs :default) error (ex-data error))))))))) diff --git a/modules/eacl/test/eacl/subproblem_cache_test.cljc b/modules/eacl/test/eacl/subproblem_cache_test.cljc index b9d1d074..2c3e2ed6 100644 --- a/modules/eacl/test/eacl/subproblem_cache_test.cljc +++ b/modules/eacl/test/eacl/subproblem_cache_test.cljc @@ -618,3 +618,21 @@ (ex-data error)))] (is (= :eacl/invalid-config (:type operation-error))) (is (= [:weight-fn] (:unknown-keys operation-error))))) + +(deftest conditional-publication-does-not-overwrite-a-concurrent-replacement + (let [store (subproblem/store small-options) + key (storage-key :answer :temporal-race) + tier (get-in store [:tiers :answer]) + old {:time 1} concurrent {:time 3} candidate {:time 2}] + (subproblem/publish! store :answer key accept-any-publication old) + (let [result (subproblem/publish! + store :answer key + {:valid? map? + :replace? (fn [prior next] + (is (identical? old prior)) + (is (identical? candidate next)) + (is (lru/replace-if! tier key old concurrent)) + true)} + candidate)] + (is (false? (:published? result))) + (is (= concurrent (:value (subproblem/lookup! store :answer key))))))) diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/.openspec.yaml b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/.openspec.yaml new file mode 100644 index 00000000..1d9aeef9 --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-04 diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/design.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/design.md new file mode 100644 index 00000000..28085f37 --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/design.md @@ -0,0 +1,406 @@ +## Context + +The selected database now has one v9 endpoint stream. An edge carries an opposite endpoint eid and a trailing qualifier eid. Phase 2 supplies immutable qualifier entities and a certified Caveat subsystem, but public writers/readers and traversal still reject non-`nil` qualifiers. + +The existing engine is demand-driven, proof-backed, and highly sensitive to unnecessary allocation and I/O. This design therefore adds one qualification boundary rather than duplicating permission operators or wrapping the full engine in a temporal database filter. + +## Goals / Non-Goals + +**Goals:** zero qualifier reads for ordinary edges, one bounded resolution per distinct qualified edge, certified atomic qualifier publication, SpiceDB-like conditional Caveat results, exclusive expiry, trusted time capture, sound managed/exact caching, stable cursors, formal-first implementation, and measured hot-path simplicity. + +**Non-Goals:** `valid-from`, recurring schedules, multiple independent grants, multiple Caveats per edge, physical expiry GC, globally shared qualifier entities, transaction-metadata validity, a second Relationship stream, or runtime execution of the formal model. + +## Decisions + +### 1. Prove traversal and cache semantics before changing production routing + +Before any production engine/operator/cache source is edited, extend the abstract models with: + +- edge values `(opposite-eid, optional qualifier)`; +- immutable qualifier resolution and authoritative faults; +- Caveat true/false/conditional outcomes and residual/missing fields; +- result composition for union, intersection, exclusion, arrows, and positive recursion; +- exclusive expiry at one captured evaluation time; +- witness/certificate intervals that bound when a result may change from time alone; +- cache reuse requiring both database proof and temporal/context compatibility; +- cursor continuation with explicit pinned/live temporal mode, authenticated context, and certified reuse intervals. + +Proofs and mutation controls must be green within locked resource budgets before implementation. The production bridge and killed-mutation certification are completed after implementation and before routing activation. Models stay in verification/test tooling and do not execute in request paths. + +### 2. Add one compact edge-qualification seam + +Backend scans must make slot five available to shared evaluation. The backend-neutral scan contract evolves from “opposite eids only” to a compact aligned representation of opposite eid plus optional qualifier eid. The implementation may use pairs, parallel primitive-friendly vectors, or an equivalent measured shape; it must not require one persistent map allocation per ordinary edge. + +One shared function conceptually performs: + +```clojure +(qualify-edge request edge) +``` + +- `qualifier-eid == nil` returns definite active membership immediately; +- non-`nil` resolves/validates the qualifier; +- expiry is checked before Caveat program work; +- Caveat evaluation returns definite, absent, or conditional evidence; +- faults remain distinguishable from ordinary false membership. + +Permission operators consume annotated evidence, not a second traversal engine. Existing order, demand bounds, cancellation, deadlines, and cursor position continue to be defined by the one endpoint stream. + +### 3. Capture trusted evaluation inputs once + +Each top-level client operation captures one request context containing: + +```clojure +{:basis selected-immutable-snapshot + :evaluation-time-ms trusted-clock-sample + :caveat-context canonical-request-context + :evaluator-fingerprint ...} +``` + +A public request context may contain inputs for several named Caveats. EACL canonicalizes and bounds the complete map once, then supplies each demanded Caveat only its declared parameter names. Type validation applies to that projection and to the strict Relationship-bound map, with bound-value precedence. The Phase 2 direct evaluator remains strict about unknown keys in its per-Caveat inputs. Unused public fields remain part of the complete authenticated cache/cursor identity; projection does not permit context aliases. + +The public `:caveat-context` map uses valid parameter names and the profile's portable scalar, homogeneous scalar-vector, or string-keyed scalar-map values. It retains the 16 KiB and 1,024 total-entry ceilings across the complete map, plus the existing string and container bounds. Its top-level field count is independent of one Caveat's 32-parameter declaration limit. Admission precedes snapshot acquisition and cache lookup, including empty batches; per-item batch context overrides are invalid. An unchanged prepared context is shared through transient snapshot delegation, while a changed context is validated anew. + +An explicit EACL snapshot freezes the database basis and evaluation time; Caveat request context remains an explicit operation input unless the public snapshot API intentionally binds it. A batch shares one captured time. The filter/evaluator never calls the wall clock per datom or per recursive step. + +The clock is an injected client dependency for tests and operations. EACL samples it only at the top-level request boundary and applies a process-local non-decreasing high-water mark: a raw backward step cannot produce an accepted evaluation time below the last accepted value. Production documentation still requires synchronized clocks across Peers and explains uncertainty/restart limits. A backward clock step can delay time progression or fail a request under a configured strict policy, but it never silently revives an already expired grant inside one client lifetime. + +### 4. Resolve and cache immutable qualifiers safely + +Every request owns a bounded map keyed by qualifier eid. A distinct qid is fetched/decoded at most once for the complete operation, including recursive revisits. The nil path does not touch this map. + +A longer-lived qualifier decode cache may reuse immutable content under a source-scoped key such as: + +```clojure +[source-lifecycle qualifier-eid qualifier-version format-version] +``` + +The adapter normalizes `qualifier-version` from the mandatory format marker's assertion `t` or the Phase 2 certified persisted equivalent. Supported writers never mutate or reuse a qualifier eid; `:touch` creates a new qid and changes both endpoint tuples, which advances the owning Relation version. An implementation may omit a repeated qualifier-entity read on a managed cache hit only when that supported-writer/non-reuse contract and the owning Relation proof are present; otherwise it validates exact content. + +Safety tiers: + +- exact immutable snapshots may cache by exact source/basis and qid; +- managed supported-writer sources may reuse by lifecycle/qid/qualifier-version because entity ids are not reused inside the lifecycle and immutability is enforced; +- unknown-writer sources require an exact basis or a database-visible content proof covering every qualifier field and definition dependency; +- a qid-only process cache is forbidden. + +Qualifier cache entries contain decoded data only. Every request still compares its captured time with `valid-until` and evaluates Caveat bindings; cached data is not cached authorization. + +### 5. Enforce qualifier validity before semantics + +A non-`nil` ref must resolve to one supported, internally consistent qualifier whose Caveat is allowed by the selected Relation branch. Missing entity, wrong format, context without Caveat, detected ownership/immutability violation, invalid generation, unknown Caveat, malformed context, or out-of-range expiry produces a typed authoritative fault. + +Single ownership and immutability are admission/proof invariants, not reasons to perform a reverse graph scan on every edge. Supported writers allocate a fresh qid and update both tuple halves; offline integrity and certification detect sharing; unknown-writer cache paths use exact/content proof. The ordinary resolver reads only the referenced qualifier and directly required Caveat/schema data. + +For a positive grant edge, fault cannot grant. For a subtracting/deny edge, silently treating fault as absent could grant, so the full authorization operation returns an error/indeterminate failure rather than erasing the negative evidence. `can?` fails closed to false only at its compatibility boundary while diagnostic APIs preserve the error category; server integrations should not conflate faults with ordinary denial. + +### 6. Compose Caveat permissionship through the existing algebra + +The internal value is an evidence object capable of representing: + +```text +true +false +conditional(residual, missing-fields) +fault(reason) +``` + +Composition follows the formal model: + +- union is true if any child is true; otherwise conditional if any residual can become true; otherwise false; +- intersection is false if any child is definitely false; true only if all true; otherwise conditional; +- exclusion `L - R` is true only when L is true and R false; false when L false or R true; otherwise conditional according to residual Boolean `L && !R`; +- arrows qualify each edge and downstream result without changing endpoint order; +- recursion uses bounded canonical residual/evidence merging and terminates under existing recursion limits; +- any authoritative fault propagates rather than becoming Boolean absence. + +Public lookups default to definite-only SpiceObject results. `:result-policy :detailed` returns items such as `{:object , :allowed? false, :permissionship :conditional-permission, :missing-fields ["flag"], :residual }`. The object retains its closed reusable endpoint shape. The complete root result controls filtering; conditional interior paths remain eligible to combine into a definite root grant. Qualified direct Relationship filters intersect their own evidence with the permission result. Filtered inclusive lookahead recovers complete root evidence at the selected basis when the cursor retains only a structural sentinel. Detailed results survive rendering and range reuse, and both internal and rendered cache ingress reject inconsistent permissionship, missing fields, residuals, or result policy. + +Public counts default to `:result-policy :definite`. Explicit `:detailed` counts return `:count`, `:definite-count`, and `:conditional-count`; the categories sum to the reported count and exclude the capped lookahead. Missing anchors retain both zero categories. Invalid policies are rejected before capturing time or acquiring a basis, and exact count cache ingress validates the closed category shape against the request policy. + +Recursive operator candidate enumeration projects compact qualified scans onto structural endpoint identities after the existing scan-response cache. This positive cover deliberately retains expired and conditional candidates; the bounded aligned recursive evaluator owns the complete authorization decision. It receives the request qualification context, preserves faults, and applies the public result policy only after the complete root decision. Ordinary scan chunks keep their existing vector. Qualified rows remain in the scan cache, so structural projection cannot replace evidence-bearing cached data with endpoints alone. + +Relationship-bound context overlays request context before evaluation. Final detailed checks expose SpiceDB-style permissionship and missing context. `can?` returns true only for definite true. + +For recursive first-discovery enumeration, the existing admission identities and stack remain authoritative. Ordinary admitted work continues to use the existing set. A sparse evidence table records only admissions whose incoming value is not timeless true. A changed union of incoming evidence schedules further propagation; updates to already queued work coalesce at its existing stack position. Weighted stack slots retain only admission identities; the pending table owns the latest work item, so an obsolete stack entry cannot retain unaccounted old evidence. A scan whose incoming evidence changes must revisit its prefix with a new buffer revision, so a physical buffer from an earlier prefix cannot skip newly possible derivations. Admission, stack, transition, and evidence limits apply before committing these updates. The combined retained admission/result evidence pool defaults to 16,777,216 conservative storage units, including atom strings; window compaction removes result annotations with their backing vector, and count sinks retain no emitted evidence history. + +An emitted conditional path is a lower bound on the complete root result. The existing point evaluator completes that result before public projection, sharing the request's qualifier memo; a definite witness needs no completion probe. Each endpoint is emitted or filtered once. Checkpoints retain the evidence needed for queued work and the complete qualified request scope, and discard physical buffers as before. This extends the current discovery machine while preserving its ordinary path and physical chunk independence. + +First-discovery pages retain only sparse delivered evidence and explicit evidence for the single pending lookahead, including timeless true. Incomplete or faulty retained lookahead and mismatched checkpoint scope cause a replay miss. Qualified checkpoint identities include exact request scope and result policy. The standalone page token binds a compact digest of that scope; public Relay live-time intervals remain the separate cursor contract below. + +### 7. Implement exclusive `valid-until` as native qualifier semantics + +A qualifier with no bound is time-unbounded. A finite bound is active exactly when: + +```text +evaluation-time-ms < valid-until-ms +``` + +At equality the edge is expired. Passing time does not mutate the database or Relation version. Expiry is evaluated before Caveat work; an already expired Caveated edge does not compile/evaluate its Caveat for that request. + +Expiry applies to every edge role. An expiring grant may remove permission; an expiring ban/subtracting edge may add permission; an expiring intermediate arrow may change reachability. Therefore cache and cursor logic cannot assume time only narrows access. + +### 8. Carry a temporal stability certificate with reusable evidence + +Each evaluated subproblem/result has a half-open time interval within which its value remains equal if schema/Relation/qualifier proofs and request context remain equal. With no `valid-from`, the lower bound is normally the captured time and the relevant upper bound is a future expiry or infinity. + +The model proves witness-aware operator rules so demand-driven evaluation need not scan every possible edge solely for a global minimum expiry: + +- true union may use one true witness certificate; +- false union combines complete false child certificates; +- true intersection combines all true child certificates; +- false intersection may use one decisive false child certificate; +- exclusion combines the decisive evidence for its actual outcome; +- recursive certificates follow the certified fixed-point evidence graph. + +The conservative composition is interval intersection/minimum upper bound over the evidence required by that outcome. Missing completeness means no managed temporal publication, not an extra full traversal. + +A reusable cache entry is accepted only when: + +1. source, schema, Relation, identity, evaluator, and qualifier proofs match; +2. canonical request Caveat context scope matches; +3. requested evaluation time lies inside the certified interval; and +4. the entry kind preserves definite/conditional/fault distinctions. + +Timers and eager eviction may improve memory/latency but are never authorization correctness inputs. + +### 9. Keep Caveat/result cache identities complete but lean + +Final and subproblem keys include a bounded digest plus collision-checked canonical identity of request Caveat context and evaluator/profile. Relationship-bound context is covered by qualifier identity/content proof; Caveat source/parameters/profile are covered by schema proof. + +The program cache remains separate and is keyed only by definition/profile identity. A program hit can be reused across request contexts but cannot authorize on its own. Conditional residuals and missing-field sets are part of the cached value and cannot alias definite results. + +### 10. Give cursors explicit pinned or live temporal semantics + +A qualified cursor authenticates: + +- pinned-versus-live temporal mode; +- selected source/basis or recoverable dependency proof; +- storage/ordering/evaluator ABI; +- original evaluation time and the cursor state’s certified temporal interval; +- canonical request Caveat context identity; +- detailed/Boolean result policy; +- ordinary traversal boundary and continuation proof. + +An explicit EACL snapshot produces a **pinned** cursor. Every page retains that snapshot’s database basis and evaluation time; later wall time does not reinterpret it. Exact-basis availability, ordinary cursor TTL, key availability, and dependency checks still apply. + +A client-targeted lookup produces a **live** cursor. On every resume EACL captures one fresh trusted time and may reuse the retained frontier only when that time is at or after the certificate start and strictly before every finite deadline, with equal ordinary dependencies and Caveat context. Reaching or leaving the certified interval returns a typed restart requirement and no resumed page. The caller starts a new lookup to obtain the current view; EACL never silently applies the old boundary to a new temporal graph. + +The cursor certificate covers every examined/emitted/skipped candidate before the public boundary plus retained frontier, lookahead, conditional residual, and subtracting evidence. This is sufficient to prevent an expired ban from making an earlier identity newly visible behind the boundary. Unexamined candidates after the boundary may be evaluated at the fresh live time. If the evaluator cannot produce a complete certificate for the retained state, it does not permit cross-time continuation; it does not perform an extra unbounded traversal solely to manufacture one. + +Request Caveat context is frozen/authenticated for both modes. Changing it starts a new lookup. Cursor token TTL and security-key retirement remain separate operational constraints. + +### 11. Preserve logical Relationship mutation semantics + +Public write input may now include one optional Caveat/bound context and one optional `valid-until`. Normalization creates `nil` or one fresh qualifier entity. `:create` conflicts on first-four identity regardless of qualifiers. `:delete` uses identity only and removes the current owned qualifier. + +Qualified writes are enabled only when the adapter advertises one Phase 2 publication strategy. An inline-capable backend may create and attach the qualifier in one transaction. A prepared-reference backend may first create an unreferenced inert qualifier, then atomically publish or swap both endpoint values, the Relation stamp, the old qualifier retraction, and any caller-composed application datoms using its concrete eid. The publication transaction is the semantic commit point; a failed prepare/attach can leave only an orphan qualifier, never a visible half-edge. `eacl/tx-relationship` requires a prepared handle on such a backend rather than hiding an extra transaction inside returned tx-data. + +No two Relationships differing only by Caveat, context, or expiry may coexist, matching SpiceDB's Relationship identity behavior. If independent grants are required later, that is a grant-assertion entity feature, not tuple duplication. + +The public Relationship map uses `:caveat` (one declared string name), `:caveat-context` (bound portable context), and `:valid-until-ms` (exclusive UTC milliseconds). Identical batch intents coalesce before native allocation; different qualifiers on the same identity conflict. `write-relationships!` also accepts `{:updates [...] :tx-data [...]}` to commit application datoms with the final endpoint publication. Application datoms cannot alter EACL state or allocated qualifier entities. Both endpoint identity guards, one schema fence, and one generation fence per affected Relation protect the final batch. + +For caller-managed transaction composition, `prepare-relationship!` creates an inert qualifier and returns an opaque handle. The caller obtains a snapshot after preparation and supplies the handle as `:prepared-qualifier` on the update passed to `tx-relationship`. The pure snapshot planner validates source, Relationship identity, schema generation, exact immutable facts, and equality with the requested qualifier value. Snapshots retain pure planning functions and never a connection or writer. `discard-prepared-relationship!` removes an unchanged, unattached handle through its original writer; attached or altered entities cannot be removed through this API. An ordinary Relationship needs no preparation and returns `nil`. + +`tx-relationships` plans multiple updates together against one snapshot, coalesces identical intents, and emits one fence per schema/Relation alongside all endpoint guards and optional application datoms. Callers use this batch planner when composing multiple mutations. Inline backends accept semantic qualifier values directly; prepared backends use the individual preparation handles. Datalevin uses its native unique temporary-ID allocator so independent pure plans cannot accidentally share an entity. + +### 12. Keep physical collection out of correctness and out of this phase + +Expired Relationships remain stored and visible to administrative stored-state reads, create conflicts, deletion, history, and integrity inspection. Authorization treats them inactive at the captured time. + +No derived expiry index, scheduler, background job, or GC API is required here. A later maintenance change can add a bounded collector with exact current-pair revalidation and Relation stamping after real retained-data measurements justify it. + +### 13. Gate activation on refinement and performance evidence + +After implementation but before the route/capability epoch is enabled: + +- production qualifier resolution, Caveat algebra, expiry boundary, temporal certificate, cache acceptance, and cursor scope must match generated/refinement oracles; +- mutation controls must kill omitted qualifier reads, wrong boundary (`<=`), context precedence inversion, conditional-as-true, fault-as-absence, missing Relation stamp, unsafe q-cache key, and expired-ban cache reuse; +- public cached results must equal uncached evaluation on the selected temporal/context view; +- 0%, 5%, and 10% qualifier workloads must satisfy recorded read/allocation/latency budgets, including concentrated qualified prefixes and negative/exclusion paths. + +The nil fast path is inspected and measured directly. No runtime model evaluation, shadow engine, duplicate full permission check, or per-edge proof recomputation may be introduced to satisfy certification. + +## Risks / Trade-offs + +- **Qualifier indirection is slower when most edges are qualified** → optimize for stated 5–10% sparsity and measure concentrated endpoints; revisit inline data only with evidence. +- **Partial Caveat algebra is complex** → keep one canonical evidence type and formally prove operator composition before implementation. +- **Temporal cache certificates can reduce reuse** → witness-aware rules retain safe demand-driven reuse; exact evaluation remains fallback. +- **Clock skew affects boundary behavior across peers** → capture once, use a process-local non-decreasing high-water mark, require synchronized clocks, document uncertainty/restart limits, and keep explicit snapshot semantics. +- **An explicit pinned snapshot can preserve a past grant after wall-clock expiry** → make client-targeted live mode the ordinary access-control default and document pinned temporal snapshots as intentional historical/simulation semantics, not a live-session lease. +- **Pinned cursors are historical views and live cursors can cross a temporal boundary** → make modes explicit and require a typed restart/new query whenever live certification ends. +- **Fault propagation can surface more errors than old Boolean checks** → preserve Boolean fail-closed compatibility while exposing structured diagnostics and preventing negative-evidence erasure. +- **CLJS Caveats require an external evaluator** → capability-check before activation; expiry-only qualifiers remain portable. +- **Some backends cannot resolve newly allocated refs inside tuple values** → activate only a certified inline or prepared-reference writer; keep capability selection out of per-edge reads and treat prepared orphans as non-authoritative storage. + +## Migration Plan + +1. Complete Phases 1 and 2 and verify no serving client can yet accept non-`nil` qualifiers. +2. Complete the extended formal models and mutation controls; do not edit production engine/cache/cursor code before the formal gate is green. +3. Implement edge scan qualification, request context, qualifier resolver/cache, Caveat algebra, expiry, certified inline/prepared writes, reads, temporal cache certificates, and pinned/live cursor scope behind a disabled semantic capability epoch. +4. Complete production refinement, differential uncached/cached tests, SpiceDB Caveat fixtures, cross-backend conformance, and performance gates. +5. Upgrade all serving peers to code capable of the new epoch, verify every enabled backend advertises a certified qualifier-publication strategy and evaluator where required, then activate qualified writes/authorization in a coordinated schema/capability change. +6. Rollback disables qualified writes and returns to the pre-activation database/schema. A database containing qualified Relationships cannot be safely served by Phase 1/2 readers. + +### Schema admission and replacement + +The disabled qualified epoch now admits named Caveat alternatives through the +public committed and speculative schema paths. A matching evaluator is required +when any Relation names a Caveat, even when the requested Relation is ordinary +or the store has no Relationships. Unused named definitions and expiry-only +schemas do not require an evaluator. Bundled adapters advertise exactly one +certified inline or prepared-reference publication contract; request context +construction checks it before answer-cache lookup. Datahike remote writers do +not advertise the prepared-reference contract. + +A typed Relation keeps its eid when its alternatives change. Replacement checks +both of its stored endpoint streams, validates exact pair symmetry and qualifier +contents, and rejects alternatives that invalidate retained data, including +expired data. Only removed identities are entity retractions. Optional Caveat +facts are retracted separately, and every changed Relation has a native commit +guard. Datalevin captures validation, qualifier reads, and commit generations +inside one owned native read snapshot and releases it before transaction +submission. Its schema scan consumes bounded AVET batches across the complete +Relation prefix, preserving rows with equal endpoint tails at batch boundaries. + +### Implemented physical inspection and work-bound contract + +`read-relationships` accepts `:relationship-state :stored` (default) or +`:relationship-state :expiry-active`. Qualified responses retain canonical public +qualifier metadata and label their mode and captured `:evaluation-time-ms`. +Native scan rows carry private aligned Relation/qid fields only until the shared +bounded decoder externalizes them. Expiry-active acceptance uses the existing +candidate window and continuation machinery; expired candidates spend that +window even when no row is emitted. Physical inspection never compiles or +evaluates a Caveat and therefore does not require an evaluator unless an +`:authorization` filter also requests a permission decision. + +All four native backends retain forward/reverse/partial/exact scan semantics. +The ordinary Datalevin scan arity retains its numeric native bound. Qualified +cache ingress validates canonical metadata without narrowing the pre-existing +ordinary boxed-value contract. Datomic schema publication resolves newly created +Caveats by their transaction tempids when constructing Relation alternatives. +Shared tests cover expiry equality, expired identity conflicts, renewal, +shortening, and expiry removal through immutable touch. Existing compact scan +cache, weighted reducer, checkpoint/head, dimensional-budget and candidate-window +conformance is complemented by stopping after a native qualifier read when the +absolute execution deadline or cancellation token fires. + +### Implemented decode reuse proof strength + +The optional client decode cache shares one bounded LRU capacity between exact +and complete-content indices. Exact identity retains the full native basis; +content identity retains source lifecycle/branch, Relation eid and complete +content, qid, marker assertion version when available, qualifier format and +complete content, and the named Caveat entity. Raw native writers remain possible +on the bundled backends: atomic publication capability therefore does not imply +a source-wide non-reuse contract. Cross-basis decode reuse currently takes the +stronger complete-content fallback and performs bounded entity reads. It never +skips those reads based only on a qid, assertion marker, or Relation stamp. + +Both indices retain decoded data only. Per-request context and expiry evaluation, +request-local shared fetch memoization, request cache controls, source rotation, +and speculative no-publication rules remain independent. A single capacity +counts both indices, and each points to the same immutable decoded value. +Portable and native traces compare warm and uncached outcomes after lifecycle +reset, deletion, unchanged-marker mutation, Caveat replacement, and Relation +allowance change. No global ownership scan or authorization result is introduced +into this structural tier. + +### Implemented point-answer temporal acceptance + +Point-answer cache keys retain exact source/basis and canonical context/evaluator +scope but remove request time. Values use the versioned temporal-point envelope: +original time, exclusive deadline, completeness, permissionship, and canonical +encoded evidence. Ingress checks the interval and classification against that +evidence. Resident acceptance checks the captured request time independently of +retention; missing completeness permits only the original exact basis/time. +Operator evidence retains its proven witness-aware deadline rather than a blanket +minimum over every observed qualifier. + +A later miss can replace an expired or incomplete interval through a conditional +expected-value replacement. Validation and replacement eligibility run outside +atomic storage operations; a concurrent publisher changes the expected identity +and wins. Older pinned computations do not replace newer intervals. This keeps +expired-but-resident entries harmless and allows an expired ban to grant on a +later request without a write. Qualified managed answers remain disabled, and +page/count/range/checkpoint reuse remains partitioned by exact time. Public +continuations use the certificate rules described below. + +### Implemented public continuation certificates + +Qualified cursor scopes bind canonical whole-context and evaluator/profile +identity, pinned/live mode, result policy and adapter ABI. An authenticated +closed temporal envelope carries original and current interval start times, +exclusive deadline and completeness. It is checked before traversal and exact +basis recovery cannot rebase the captured live time. Ordinary cursor scope and +encoding stay unchanged; qualified tokens without the certificate are rejected. + +A sparse request ledger records active qualifier deadlines during existing +bounded fetch/evaluation work. Expired qualifiers are stable going forward. +Accepted denotation evidence and complete recursive replay decisions import +their witness certificates. Stable first-discovery checkpoints store and validate +a certificate for their whole retained state before importing it. Page/count +answers use a versioned certificate-bearing cache shape, and range slices, +compositions and continuation-only hits retain the combined certificate. The +existing exact-time cache scope remains conservative while live cursor resumption +can cross time within the certified interval. Incoming prefix certificates are +intersected with each resumed page and never extended. No extra graph traversal +or all-qualifier pass constructs these proofs. + +Stored physical inspection has an unbounded temporal interval. Other producers +without a complete proof explicitly emit an incomplete certificate; future-time +resume returns a typed restart rather than silently restarting. Public results +strip internal certificate fields. Tests cover both directions, forward/backward +pages, demand/complete evaluation, expired bans before a boundary, changed +context/evaluator/policy/mode, incomplete proof, range and checkpoint retention. + +### Completed cache coherence scope + +The active cache paths use complete exact-basis qualification identity rather +than promoting incomplete ordinary Relation proofs. This exact fallback is the +implemented managed policy for qualified answers on the current bundled native +adapters: external writers prevent a source-wide immutable-qualifier guarantee. +Publication capability alone never enables cross-basis authorization reuse. +Point intervals permit certified time reuse; other semantic tiers retain exact +time as a conservative guard and carry their evidence/continuation certificates. +Raw aligned scan rows and structural schema plans remain independent of time. + +One portable native conformance trace compares cold, warm, repeated and +read-only-cache outcomes through all four backends (both Datahike modes). Eight +states cover two ban expirations, positive expiry, touch, bound context, Caveat +replacement and source lifecycle rotation. Every state exercises three request +contexts, five permissions, both lookup directions and result policies, and +limited/unlimited counts. Separate DataScript traces cover unknown native +qualifier mutation/deletion faults and restored entity identities after explicit +lifecycle rotation. Datalevin persists a lifecycle replacement and recreates its +client. Controls omitting time from shared denotation identity or context from +answer identity fail these same public traces. + +### Qualified deletion and existing integrity dependencies + +Qualified object deletion now consumes exact native half-retractions and fits +whole pairs plus owned qualifier cleanup into the final transaction limit. +Selected-basis validation, schema and Relation fences, and exact qualifier facts +precede all removals. Conflicting or duplicate identities and missing/malformed +qualifiers fail before submission; an already missing endpoint or peer can be +cleaned from the surviving exact half. Unattached preparations are preserved. + +Datalevin's ordinary deletion operation is a commit-time rescan function, so it +cannot be treated as a half-retraction stream. Its qualified data adapter exposes +selected-snapshot pair retractions and unwraps that same owned selection for the +shared native cleanup planner. It does not select a second basis. Submission +occurs after releasing the read, and the native basis assertion rejects drift. +Transaction sizing includes all guards, expanded peers and qualifier cleanup. +Native adapters that already materialize object retractions continue to do so; +this change does not claim a new bounded-memory scan implementation for them. + +The existing integrity proof captures qualifier facts/version, Caveat definition +content, Relation definition/generation and source/schema identity. Cache traces +exercise the corresponding exact/content fallback against unknown native writers. +The new deletion contract checks integrity after every bounded native commit on +all backends, including both Datahike modes, preserves inert orphans and rejects +stale plans after unstamped identity changes and qualifier replacement. Additional +DataScript coverage removes an endpoint out of band, cleans its surviving peer, +and verifies that referenced expired qualifiers block Caveat removal until their +owned Relationship cleanup completes. Subtracting qualifier faults remain covered +by the cross-cache conformance traces. diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/proposal.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/proposal.md new file mode 100644 index 00000000..8378f10d --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/proposal.md @@ -0,0 +1,39 @@ +## Why + +Phases 1 and 2 provide a single v9 Relationship stream, sparse immutable qualifiers, shared Caveat definitions, and a formally specified evaluator, but qualified Relationships still cannot participate in authorization. EACL now needs one coherent edge-qualification seam that activates Caveats and exclusive `valid-until` semantics without invalidating its proof-backed caches or adding work to the common `qualifier-eid = nil` path. + +Time can change an authorization answer without a database transaction, including changing a denial into a grant when an expiring subtracting Relationship disappears. Qualifier content and request context likewise affect answers beyond Relation tuple membership. This phase therefore models temporal/conditional traversal and cache reuse first, then certifies the implementation against those models before routing is enabled. + +## What Changes + +- Require completion of `2026-09-04-01-adopt-v9-qualifier-reference-storage` and `2026-09-04-02-build-caveat-qualifier-foundation`. +- **FORMAL GATE:** extend EACL's permission/traversal/cache models with sparse qualifier resolution, Caveat outcome algebra, exclusive expiry, decisive temporal witnesses, and cursor context before production engine changes begin. +- Activate non-`nil` qualifier refs through one shared edge-qualification seam only on backends whose Phase 2 qualified-writer publication strategy is certified. `nil` continues directly with zero qualifier entity reads, zero clock-dependent work beyond the request's one captured time, and no Caveat evaluation. +- Resolve each distinct non-`nil` qualifier at most once per request/batch and support bounded immutable qualifier caching keyed by source lifecycle, qualifier eid, and its certified creation `t`/version. Unknown-writer paths remain exact or content-proof backed; a dangling non-`nil` ref never aliases the `nil` fast path. +- Treat a missing, malformed, wrong-version, disallowed, or otherwise invalid qualifier as an authoritative error/fail-closed condition, never as an unconditional Relationship. Single ownership and immutability are enforced by admitted writes and certification/offline integrity; the ordinary hot path does not scan the graph to rediscover those invariants. +- Enable Caveat evaluation using Relationship-bound context overlaid on request context, three-state permissionship (`has`, `no`, `conditional`), and correct composition through union, intersection, exclusion, arrows, and recursion. +- Add public Caveat request context and detailed permissionship results. Boolean `can?` remains backward-compatible and fail-closed: only definite `has-permission` returns true. +- Enable optional `valid-until-ms` with the half-open rule `evaluation-time < valid-until`. Capture trusted evaluation time once per top-level operation or explicit snapshot. +- Annotate reusable authorization results and subproblems with a formally justified temporal stability certificate. Relation/schema proofs must still match, and wall-clock eviction/listeners are never correctness mechanisms. +- Give qualified cursors explicit temporal modes. Client-targeted live cursors capture fresh trusted time on resume and may reuse state only inside a complete certified temporal interval; at a boundary they fail with a typed restart requirement. Cursors from explicit EACL snapshots remain pinned to their selected time. Caveat request context is authenticated and cannot change within either series. +- Keep physical expired-Relationship collection outside this phase. Expiry correctness is read-time semantics; retained expired data may be removed by a later bounded maintenance proposal. +- Benchmark 0%, 5%, and 10% qualified workloads and enforce that ordinary nil-qualified paths remain allocation/read neutral within recorded budgets. Formal compliance must not add runtime model interpreters, shadow traversal, or redundant full-graph checks. + +## Capabilities + +### New Capabilities + +- `qualified-relationship-evaluation`: one-stream qualifier resolution, Caveat permissionship, conditional permission algebra, public request context, fail-closed faults, mutation semantics, and fast nil path. +- `expiring-relationships`: trusted exclusive `valid-until`, positive/negative evidence behavior, renewal, stored-versus-active inspection, and correctness independent of collection. + +### Modified Capabilities + +- `public-authorization`: captured evaluation time, request Caveat context, detailed permissionship, conditional lookup/count behavior, and Boolean compatibility. +- `dependency-validated-authorization-cache`: immutable qualifier cache identity, Caveat/context-complete result keys, and temporal stability certificates. +- `cursor-dependency-validity`: authenticated pinned/live temporal mode, Caveat context, complete reuse deadlines, and restart-safe continuation semantics. + +## Impact + +This is the first phase that changes permission evaluation. It affects backend scan response shape, direct membership, shared traversal and operator algebra, public checks/lookups/counts, Relationship writes/reads, qualifier lifecycle, trusted request context, caches and proof frames, cursor formats, formal models, differential oracles, performance instrumentation, and documentation. + +Caveat definitions remain shared schema data; Relationship-bound context and `valid-until` remain sparse qualifier data. One logical Relationship still exists per subject/Relation/resource identity. No `valid-from` scheduling, multiple grant assertions, supplemental tuple source, Relationship entity, or mandatory expiry job is introduced. diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/cursor-dependency-validity/spec.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/cursor-dependency-validity/spec.md new file mode 100644 index 00000000..662cf543 --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/cursor-dependency-validity/spec.md @@ -0,0 +1,77 @@ +## ADDED Requirements + +### Requirement: Expiry-aware continuations bind temporal mode and deadline +Every qualified continuation SHALL authenticate its temporal mode, original evaluation time, and complete exclusive temporal reuse interval. An explicit EACL snapshot SHALL produce a pinned continuation. A client-targeted lookup SHALL produce a live continuation unless it explicitly evaluates a pinned snapshot. Cursor token expiry and security-key availability SHALL remain separate checks. + +Pinned resumption SHALL preserve the exact database basis and evaluation time. Live resumption SHALL capture one fresh trusted time and MAY reuse retained state only when that time is at or after the certificate start and strictly before every finite deadline, with ordinary dependency validation still succeeding. Leaving the certified interval MUST return a typed restart requirement and no resumed page; it MUST NOT silently reuse the previous position against a new temporal graph. + +#### Scenario: Live resume before expiry +- **WHEN** a live cursor resumes within its certified interval with equal complete dependencies +- **THEN** retained traversal state may be reused at the newly captured time + +#### Scenario: Live resume at expiry +- **WHEN** a live cursor resumes exactly at its exclusive deadline +- **THEN** EACL returns a typed restart requirement and no page + +#### Scenario: Pinned snapshot after wall-clock expiry +- **WHEN** a cursor was created from an explicit snapshot at time 90 with an evidence deadline of 100 and wall time reaches 110 +- **THEN** its temporal evaluation remains pinned at 90 +- **AND** exact-basis availability, cursor TTL, key availability, and ordinary proof checks still apply + +#### Scenario: Clock regresses before certificate start +- **WHEN** a live resume captures a time earlier than the cursor certificate start +- **THEN** EACL rejects reuse rather than reviving retained state under an unproved view + +### Requirement: Continuation proofs include qualified skipped evidence +A live cursor's temporal certificate SHALL cover examined emitted and skipped candidates before the boundary, subtracting evidence, conditional residuals, retained frontier, and lookahead needed to justify continuation. Incomplete proof MUST prevent cross-time reuse rather than trigger an unbounded proof-building traversal. + +#### Scenario: Expired ban authorizes an earlier identity +- **GIVEN** a lookup skipped identity 10 because a ban was active and emitted through identity 20 +- **WHEN** that ban expires before live resumption +- **THEN** the cursor cannot resume after identity 20 and silently omit newly authorized identity 10 +- **AND** its temporal deadline forces a restart + +#### Scenario: Conditional candidate precedes boundary +- **WHEN** a candidate before the boundary was conditional under the original request context +- **THEN** the certificate and result-policy scope preserve that classification rather than replaying it as definite + +#### Scenario: Complete temporal proof is unavailable +- **WHEN** retained state cannot be assigned a complete safe interval within existing work bounds +- **THEN** EACL does not permit cross-time continuation and does not scan the remaining graph solely to create a certificate + +### Requirement: Caveat request context is authenticated continuation scope +A Caveat-aware cursor SHALL authenticate the complete canonical request context, evaluator/profile identity, and conditional-result policy needed to reproduce its page. Context supplied on resumption MUST match the authenticated scope. Relationship-bound context and named Caveat definitions SHALL remain database dependency data covered by the selected snapshot proof; parsed evaluator programs MUST NOT be serialized in cursors. + +#### Scenario: Request context changes between pages +- **WHEN** a caller resumes a cursor with different Caveat request context +- **THEN** EACL rejects the continuation and requires a separately scoped lookup + +#### Scenario: Bound context changes between pages +- **WHEN** `:touch` replaces a Relationship's bound Caveat context before resumption +- **THEN** Relation and qualifier proof validation prevent old continuation reuse + +#### Scenario: Evaluator identity changes +- **WHEN** the Caveat evaluator/profile fingerprint differs from the cursor scope +- **THEN** EACL rejects or exact-recovers only under an explicitly compatible identity and never silently adopts new semantics + +### Requirement: Conditional lookup state is stable across pages +Caveat-aware lookup cursors SHALL preserve whether emitted membership is definite or conditional and SHALL authenticate retained missing-context or residual-condition state. A continuation MUST NOT turn a conditional candidate into a definite grant, lose a definite candidate, or mix detailed and definite-only result policies. + +#### Scenario: Conditional candidate crosses a page boundary +- **WHEN** a conditional lookup candidate is the last item on one page or the first item on the next +- **THEN** its permissionship and missing-context fields remain reproducible + +#### Scenario: Multiple paths reach one resource +- **WHEN** definite and conditional permission paths reach the same resource +- **THEN** pagination applies the permission algebra before emitting one correctly classified resource identity + +#### Scenario: Boolean compatibility lookup +- **WHEN** a definite-only lookup omits conditional results +- **THEN** its cursor authenticates that result policy and cannot resume as a detailed conditional lookup + +### Requirement: A current-time or new-context view restarts pagination +A caller requesting a new Caveat context or a current temporal view after a live cursor leaves its certified interval SHALL start a new lookup from the beginning. EACL SHALL NOT rebase an existing public boundary onto that changed semantic view. + +#### Scenario: Caller requests current authorization after restart requirement +- **WHEN** a live cursor reaches its temporal deadline +- **THEN** the caller starts a new lookup without the old cursor and receives a newly captured time, result set, and boundary series diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/dependency-validated-authorization-cache/spec.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/dependency-validated-authorization-cache/spec.md new file mode 100644 index 00000000..8b498498 --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/dependency-validated-authorization-cache/spec.md @@ -0,0 +1,61 @@ +## ADDED Requirements + +### Requirement: Qualifier decode reuse is source- and version-scoped +A decoded qualifier MAY be reused only under a source lifecycle, qualifier eid, supported format, and immutable qualifier version derived from creation `t` or a certified persisted equivalent (or a stronger exact proof) that together exclude eid reuse or content replacement. Managed reuse that omits a repeated entity read SHALL additionally depend on the supported-writer/non-reuse contract and the owning Relation proof. Unknown-writer sources SHALL require exact-snapshot or content-proof validation. + +#### Scenario: Same immutable qualifier on a later basis +- **WHEN** a supported-writer source advances for unrelated data and the same qid/qualifier-version remains referenced +- **THEN** its decoded qualifier may be reused while expiry and Caveat evaluation still run for the new request + +#### Scenario: Touch replaces qualifier +- **WHEN** `:touch` installs a new qid and advances the Relation version +- **THEN** no result or decoded-qualifier lookup aliases the old qid as the new qualifier + +#### Scenario: Unknown writer mutates qualifier content +- **WHEN** a source cannot certify qualifier immutability +- **THEN** qid/qualifier-version alone cannot authorize cross-basis reuse and exact/content proof is required + +### Requirement: Caveat result cache identity is context complete +Any cached authorization value depending on Caveats SHALL distinguish canonical request context, evaluator/profile identity, selected schema/Caveat definition, Relationship-bound context, and result kind. Conditional residuals and missing fields MUST NOT alias definite values. + +#### Scenario: Request context differs +- **WHEN** two checks share graph/time but have different request context +- **THEN** their final Caveat-dependent cache entries cannot collide + +#### Scenario: Bound context changes through touch +- **WHEN** a new qualifier binds different context +- **THEN** tuple/Relation/qualifier proof changes invalidate old result reuse + +#### Scenario: Conditional is replayed as definite +- **WHEN** a cache provider returns a conditional value under a definite-value kind or incomplete identity +- **THEN** EACL rejects it as a miss or malformed entry and recomputes + +### Requirement: Temporal cache reuse requires a stability certificate +Every reusable value whose denotation can depend on expiring Relationships SHALL carry a certified half-open evaluation-time interval. A request may reuse it only when its captured time lies inside that interval and all ordinary schema/Relation/qualifier/context proofs match. + +#### Scenario: Grant reaches expiry on unchanged basis +- **WHEN** a cached grant's certificate ends at `valid-until` and a later request occurs at that instant +- **THEN** the entry is not reused even though no database version changed + +#### Scenario: Ban reaches expiry +- **WHEN** a cached denial depends on an active subtracting Relationship whose expiry is its certificate end +- **THEN** a later request at/after expiry recomputes and may grant + +#### Scenario: Permanent decisive witness +- **WHEN** the formal witness proves the result independent of every expiring alternative under the selected request +- **THEN** its certificate may be unbounded without scanning unrelated graph data + +#### Scenario: Certificate completeness is unavailable +- **WHEN** evaluation cannot certify a safe interval for a reusable intermediate/result +- **THEN** EACL returns the exact result but does not publish it for managed temporal reuse + +### Requirement: Temporal correctness does not depend on cache eviction +Expiry timers, eager removal, listeners, and bounded-store eviction SHALL affect performance only. Cache acceptance SHALL always validate the captured time against the authenticated certificate. + +#### Scenario: Expired entry remains resident +- **WHEN** a cache entry remains physically present after its certificate end +- **THEN** lookup rejects it and recomputes + +#### Scenario: Eviction callback is delayed +- **WHEN** an optional timer/callback does not run +- **THEN** authorization correctness is unchanged diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/expiring-relationships/spec.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/expiring-relationships/spec.md new file mode 100644 index 00000000..f3d42e6f --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/expiring-relationships/spec.md @@ -0,0 +1,100 @@ +## Purpose + +Defines native exclusive `valid-until` behavior for v9 qualified Relationships, including trusted time capture, positive and negative evidence, renewal, and collection-independent correctness. + +## ADDED Requirements + +### Requirement: Relationship expiry is exclusive +A qualifier MAY carry `valid-until-ms`. The Relationship SHALL be expiry-active exactly when the captured evaluation time is strictly less than that value. An absent value SHALL be unbounded. + +#### Scenario: Before expiry +- **WHEN** evaluation time is one millisecond before `valid-until` +- **THEN** the Relationship may participate subject to its Caveat + +#### Scenario: At expiry +- **WHEN** evaluation time equals `valid-until` +- **THEN** the Relationship is inactive + +#### Scenario: After expiry +- **WHEN** evaluation time is later than `valid-until` +- **THEN** the Relationship is inactive + +#### Scenario: No expiry +- **WHEN** the qualifier has no `valid-until` +- **THEN** expiry alone never removes the Relationship + +### Requirement: Evaluation time is trusted and captured once +Each top-level authorization operation, batch, or explicit temporal snapshot SHALL use one trusted evaluation-time sample for every edge, recursive step, cache decision, and result. Callers MUST NOT provide an untrusted `now` value as ordinary request context. + +#### Scenario: Clock crosses a deadline during evaluation +- **WHEN** wall time passes an expiry while one request is executing +- **THEN** every decision in that request uses the originally captured time + +#### Scenario: Two later requests share one database basis +- **WHEN** one request occurs before expiry and another after expiry without a database transaction +- **THEN** they may return different authorization results based on their distinct captured times + +#### Scenario: Test clock +- **WHEN** a test client injects a deterministic trusted clock +- **THEN** boundary results are reproducible without changing database state + +#### Scenario: Raw clock moves backward +- **WHEN** a client's underlying wall clock reports a value below its last accepted evaluation time +- **THEN** EACL does not accept the lower value as a new live authorization time +- **AND** the client applies its documented non-decreasing or strict-failure policy rather than reviving expired access + +### Requirement: Expiry applies to granting and subtracting evidence +Expiry SHALL apply uniformly to direct grants, group membership, arrows, recursion, exclusions, and deny/subtracting Relationships. The implementation MUST NOT assume that passing time can only remove permission. + +#### Scenario: Grant expires +- **WHEN** the only true grant witness expires +- **THEN** permission may change from has-permission to no-permission + +#### Scenario: Ban expires +- **WHEN** an active subtracting Relationship was the reason an exclusion was false and it expires +- **THEN** permission may change from no-permission to has-permission + +#### Scenario: Intermediate edge expires +- **WHEN** a Relationship used to traverse an arrow expires +- **THEN** downstream reachability is recomputed without that edge + +### Requirement: Expiry is independent of physical collection +An expired Relationship SHALL stop affecting authorization without any write, callback, timer, listener, or garbage-collection transaction. It MAY remain visible in stored Relationship inspection and conflict checks. + +#### Scenario: No transaction occurs at expiry +- **WHEN** wall time reaches `valid-until` and no database write occurs +- **THEN** a new authorization operation treats the Relationship inactive + +#### Scenario: Expired data remains stored +- **WHEN** an administrative stored-state read includes inactive data +- **THEN** it can return the Relationship, qualifier, and expiry while marking it inactive at the requested time + +#### Scenario: Collector is absent +- **WHEN** no expired-data maintenance process runs +- **THEN** authorization remains correct and only retained storage/scan density may grow + +### Requirement: Renewal atomically replaces expiry +Renewing, shortening, or removing an expiry SHALL use `:touch` to replace the immutable qualifier and both endpoint references in one admitted transaction. `:create` SHALL still conflict while an expired stored Relationship exists. + +#### Scenario: Renew expired Relationship +- **WHEN** `:touch` assigns a later `valid-until` to a stored expired Relationship +- **THEN** a new qualifier and tuple pair become active according to the new bound atomically + +#### Scenario: Create after expiry +- **WHEN** `:create` targets an expired but still stored first-four identity +- **THEN** it reports a Relationship conflict and directs the caller to touch or delete + +#### Scenario: Remove expiry +- **WHEN** `:touch` removes the final expiry/Caveat data +- **THEN** the Relationship canonicalizes to `nil` qualifier and the old qualifier entity is removed + +### Requirement: Stored and active Relationship reads are distinct +Public Relationship inspection SHALL distinguish physical stored state from expiry-active state and from full Caveat authorization. A caller MUST explicitly select or observe the mode. + +#### Scenario: Stored read +- **WHEN** a caller asks for stored Relationships +- **THEN** expired rows are included with qualifier metadata + +#### Scenario: Active-at read +- **WHEN** a caller asks for expiry-active Relationships at a trusted/explicit time +- **THEN** expired rows are excluded but Caveat-conditional rows are not mislabeled as authorized diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/public-authorization/spec.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/public-authorization/spec.md new file mode 100644 index 00000000..586590ed --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/public-authorization/spec.md @@ -0,0 +1,62 @@ +## ADDED Requirements + +### Requirement: Public authorization captures temporal and Caveat request context +Each public check, lookup, count, or batch SHALL select one immutable database snapshot, capture one trusted evaluation time, canonicalize one bounded request Caveat context, and use that request context throughout evaluation. Request Caveat context SHALL never be persisted as Relationship data. The complete bounded context SHALL participate in cache and cursor identity. Each demanded Caveat SHALL receive the projection onto its declared parameter names; its declared types and Relationship-bound context SHALL retain strict validation. + +#### Scenario: Same database before and after expiry +- **WHEN** two client-targeted checks select the same database basis on opposite sides of an expiry +- **THEN** each uses its own captured time and may return a different result + +#### Scenario: Context changes between checks +- **WHEN** two checks use equal database/time inputs but different Caveat request context +- **THEN** each evaluates and caches under its own canonical context identity + +#### Scenario: Several Caveats use different request fields +- **WHEN** one operation supplies fields for multiple named Caveats +- **THEN** each demanded Caveat receives only its declared fields and validates their types +- **AND** unused public fields remain part of the complete cache and cursor scope +- **AND** unknown fields in Relationship-bound context remain invalid + +#### Scenario: Batch operation +- **WHEN** one batch crosses wall-clock expiry while executing +- **THEN** every item uses the batch's single captured time + +#### Scenario: Invalid or excessive unused context +- **WHEN** a request supplies a malformed or excessive field that no demanded Caveat uses +- **THEN** admission rejects the complete request context before snapshot acquisition or any answer-cache lookup +- **AND** an empty batch applies the same context validation + +#### Scenario: Batch context is request-wide +- **WHEN** a batch item attempts to override the request Caveat context +- **THEN** batch admission rejects the per-item control before execution + +#### Scenario: Explicit snapshot pins evaluation time +- **WHEN** a caller explicitly creates an EACL snapshot and later evaluates it after wall time advances +- **THEN** the snapshot continues to use its captured database basis and evaluation time +- **AND** a client-targeted operation is required for a new current-time decision + +### Requirement: Public checks expose detailed permissionship +EACL SHALL provide a detailed check result distinguishing has-permission, no-permission, conditional-permission with missing fields/residual, and authoritative evaluation failure. Existing Boolean `can?` SHALL return true only for has-permission. + +#### Scenario: Conditional check +- **WHEN** a Caveat lacks context that could change its result +- **THEN** the detailed API returns conditional-permission and `can?` returns false + +#### Scenario: Authoritative qualifier fault +- **WHEN** qualified evidence is corrupt or evaluator execution fails +- **THEN** the detailed API reports a typed failure and no public Boolean grant is returned + +### Requirement: Lookup and count policies distinguish conditional results +Caveat-aware lookup and count operations SHALL expose or explicitly filter definite and conditional results according to a declared result policy. They MUST NOT silently count a conditional resource as definitely authorized. + +#### Scenario: Detailed lookup +- **WHEN** one resource is definite and another conditional +- **THEN** detailed lookup preserves each result state and any missing fields + +#### Scenario: Boolean compatibility lookup +- **WHEN** a caller selects definite-only compatibility behavior +- **THEN** only has-permission resources are emitted and conditional resources are excluded without being counted as grants + +#### Scenario: Conditional-only count +- **WHEN** all reachable candidates are conditional +- **THEN** a definite count does not report them as authorized and a detailed count reports the conditional category explicitly diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/qualified-relationship-evaluation/spec.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/qualified-relationship-evaluation/spec.md new file mode 100644 index 00000000..dba68194 --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/specs/qualified-relationship-evaluation/spec.md @@ -0,0 +1,147 @@ +## Purpose + +Defines how EACL resolves sparse v9 Relationship qualifiers and composes Caveat-derived definite, conditional, and fault evidence through one authorization engine. + +## ADDED Requirements + +### Requirement: Ordinary Relationships retain a zero-lookup fast path +A Relationship whose qualifier component is `nil` SHALL participate as permanent unconditional edge evidence without resolving a qualifier entity or evaluating a Caveat. The common path MUST remain on the single v9 endpoint stream. + +#### Scenario: Nil-qualified direct edge +- **WHEN** authorization encounters an ordinary endpoint value +- **THEN** it returns definite active edge evidence using no qualifier entity read + +#### Scenario: Nil-qualified arrow page +- **WHEN** a page contains only ordinary edges +- **THEN** traversal preserves existing order and result semantics without allocating one qualifier object per edge + +### Requirement: Non-nil qualifiers are resolved once and fail closed +Each distinct non-`nil` qualifier eid SHALL be resolved and validated at most once per top-level operation. Missing, malformed, wrong-version, disallowed, or otherwise invalid qualifier data MUST NOT be treated as an unconditional or absent edge. Admitted-writer single-ownership and immutability invariants MUST NOT be re-proved by a reverse whole-graph scan on the ordinary authorization path. + +#### Scenario: Valid qualifier +- **WHEN** an endpoint pair references one valid singly owned qualifier +- **THEN** the request reuses one decoded qualifier for all encounters of that qid + +#### Scenario: Missing qualifier +- **WHEN** a tuple references an entity that does not resolve +- **THEN** authorization returns a typed authoritative fault and never grants through that edge + +#### Scenario: Fault is on a subtracting edge +- **WHEN** a deny/exclusion edge has corrupt qualifier data +- **THEN** the fault propagates rather than erasing the negative evidence and granting permission + +#### Scenario: Managed writer ownership is already certified +- **WHEN** a supported-writer source returns a referenced qualifier under matching Relation/source proof +- **THEN** the hot path validates the referenced qualifier directly and does not enumerate other Relationships to prove single ownership again + +### Requirement: Caveats produce detailed permissionship +A Caveated edge SHALL merge request context with Relationship-bound context using bound-value precedence and evaluate to definite true, definite false, conditional residual, or fault. EACL SHALL expose detailed permissionship equivalent to `has-permission`, `no-permission`, or `conditional-permission` for non-fault results. + +#### Scenario: Caveat is true +- **WHEN** effective context makes the Caveat true +- **THEN** the edge contributes definite membership + +#### Scenario: Caveat is false +- **WHEN** effective context makes the Caveat false +- **THEN** the edge contributes definite non-membership + +#### Scenario: Context is incomplete +- **WHEN** missing parameters can change the Caveat result +- **THEN** the edge contributes conditional evidence with residual and missing fields + +#### Scenario: Bound value conflicts with request value +- **WHEN** both contexts contain one declared parameter +- **THEN** the Relationship-bound value is evaluated + +### Requirement: Conditional evidence composes through permission algebra +Union, intersection, exclusion, arrows, and supported recursion SHALL compose definite and conditional evidence according to their Boolean meaning while preserving bounded residuals, missing fields, order, and existing resource limits. Faults SHALL remain distinct from false. + +#### Scenario: Unconditional union witness +- **WHEN** one union branch is definitely true and another is conditional +- **THEN** the union is definitely true + +#### Scenario: Conditional intersection +- **WHEN** all known intersection branches are true and one branch is conditional +- **THEN** the intersection is conditional with the required residual + +#### Scenario: Definite false intersection +- **WHEN** any intersection branch is definitely false +- **THEN** the intersection is definitely false even if another branch is conditional + +#### Scenario: Conditional exclusion +- **WHEN** the left side is true and the right side is conditional +- **THEN** the exclusion is conditional with the logical residual `left && !right` + +#### Scenario: Conditional arrow +- **WHEN** an intermediate edge or downstream permission is conditional +- **THEN** the resulting resource evidence preserves the combined condition without changing endpoint order + +### Requirement: Qualified filtering preserves bounded work and completion state +Qualified and expired candidates SHALL consume the existing declared scan/evaluation work budgets. EACL MUST NOT perform an unbounded fill-to-page loop merely because candidates are false, expired, conditional, or faulty, and MUST preserve the distinction between a complete empty result and an incomplete result stopped by a deadline/resource bound. + +#### Scenario: Many qualified candidates are filtered +- **WHEN** a requested page encounters more false, expired, or excluded qualified candidates than the operation budget permits +- **THEN** EACL returns the existing typed bounded-work outcome with resumable progress where supported +- **AND** it does not claim that the remaining result set is empty or complete + +#### Scenario: Ordinary-only page +- **WHEN** every candidate has `nil` qualifier +- **THEN** qualification adds no secondary Relationship scan, qualifier fetch, or per-candidate Caveat allocation + +### Requirement: Qualified writes require a certified publication strategy +A backend SHALL enable qualified Relationship writes only when it can publish both endpoint refs and the owning Relation mutation stamp atomically using either certified inline qualifier allocation or a concrete prepared qualifier eid. Capability selection SHALL occur at construction/activation, not as a per-edge authorization check. + +#### Scenario: Inline-capable backend +- **WHEN** a backend is certified to resolve a newly created qualifier eid inside endpoint values +- **THEN** the committed writer may create and publish the qualifier in one transaction + +#### Scenario: Prepared-reference backend +- **WHEN** a backend requires qualifier preparation +- **THEN** an unreferenced qualifier may be created first +- **AND** one later transaction atomically publishes both endpoint values, Relation stamp, and caller-composed application datoms + +#### Scenario: Publication fails after preparation +- **WHEN** the final publication transaction fails +- **THEN** no qualified Relationship is visible and only inert orphan qualifier data may remain + +#### Scenario: Backend capability is absent +- **WHEN** no certified publication strategy is available +- **THEN** qualified writes and schema activation fail before authorization rather than attempting a best-effort or two-half update + +### Requirement: Qualifier changes replace one logical Relationship +Subject, Relation, and resource SHALL remain the Relationship identity. Caveat, bound context, and expiry differences SHALL be updated through `:touch`, not represented as parallel Relationships. + +#### Scenario: Create differs only by qualifier +- **WHEN** `:create` targets an existing first-four identity with another Caveat/context/expiry +- **THEN** it reports `:eacl/relationship-conflict` + +#### Scenario: Touch changes qualifier +- **WHEN** `:touch` changes any qualifier field +- **THEN** the semantic publication transaction replaces both exact endpoint values, removes the old current qualifier, and advances the Relation version atomically +- **AND** a backend using prepared references may have created the new unattached qualifier in an earlier inert preparation step + +#### Scenario: Delete omits qualifier +- **WHEN** `:delete` names only the logical identity +- **THEN** it removes the stored tuple pair and owned qualifier regardless of their contents + +### Requirement: Caveat evaluator capability is checked before serving +A selected schema that can reach Caveated Relationships SHALL be served only by a client with the matching certified evaluator/profile. Missing or mismatched capability MUST fail before current authorization. + +#### Scenario: JVM supported profile +- **WHEN** a JVM client has the pinned evaluator for the schema profile +- **THEN** Caveated Relationships may be activated + +#### Scenario: ClojureScript has no evaluator +- **WHEN** a CLJS client lacks a matching evaluator +- **THEN** it cannot serve Caveated schema and does not silently ignore Caveats + +### Requirement: Boolean compatibility is fail closed +The public Boolean `can?` result SHALL be true only for definite `has-permission`. Definite denial, conditional permission, and authoritative faults SHALL not return true; detailed APIs SHALL preserve their distinct diagnostics. + +#### Scenario: Conditional through can? +- **WHEN** a detailed check is conditional +- **THEN** `can?` returns false + +#### Scenario: Definite grant through can? +- **WHEN** a detailed check is definite has-permission +- **THEN** `can?` returns true diff --git a/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/tasks.md b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/tasks.md new file mode 100644 index 00000000..0ca8840c --- /dev/null +++ b/openspec/changes/2026-09-04-03-enable-qualified-relationship-evaluation/tasks.md @@ -0,0 +1,69 @@ +## 1. Complete temporal/conditional formal models before engine code + +- [x] 1.1 Extend edge and permission denotation models with optional qualifier refs, prepared-but-unattached qualifier states, atomic publication, authoritative qualifier faults, and one captured evaluation time; verify unattached qualifiers have no denotation and all finite results are total and deterministic. +- [x] 1.2 Model Caveat true/false/conditional evidence through union, intersection, exclusion, arrows, and supported recursion; verify residual algebra and fault propagation against exhaustive truth tables. +- [x] 1.3 Model exclusive `valid-until`, including expiring positive, intermediate, and subtracting evidence; verify counterexamples kill any monotonic “time only removes permission” assumption. +- [x] 1.4 Prove witness-aware temporal stability intervals for every operator/result outcome and no-publication fallback when completeness is unavailable; verify killed horizon/minimum rules fail. +- [x] 1.5 Model cache acceptance and cursor continuation with source/schema/Relation/qualifier proofs, request context, evaluator identity, pinned/live temporal mode, certified time interval, and result kind; verify stale grants, expired bans, and conditional aliases are rejected. +- [x] 1.6 Register models, resource pins, mutation controls, and assurance mappings; verify `bin/formal fast` and every affected formal gate are green before editing production engine/cache/cursor code. + +## 2. Carry qualifier refs through one scan engine + +- [x] 2.1 Extend the backend scan contract to expose opposite eid plus optional qualifier eid in an allocation-minimal measured representation; verify order, bounds, uniqueness, and public result shape remain unchanged. +- [x] 2.2 Update Datomic, Datahike, DataScript, and Datalevin forward/reverse/direct scan primitives to return aligned qualifier data from the single v9 stream; verify no adapter reads a second Relationship attribute. +- [x] 2.3 Add one shared edge-qualification seam and retain the existing nil-eid fast path; verify instrumentation records zero qualifier reads and no per-edge qualifier map allocation for ordinary fixtures. +- [x] 2.4 Preserve deadlines, cancellation, dimensional work accounting, scan-response cache scope, and continuation heads with the new compact edge shape; verify filtered qualified candidates count against existing bounds and no fill-to-page loop hides incomplete work. + +## 3. Resolve and cache immutable qualifiers + +- [x] 3.1 Implement exact qualifier fetch/decode/validation across bundled backends, including format, certified creation `t`/version, Caveat allowance, context, and expiry; verify malformed/dangling fixtures fault while managed hot-path instrumentation performs no reverse graph scan for writer-certified ownership. +- [x] 3.2 Add one bounded request-local qid cache shared by recursive/operator paths; verify one distinct non-`nil` qid causes at most one qualifier fetch per top-level operation. +- [x] 3.3 Add optional longer-lived qualifier decode caching keyed by source lifecycle, qid, certified creation `t`/version, and format, conditioned on owning Relation/supported-writer proof with exact/content-proof fallback for unknown writers; verify eid reuse/reset, deletion, and in-place mutation traces cannot reuse stale data. +- [x] 3.4 Ensure qualifier-cache values are decoded data only and perform expiry/Caveat evaluation per request; verify different times/contexts reuse structure but not final authorization. + +## 4. Activate Caveat evaluation and public permissionship + +- [x] 4.1 Integrate Phase 2 context merge, partial evaluator, and JVM cel-parser adapter at the qualification seam; verify complete, short-circuit, conditional, wrong-type, overload, and budget cases. +- [x] 4.2 Implement one internal evidence type for true, false, conditional residual/missing fields, and fault; verify canonical bounded encoding/equality across CLJ and supported CLJS paths. +- [x] 4.3 Extend union, intersection, exclusion, arrow, and recursive evaluators to compose evidence per the green model without duplicating traversal; verify production-vs-model generated differentials. +- [x] 4.4 Add detailed check/lookup/count result policies and request Caveat context while preserving `can?` true-only-on-definite behavior; verify conditional and fault values never appear as Boolean grants. +- [x] 4.5 Enforce evaluator/profile and qualified-writer publication capabilities before serving Caveated schema; verify JVM default, CLJS absent, custom mismatch, prepared-reference backend, and unsupported-writer fixtures. + +## 5. Activate trusted exclusive expiry + +- [x] 5.1 Add one trusted clock sample plus process-local non-decreasing high-water mark to the top-level request/snapshot context and prohibit per-edge ambient clock reads; verify a request crossing expiry is consistent and a backward raw-clock step cannot revive access. +- [x] 5.2 Evaluate `evaluation-time-ms < valid-until-ms` before Caveat program work; verify before/equal/after boundaries and expired Caveat compile suppression. +- [x] 5.3 Apply expiry uniformly to grant, group, arrow, recursion, exclusion, and deny evidence; verify an expiring ban can change denial to grant on a later request. +- [x] 5.4 Add stored-versus-active Relationship inspection and renewal/shortening/removal through immutable qualifier `:touch`; verify create still conflicts with retained expired identity. + +## 6. Make result and subproblem caches temporally coherent + +- [x] 6.1 Carry certified temporal intervals through production evidence and operator results using the proven witness rules; verify every reused value's interval contains the request time. +- [x] 6.2 Extend exact/managed cache keys and authenticated values with evaluator/profile, canonical request-context identity, result kind, qualifier proof inputs, and temporal certificate; verify no definite/conditional/time alias. +- [x] 6.3 Make resident-but-expired certificates ordinary misses independent of timers/eviction callbacks; verify disabled/delayed eviction cannot change results. +- [x] 6.4 Update scan-response, range, answer, denotation, and recursive checkpoint reuse only where their semantic value depends on qualification; verify unrelated structural caches are not needlessly invalidated. +- [x] 6.5 Add cached-vs-uncached differential traces spanning qualifier touch, Caveat schema change, context change, expiry without write, expired ban, and source lifecycle reset; verify exact equality or mapped fault. + +## 7. Certify qualified pagination scope + +- [x] 7.1 Extend cursor envelopes/scopes with pinned/live temporal mode, original evaluation time, complete exclusive reuse interval, canonical Caveat context identity, evaluator/profile, result policy, and qualified storage/order ABI; verify tampering and mismatches fail before traversal. +- [x] 7.2 Make explicit-snapshot cursors preserve their pinned basis/time and make client-targeted live cursors capture fresh time on resume; verify live reuse succeeds only inside the certified interval and equality with its deadline returns a typed restart requirement. +- [x] 7.3 Cover examined emitted/skipped conditional/expired/subtracting evidence, frontier, lookahead, and residual state in continuation certificates; verify an expired ban before the boundary cannot cause silent omission. +- [x] 7.4 Refuse cross-time continuation when a complete certificate is unavailable without scanning the remaining graph solely to create one; verify bounded-work outcomes remain honest. +- [x] 7.5 Document and test the explicit “start a new lookup for now/new context” workflow; verify old cursor use never silently restarts or rebases. + +## 8. Complete write, delete, integrity, and proof integration + +- [x] 8.1 Extend public Relationship update normalization with optional Caveat/context/`valid-until` and first-four conflict identity; verify SpiceDB-like create/touch/delete cases and one-Caveat maximum. +- [x] 8.2 Implement the Phase 2 certified inline and prepared-reference publication paths: atomically attach/swap both exact tuple refs, Relation stamp, old qualifier cleanup, and caller-composed datoms at the semantic commit point; verify concurrent touch/delete, failed prepared publication, orphan cleanup, and one-sided repair cannot grant, leak a half-edge, or share qualifiers. +- [x] 8.3 Extend object deletion, schema orphan checks, integrity reports, and unknown-writer content proofs through qualifier and Caveat dependencies; verify faults on subtracting evidence propagate. +- [x] 8.4 Keep expired Relationship collection absent from the authorization contract; verify no test or runtime requires a scheduler/GC write for expiry correctness. + +## 9. Certify and qualify before activation + +- [x] 9.1 Add production refinement bridges for edge qualification, Caveat algebra, expiry boundary, temporal certificate, cache acceptance, and cursor scope; verify exhaustive finite and randomized comparisons are green. +- [x] 9.2 Add mutation controls for omitted q lookup, missing-q-to-`nil`, non-atomic pair publication, unresolved qid publication, `<=` expiry, reversed context precedence, conditional-as-true, fault-as-absence, unsafe q cache, missing Relation stamp, and expired-ban reuse; verify every mutant is killed. +- [x] 9.3 Benchmark 0%, 5%, and 10% qualified distributions plus concentrated qualified/expired endpoint prefixes across direct, arrow, recursive, page, count, cache-hit, and cold/warm workloads; verify recorded numerical release budgets. +- [x] 9.4 Audit hot paths for runtime model calls, shadow authorization, redundant full traversals, unconditional qualifier pulls, and excess allocations; verify none were introduced to satisfy formal/CI gates. +- [x] 9.5 Update public docs, schema examples, error/result reference, cache/cursor semantics, monotonic-clock/Peer-skew guidance, pinned-versus-live snapshot warning, CLJS evaluator boundary, and rollout instructions; verify examples execute against the qualified implementation. +- [x] 9.6 Run CI-equivalent nREPL tests, CLJS suites, all affected formal gates, source-closure, performance gates, dependency audit, and strict OpenSpec validation; verify everything is green before enabling the qualified semantic epoch. diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/.openspec.yaml b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/.openspec.yaml new file mode 100644 index 00000000..1d9aeef9 --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-04 diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/design.md b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/design.md new file mode 100644 index 00000000..7704c3b1 --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/design.md @@ -0,0 +1,210 @@ +## Context + +See `proposal.md` for motivation. EACL already has static keyring configuration and key identifiers for protected formats, but the normalized keys are captured during client construction. The current documentation describes staged rotation, yet operators cannot update a running Peer through one supported public control surface. + +The primary security keys protect cursor authenticity/confidentiality and portable-cache authenticity. EACL may also use a dedicated Zed-token ring; when absent, the Zed-token domain derives from the primary ring as today. None of these keys are authorization data or suitable for the EACL database. Rotation must therefore be externally orchestrated but locally linearizable, must permit old and new keys to overlap, and must define different failure behavior for user-supplied cursors/tokens versus optional caches. + +The existing cursor contract permits no TTL by default. Consequently, no implementation can both retire a key after a finite overlap and promise that every cursor minted under that key remains resumable. This design makes that trade-off explicit rather than inventing an unsafe automatic retirement heuristic. + +## Goals / Non-Goals + +**Goals:** + +- Let a consumer add, activate, and retire security keys on a running Peer without replacing clients or interrupting authorization service. +- Give every encode/decode operation one immutable, internally consistent keyring snapshot. +- Mint only with the active key and verify with any retained key selected by authenticated key identifier. +- Make partial rollout, stale control-plane writes, key-id reuse, active-key retirement, and cache/cursor behavior deterministic and fail closed. +- Let one controller feed multiple clients on one Peer while preserving adapter/module isolation. +- Keep authorization hot paths free of network calls, database reads, distributed consensus, runtime reference-model evaluation, and keyring-wide cryptographic trial loops. +- Supply a concrete zero-downtime deployment runbook and observability that never exposes key material. + +**Non-Goals:** + +- Fetching keys from a secret manager, exposing an administrative HTTP endpoint, discovering Peers, or coordinating rollout acknowledgements across a cluster. +- Persisting key bytes, encrypted key bytes, provider handles, or a desired keyring in EACL's databases. +- Keeping non-expiring artifacts valid after the operator deliberately retires their key. +- Rotating datastore credentials, object-codec secrets, source-lifecycle identity, or unrelated application keys. +- Changing authorization results, cache dependency proofs, graph revisions, Relationship storage, Caveat semantics, or temporal semantics. +- Proving cryptographic primitive security inside EACL; the existing cryptographic assumptions and vetted runtime primitives remain the trust boundary. + +## Decisions + +### D1. Add one mutable controller whose values are immutable snapshots + +Introduce one backend-neutral controller abstraction, with any number of explicitly scoped instances, conceptually shaped as: + +```clojure +{:generation 17 + :active-kid :eacl-2026-10 + :keys {:eacl-2026-09 normalized-root-key-1 + :eacl-2026-10 normalized-root-key-2} + :retired-kids #{:eacl-2026-08}} +``` + +The controller owns an atomic reference to a validated immutable state. Secret bytes are private implementation values; public status returns only: + +```clojure +{:generation 17 + :active-kid :eacl-2026-10 + :accepted-kids #{:eacl-2026-09 :eacl-2026-10} + :retired-kids #{:eacl-2026-08}} +``` + +A controller may be passed to multiple clients on one Peer: + +```clojure +(def primary-ring + (eacl/security-keyring + {:keys {:eacl-2026-09 old-root} + :active-kid :eacl-2026-09})) + +(def acl + (make-client conn {:security-keyring-controller primary-ring})) +``` + +For source compatibility, static `:security-key`, `:security-keyring`, and `:security-kid` create a private primary controller. Existing dedicated Zed-token key options create a private dedicated controller; a new `:zed-token-keyring-controller` permits live rotation of that separate ring. When neither dedicated static options nor a dedicated controller is supplied, Zed tokens reuse the primary controller with their existing distinct derivation domain. Supplying both a controller and static material for the same scope is rejected as ambiguous. + +*Alternative considered:* mutate each client independently. Rejected because applications commonly hold several clients/caches on one Peer and could accidentally leave them on inconsistent rings. + +### D2. Make complete-state compare-and-set the primitive + +The authoritative public mutation is a full desired-state replacement with an expected generation: + +```clojure +(eacl/replace-security-keyring! + keyring + {:expected-generation 17 + :keys {:eacl-2026-09 old-root + :eacl-2026-10 new-root} + :active-kid :eacl-2026-10}) +``` + +A stale expected generation fails with a typed conflict and reveals only the current generation/status. Convenience functions `add-security-key!`, `activate-security-key!`, and `retire-security-key!` are bounded CAS loops over the same validator, not separate state machines. + +The validator enforces: + +- the ring is non-empty; +- every key id is canonical, bounded, and unique; +- every key meets the existing minimum entropy/length contract; +- `active-kid` exists in `keys`; +- an active key is not removed unless another installed key becomes active in the same replacement; +- accepted-key and retired-id counts stay within explicit hard/configured ceilings; +- a retired key id cannot be reintroduced during the controller lifetime, even with the same material, because doing so would revive old artifacts; +- status, errors, and equality diagnostics never include secret bytes. + +*Alternative considered:* independent add/remove/current atoms. Rejected because readers could observe an active id without its key or a removed key still selected for minting. + +### D3. Linearize encode and decode on one state snapshot + +Every mint or decode captures the controller state once: + +```text +mint: + snapshot := ring.state + key := snapshot.keys[snapshot.active-kid] + derive domain key + emit authenticated envelope naming snapshot.active-kid + +decode: + parse bounded envelope and obtain kid + snapshot := ring.state + key := snapshot.keys[kid] or fail + authenticate before decoding protected payload +``` + +No request iterates over all keys. The visible key id is authenticated as associated data or inside the authenticated envelope, so changing it cannot redirect validation without detection. + +A concurrent operation that captured the old state before retirement may complete and is linearized before the retirement. Once `retire-security-key!` returns, every newly started operation observes a state without that key. + +Derived domain keys may be cached per `[generation kid domain format-version]` inside the controller. Replacing the state drops unreachable derived-key entries. Key activation does not clear authorization caches because their denotations are unchanged. + +### D4. Use distribute, activate, overlap, retire + +The documented cluster rollout is: + +1. **Generate externally:** create a new random root key and globally unique key id outside EACL. +2. **Distribute:** add the new key as accepted but inactive on every Peer. +3. **Observe:** verify every Peer reports the expected generation and accepted key id through the application's authenticated control plane. +4. **Activate:** select the new key for minting on every Peer; old keys remain accepted. +5. **Overlap:** retain the old key for at least the maximum age of artifacts that must remain valid, plus rollout/clock margin. +6. **Retire:** remove the old key on every Peer and verify its identifier is absent. + +A Peer that has not received the new key cannot decode artifacts minted by a Peer that has activated it. EACL surfaces that error; it cannot repair a bad rollout without receiving the secret. The operational API therefore makes install and activation separate by design. + +For non-expiring cursors, the overlap has no finite safe upper bound. Operators must either retain the old key, configure a finite cursor TTL before relying on bounded rotation, or accept deliberate cursor invalidation at retirement. + +### D5. Keep key material external and status non-secret + +No keyring update writes any EACL database datom, source token, cache snapshot, cursor payload, audit event, metric label, exception data, or log line containing raw/encoded key material. EACL may emit safe events containing operation, key id, generation, success/failure category, and counts. + +The controller accepts in-memory key material supplied by the application. Provider-specific handles are outside the portable API unless they can synchronously yield the existing normalized root-key representation without a per-request remote call. + +Best-effort cleanup may discard private references to retired arrays and derived keys, but the documentation must not promise guaranteed JVM memory zeroization for immutable strings or garbage-collected objects. Examples use byte material or secret-manager outputs rather than source-code literals. + +### D6. Tag every protected artifact and related cursor cache state with its key id + +Every cursor and portable authenticated cache entry must select exactly one primary-controller key by authenticated `kid`. Every Zed token selects exactly one key from its configured dedicated controller or, when none exists, the primary controller under the Zed derivation domain. Formats that already carry a key id retain it; any remaining implicit-key path is upgraded rather than attempting every ring key. + +Cursor-related process-local entries carry the minting token's key id: + +```clojure +{:cursor-identity ... + :security-kid :eacl-2026-10 + :continuation ...} +``` + +Resume first authenticates the supplied cursor against the current ring, then requires any found continuation/rendered-page entry to name the same retained key id. Retirement eagerly evicts entries by key id. Missed or racing eviction cannot authorize reuse because the current ring/key-id check is mandatory at the external cursor boundary. + +Portable completed-cache entries and private entries whose trust originated only from such an imported artifact retain the verifying key id/trust epoch. If that key is unavailable they are untrusted optional data and become misses. Locally computed authorization entries derive correctness from the selected snapshot/proofs rather than a transport key and remain reusable. A cursor or causal token supplied by the caller is part of the requested consistency/pagination contract and therefore fails loudly rather than falling back to another snapshot or first page. + +### D7. Key rotation does not rotate authorization identity + +Adding or activating a key in either controller scope does not change: + +- selected database basis; +- source lifecycle; +- schema generation; +- Relation generations; +- qualifier generation; +- authorization semantic key; +- result ordering. + +It may change the byte representation and key id of newly minted artifacts. Existing entries remain usable while their key remains accepted. Retirement removes cryptographic acceptability only; it does not invalidate or recompute authorization data that can be safely recomputed from the selected snapshot. + +This separation avoids turning routine key rollout into a whole-client authorization-cache flush. Retirement synchronously detaches or makes ineligible cursor state and externally authenticated trust under the retired id; targeted physical cleanup is operational hygiene and memory reclamation. Locally computed answers are unaffected. + +### D8. Verify the state machine without a production shadow interpreter + +Add a small pure transition oracle used only by tests. Generate sequential and concurrent traces of add, activate, replace, retire, mint, decode, cache lookup, and cursor resume; assert linearizable outcomes against the oracle and deterministic killed controls. + +The production request path contains only: + +- one atomic state read; +- one direct key-id map lookup; +- normal domain-key derivation/cache lookup; +- existing cryptographic verification. + +No runtime model comparison, ring-wide fallback loop, Peer coordination check, database check, or network call is introduced to satisfy verification. + +## Risks / Trade-offs + +- **[Partial rollout causes temporary decode failures]** → Separate install from activation, provide generation/status APIs, document cluster acknowledgements, and keep old/new overlap. +- **[Non-expiring cursors prevent finite lossless retirement]** → State this explicitly; require finite TTL or intentional invalidation when operators need a bounded retirement date. +- **[A retired key id is reused]** → Reject every in-lifetime reintroduction and document globally unique epoch identifiers across restarts so retired artifacts cannot be revived. +- **[A stale control-plane write removes a newer key]** → Require expected-generation CAS for full replacement and return a typed conflict. +- **[Concurrent retirement races with decode]** → Linearize each operation on one immutable state snapshot; after retirement returns, later operations cannot obtain the key. +- **[Targeted cache cleanup is incomplete]** → Make current key presence or an equivalent synchronously advanced trust epoch authoritative; externally authenticated optional data misses and cursors error, while local computed answers remain independent. +- **[Secrets leak through observability]** → Closed status/error/event schemas contain identifiers and counts only; add redaction tests over logs and ex-data. +- **[Shared controller broadens blast radius]** → Sharing is explicit and scoped to clients that deliberately receive the same controller; static options still create private controllers. + +## Migration Plan + +1. Add the controller and pure state validator while preserving static construction through a private controller. +2. Upgrade/confirm every protected format carries an authenticated key id and add per-key cache metadata. +3. Route mint/decode operations through atomic controller snapshots. +4. Add public replacement/convenience/status APIs and safe events. +5. Add per-key eager eviction and all cross-backend/concurrency tests. +6. Publish the deployment runbook and examples for external secret watchers/control planes. +7. Exercise a two-Peer old/new overlap, activation skew, and retirement drill before release. + +There is no database migration or rollback. A deployment may revert to static configuration only after preserving every key still needed to decode outstanding artifacts. Removing the controller while dropping an old key intentionally invalidates those artifacts. diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/proposal.md b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/proposal.md new file mode 100644 index 00000000..9d41b8af --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/proposal.md @@ -0,0 +1,39 @@ +## Why + +EACL already accepts static security keyrings with an active key identifier, but changing that configuration normally requires replacing clients or restarting Peers. That turns routine cryptographic key rotation into a coordinated outage risk and makes it too easy to retire an old key before every Peer can verify artifacts minted during the overlap. + +This phase adds one external, live, atomic keyring control surface. Library consumers remain responsible for obtaining secrets from their secret manager and distributing updates to every Peer; EACL owns only the in-process state transition, artifact key selection, fail-closed behavior, cache handling, and operational guidance. + +## What Changes + +- Order this as Phase 4 after `2026-09-04-03-enable-qualified-relationship-evaluation` for the release series, while keeping the keyring implementation independent of Relationship qualifier semantics and database storage. +- Add one public, non-durable `SecurityKeyring` controller abstraction that may be shared by multiple EACL clients on one Peer. The primary controller is initialized from existing `:security-key`, `:security-keyring`, and `:security-kid` configuration; an optional dedicated Zed-token controller is initialized from the existing Zed-token key options and otherwise reuses the primary controller through existing domain separation. +- Add an atomic full-state replacement operation with an expected generation plus convenience operations to add an inactive verification key, activate an installed key for new artifacts, and retire a non-active key. +- Use one immutable keyring snapshot per encode or decode operation. New artifacts use exactly the active key; existing artifacts may be accepted by any currently retained key named by their authenticated key identifier. +- Define the zero-downtime rollout as distribute, observe, activate, overlap, and retire. A new key cannot become active until it is installed, and an active key cannot be retired without atomically selecting another installed key. +- Keep secret material outside Datomic, Datahike, DataScript, Datalevin, cache snapshots, cursor payloads, metrics, logs, errors, and diagnostics. Status APIs expose only key identifiers, the active identifier, generation, and safe counts. +- Require globally unique key identifiers per key epoch and reject any reintroduction of a retired identifier within one controller lifetime; documentation prohibits identifier reuse across restarts because reviving an id can revive old artifacts. +- Require cursor and authenticated portable-cache formats to carry an authenticated primary-controller key identifier, and Zed tokens to carry an authenticated identifier from their selected primary-or-dedicated controller. Cursor continuation/cache entries also retain the minting key identifier and cannot resume after that key has been retired. +- Treat an unavailable cursor/token key as a typed fail-closed request error with no silent restart. Treat an authenticated cache entry whose key is unavailable as a cache miss followed by ordinary selected-snapshot evaluation. +- Invalidate process-local cursor/continuation/rendered-page state and externally authenticated/imported cache records associated with a retired key, while leaving locally computed authorization caches intact. Artifact key-presence validation or an equivalent synchronous trust-epoch detachment remains the correctness boundary if targeted cleanup is delayed. +- Preserve authorization, source-lifecycle, schema, Relationship, and proof-cache semantics across key addition or activation. Cryptographic rotation changes which artifacts can be authenticated; it does not create a new authorization graph revision. +- Document the special case of non-expiring cursors: an old verification key must be retained indefinitely or its retirement intentionally invalidates those cursors. Recommend bounded cursor TTL where operators require a finite retirement deadline. +- Add deterministic state-machine, concurrency, overlap, partial-rollout, retirement, restart, cache, and cross-Peer tests without adding a runtime reference interpreter, distributed coordinator, secret watcher, or per-request control-plane call. + +## Capabilities + +### New Capabilities + +- `live-security-keyring-rotation`: externally driven, atomic in-process key installation, activation, overlap, retirement, status, concurrency, and operational rollout semantics. + +### Modified Capabilities + +- `cursor-token-handling`: authenticated key identifiers, live-ring decode behavior, retired-key failure, cursor-cache key affinity, and non-expiring-cursor retirement guidance. +- `backend-unification`: one shared runtime keyring controller and equivalent rotation behavior across bundled adapters. +- `modular-backend-workspace`: shared cryptographic format service, dependency isolation, non-durable secret handling, and consumer-facing rotation documentation. + +## Impact + +The change affects client construction, runtime ownership, secure-format key derivation, cursor/Zed/cache envelopes, continuation and rendered-page stores, cache import/export, telemetry, public API documentation, backend conformance suites, and deployment runbooks. It adds no database schema or migration and must not require restarting EACL clients or Peers. + +EACL does not become a secret-distribution system. A consumer may drive the controller from Kubernetes Secrets, Vault, AWS Secrets Manager, an internal control plane, or another authenticated channel, but no provider-specific watcher or network endpoint is part of this change. diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/backend-unification/spec.md b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/backend-unification/spec.md new file mode 100644 index 00000000..02366840 --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/backend-unification/spec.md @@ -0,0 +1,45 @@ +## MODIFIED Requirements + +### Requirement: Uniform construction surface +`make-client` SHALL accept one documented option map across backends, with per-backend extensions explicitly namespaced and documented; equivalent options SHALL share names and semantics, including one token-key option family, one cursor-TTL name, one backend-neutral primary live security-keyring controller, and one optional dedicated Zed-token controller with existing primary-ring fallback. Unknown-option errors SHALL be uniform. Supplying a live controller together with static security-key material MUST fail as ambiguous configuration. + +#### Scenario: Switching backends +- **WHEN** a consumer moves a valid Datomic client configuration to Datahike or DataScript, changing only the connection/database argument and any documented per-backend extension +- **THEN** construction succeeds without renaming semantically identical options + +#### Scenario: Switching backends with a live controller +- **WHEN** the same valid controller is supplied to clients for two bundled backends supported in one runtime +- **THEN** both observe equivalent keyring generation, install, activate, retire, encode, and decode semantics + +#### Scenario: Static and live key sources conflict +- **WHEN** a client supplies a primary or dedicated Zed-token controller together with static key/keyring options for the same scope +- **THEN** every bundled backend rejects construction with the same typed invalid-configuration error + +### Requirement: Shared codecs everywhere +Endpoint-pair encoding/decoding and protected token/cache codecs SHALL be consumed from the shared core by every backend; no backend SHALL inline its own copy of a shared encoding. Every protected envelope MUST carry an authenticated key identifier and perform a direct lookup in one captured live-ring snapshot; no backend MAY retain an implicit singleton-key format or ring-wide trial-decryption loop. + +#### Scenario: Codec change propagation +- **WHEN** the shared endpoint-pair or protected-envelope encoding changes +- **THEN** all bundled backends observe the change by construction, with no hand-rolled tuple or token literals to drift + +#### Scenario: Key id is tampered with +- **WHEN** an attacker changes the visible key identifier on a protected artifact +- **THEN** authentication fails before the protected payload is accepted + +#### Scenario: Key is absent +- **WHEN** an artifact names a key unavailable in the captured controller state +- **THEN** each bundled backend produces the same artifact-specific typed error or cache-miss behavior + +## ADDED Requirements + +### Requirement: Shared live keyring orchestration +Public operation orchestration SHALL integrate the live keyring controller once in the core module and SHALL pass immutable per-operation cryptographic snapshots to shared codecs. Backend modules MUST NOT implement independent live-ring state machines or read mutable keyring state more than once per protected encode/decode operation. + +#### Scenario: Backend codec receives captured state +- **WHEN** a backend emits or decodes a protected cursor through shared orchestration +- **THEN** it receives the key identifier and derived/key material selected from one captured controller generation +- **AND** does not query a backend-local ring during the same operation + +#### Scenario: Shared controller changes +- **WHEN** an application updates one controller shared by multiple backend clients on a Peer +- **THEN** subsequent protected operations by each sharing client observe the same complete new generation diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/cursor-token-handling/spec.md b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/cursor-token-handling/spec.md new file mode 100644 index 00000000..aa7476f7 --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/cursor-token-handling/spec.md @@ -0,0 +1,58 @@ +## MODIFIED Requirements + +### Requirement: Invalid cursor tokens throw typed errors +`token->cursor` SHALL return `nil` only for a `nil` input meaning first page and SHALL pass supported raw cursor maps through unchanged where the existing backward-compatibility contract allows them. Every non-nil encoded cursor MUST name exactly one authenticated security key identifier. A malformed cursor, authentication failure, unknown or retired key identifier, mismatched cursor-cache key identifier, or unsupported format SHALL throw `ex-info` with `:type :eacl/invalid-cursor` and MUST NOT silently restart pagination from the first page. + +#### Scenario: Garbage token fails loudly +- **WHEN** `lookup-resources` is called with `:cursor "eacl1_not-valid"` or `:cursor "garbage"` +- **THEN** an `ex-info` with `:type :eacl/invalid-cursor` is thrown and the first page is not silently returned + +#### Scenario: nil cursor still means first page +- **WHEN** `lookup-resources` is called with `:cursor nil` +- **THEN** the first page is returned + +#### Scenario: Retired key names an outstanding cursor +- **WHEN** a cursor names a key identifier that has been retired from the live ring +- **THEN** resume fails with `:eacl/invalid-cursor` and reason `:security-key-unavailable` +- **AND** EACL does not try another key, rebase, replay unauthenticated state, or restart at page one + +#### Scenario: Cursor cache entry names another key +- **WHEN** an authenticated cursor names key K1 but a process-local continuation or rendered-page entry under its identity names K2 +- **THEN** EACL ignores or rejects that entry and reconstructs only from authenticated cursor state under the existing continuation contract + +### Requirement: Cursor TTL is configurable and defaults to no expiry +Cursor expiry SHALL remain off by default for every backend. Tokens minted without `:cursor-ttl-seconds` SHALL carry no age expiry and SHALL remain age-valid only while their named security key and source lifecycle remain accepted. When a positive TTL is configured, minted tokens SHALL embed an expiry and decoding at or after that instant SHALL throw `ex-info` with `:type :eacl.pagination/expired-cursor` and `:reason :expired` without restarting pagination. Cache/checkpoint retention SHALL NOT determine cursor lifetime. Key retirement is independent of age expiry and MUST fail as unavailable authenticated key material. + +#### Scenario: Slow batch pagination does not restart +- **WHEN** a client without `:cursor-ttl-seconds` resumes pagination with a token minted more than 5 minutes ago whose key remains accepted +- **THEN** the next page is returned normally and age alone does not invalidate the cursor + +#### Scenario: Configured TTL is enforced loudly +- **WHEN** a client configured with `{:cursor-ttl-seconds 60}` decodes a token at or after its expiry +- **THEN** an `:eacl.pagination/expired-cursor` error with `:reason :expired` is thrown and pagination does not restart + +#### Scenario: Key is deliberately retired +- **WHEN** a non-expiring cursor names a key id absent from the live keyring +- **THEN** EACL rejects it as an invalid authenticated cursor with reason `:security-key-unavailable` rather than age expiry + +#### Scenario: Source lifecycle changes +- **WHEN** restore, reset, excision, purge, branch replacement, or destructive history replacement rotates the source lifecycle +- **THEN** a prior non-expiring cursor is rejected for lifecycle mismatch and never interpreted against replacement history + +#### Scenario: Non-expiring cursor must survive rotation losslessly +- **WHEN** an operator requires every non-expiring cursor minted under an old key to remain resumable +- **THEN** documentation requires retaining that key indefinitely +- **AND** retiring it is documented as intentional invalidation rather than a lossless finite-overlap rotation + +## ADDED Requirements + +### Requirement: Cursor state is key-affine and retireable +Every process-local cursor, continuation, visited-page, rendered-page, and equivalent replay entry SHALL retain the security key identifier of the authenticated cursor series that created it. Retiring a key SHOULD eagerly evict entries naming it, while current-ring authentication MUST remain the correctness boundary even if eager eviction is delayed or incomplete. + +#### Scenario: Retired cursor state remains in memory briefly +- **WHEN** an entry naming a retired key survives an eviction race +- **THEN** a new resume request fails at live-ring cursor authentication before that entry can influence pagination + +#### Scenario: New key activates without global cursor eviction +- **WHEN** a new key becomes active while the old key remains accepted +- **THEN** cursor series under both keys can resume and entries for unrelated retained keys are not globally cleared diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/live-security-keyring-rotation/spec.md b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/live-security-keyring-rotation/spec.md new file mode 100644 index 00000000..18b70046 --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/live-security-keyring-rotation/spec.md @@ -0,0 +1,149 @@ +## Purpose + +Enable externally orchestrated, zero-downtime rotation of EACL security keys on running Peers while keeping secret material non-durable and every local transition atomic and fail closed. + +## ADDED Requirements + +### Requirement: One live non-durable keyring controller abstraction + +EACL SHALL provide one backend-neutral live security-keyring controller abstraction whose instances contain a monotonically increasing generation, one active key identifier, and one or more accepted keys. A primary instance SHALL protect cursors and portable cache artifacts. An optional dedicated instance MAY protect Zed tokens; when absent, Zed tokens SHALL use the primary instance through their distinct derivation domain. Controllers and all secret key material MUST remain in process memory and MUST NOT be persisted in an EACL database or portable cache snapshot. + +#### Scenario: Shared controller initializes clients + +- **WHEN** multiple EACL clients on one Peer are constructed with the same valid controller +- **THEN** they use the same current keyring generation and active key without copying an independently mutable configuration + +#### Scenario: Static configuration remains usable + +- **WHEN** a client is constructed with existing static primary or dedicated Zed-token key options and no controller for that scope +- **THEN** EACL creates private controller instances with equivalent keyring, fallback, and active-key semantics + +#### Scenario: Zed tokens use primary fallback + +- **WHEN** a client configures no dedicated Zed-token controller or static Zed-token keyring +- **THEN** Zed tokens use the primary controller with their separate cryptographic derivation domain + +#### Scenario: Ambiguous construction is rejected + +- **WHEN** a client supplies both a live controller and static key material for the same primary or Zed-token scope +- **THEN** construction fails with a typed invalid-configuration error before retaining either source + +### Requirement: Keyring replacement is atomic and generation-guarded + +The controller SHALL support atomic replacement of the complete accepted-key map and active key id guarded by an expected generation. A successful replacement MUST expose either the complete old state or the complete new state to concurrent operations and MUST advance the generation exactly once. + +#### Scenario: Valid desired state replaces atomically + +- **WHEN** a caller supplies the current generation, a non-empty valid key map, and an active id present in that map +- **THEN** the complete new state becomes visible atomically and the returned status names the next generation + +#### Scenario: Stale update conflicts + +- **WHEN** a caller supplies an expected generation older than the controller's current generation +- **THEN** EACL rejects the replacement with a typed conflict that exposes safe current status but no key material + +#### Scenario: Active key cannot disappear accidentally + +- **WHEN** a desired state removes the current active key without selecting another retained key in the same replacement +- **THEN** validation rejects the update and the old state remains unchanged + +### Requirement: Install, activate, and retire have separate semantics + +EACL SHALL provide convenience operations to install an accepted inactive key, activate an installed key for new artifacts, and retire a non-active key. Each convenience operation MUST linearize through the same atomic state validator as complete replacement. + +#### Scenario: Key is distributed before activation + +- **WHEN** a new key is installed without activation +- **THEN** the Peer can verify/decrypt artifacts naming that key but continues minting with the prior active key + +#### Scenario: Installed key becomes active + +- **WHEN** an accepted key is activated +- **THEN** subsequently started mint operations name and use that key while previously accepted keys remain available for decode + +#### Scenario: Active key retirement is rejected + +- **WHEN** a caller attempts to retire the active key without atomically activating another installed key +- **THEN** EACL rejects the operation and preserves the current ring + +#### Scenario: Old key retires after overlap + +- **WHEN** an inactive accepted key is retired +- **THEN** subsequently started decode operations cannot obtain it and safe status no longer lists it as accepted + +### Requirement: Key identifiers are stable epochs + +Every key identifier SHALL be canonical, bounded, and unique to one key epoch. Accepted-key and retired-id collections MUST obey explicit resource ceilings. A running controller MUST reject every reintroduction of a retired identifier, even with the same material, because revival could make old artifacts valid again. Public status and error data MUST NOT expose secret fingerprints. + +#### Scenario: Retired id is reintroduced + +- **WHEN** a caller tries to install any material under an identifier retired earlier in the controller lifetime +- **THEN** EACL rejects the update as key-id reuse without logging or returning either key + +#### Scenario: Idempotent distribution repeats while accepted + +- **WHEN** the same currently accepted identifier and equivalent normalized key are installed again +- **THEN** the operation is an idempotent no-op or returns unchanged status rather than advancing to an ambiguous state + +#### Scenario: Ring resource bound is exceeded + +- **WHEN** a replacement exceeds the accepted-key or retired-id ceiling +- **THEN** EACL rejects it before allocating an unbounded controller state + +### Requirement: Encode and decode use one immutable state snapshot + +Each protected-format encode or decode operation SHALL capture one immutable controller state exactly once. Encoding SHALL use only the captured active key. Decoding SHALL select one key directly by the artifact's authenticated key identifier and MUST NOT try every key in the ring. + +#### Scenario: Rotation overlaps an encode + +- **WHEN** activation races with an encode operation +- **THEN** the artifact is produced wholly under either the old or new captured active key and never combines an id from one state with material from another + +#### Scenario: Retirement overlaps a decode + +- **WHEN** a decode captured the old state before retirement linearized +- **THEN** it MAY complete using that state +- **AND** every decode started after retirement returns cannot obtain the retired key + +#### Scenario: Unknown key id is supplied + +- **WHEN** a bounded artifact names an id absent from the captured accepted ring +- **THEN** EACL fails before protected payload interpretation and does not fall back to another key + +### Requirement: Key updates are externally driven and non-secretly observable + +EACL SHALL expose safe keyring status and MAY emit safe rotation events containing only operation category, key identifiers, generation, counts, and success/failure category. EACL MUST NOT implement provider-specific secret fetching or include key material in logs, metrics, exceptions, status, cursor payloads, cache snapshots, or databases. + +#### Scenario: Consumer updates every Peer + +- **WHEN** an application obtains a new secret from its external control plane and invokes the EACL update API on each Peer +- **THEN** EACL applies each local transition without making a database write or remote secret-manager request + +#### Scenario: Status is inspected + +- **WHEN** a caller reads controller status +- **THEN** it can observe generation, active id, accepted ids, retired ids, and safe counts but cannot recover key bytes or derived cryptographic keys + +### Requirement: Rotation follows a documented overlap protocol + +Documentation SHALL define the safe order generate externally, install on all Peers, observe acknowledgement, activate on all Peers, retain overlap, and retire on all Peers. It MUST explain that activation before distribution can make cross-Peer artifacts unreadable. + +#### Scenario: Two-Peer zero-downtime rotation + +- **WHEN** both Peers install the new key, both activate it while retaining the old key, and the old key is retired only after the overlap +- **THEN** each Peer can decode artifacts minted by either Peer throughout the rollout + +#### Scenario: Peer misses distribution + +- **WHEN** one Peer activates a key another Peer has not installed +- **THEN** the stale Peer rejects artifacts naming the unknown key with a typed error rather than trying the old key or silently restarting an operation + +### Requirement: Rotation does not alter authorization semantics + +Adding, activating, or retiring a security key SHALL NOT alter an authorization graph, source lifecycle, schema generation, Relation generation, qualifier generation, query ordering, or permission result. Optional cryptographically protected cache data that becomes unverifiable MUST be recomputed rather than treated as authorization evidence. + +#### Scenario: Active key changes between identical checks + +- **WHEN** the active key changes but the selected snapshot, query, context, time, and dependency proofs remain equal +- **THEN** uncached authorization returns the same semantic result +- **AND** newly emitted protected artifacts use the new id diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/modular-backend-workspace/spec.md b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/modular-backend-workspace/spec.md new file mode 100644 index 00000000..f8454aba --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/specs/modular-backend-workspace/spec.md @@ -0,0 +1,82 @@ +## MODIFIED Requirements + +### Requirement: Shared cryptographic format service +The core module SHALL own versioned, bounded, canonical cryptographic formats for Zed tokens, cursor envelopes, and authorization-affecting cache entries. It SHALL use distinct signing/encryption domains and derived keys, authenticated key identifiers, a live rotation keyring, strict field allowlists, constant-time authentication checks, and exact portable numeric representations. Every encode/decode operation SHALL use one immutable snapshot supplied by the selected primary or optional dedicated Zed-token controller and MUST NOT read key material from a backend database. Cursor authentication is mandatory; cursor confidentiality SHALL remain an independently advertised capability. + +#### Scenario: Portable cursor is created +- **WHEN** Datahike or DataScript emits a cursor +- **THEN** shared authentication protects its scope, stable position, dependency proof, and key identifier +- **AND** the cursor contains no unserializable backend object or secret key material + +#### Scenario: Datomic encrypted cursor is created +- **WHEN** the Datomic adapter advertises confidential cursors +- **THEN** it uses authenticated encryption in addition to mandatory authenticity and names the key used by that envelope + +#### Scenario: Synchronous browser client has no synchronous AEAD +- **WHEN** DataScript runs behind a synchronous ClojureScript API without compatible synchronous encryption +- **THEN** it may emit an authenticated non-confidential cursor using stable external identities/digests +- **AND** MUST NOT advertise cursor confidentiality + +#### Scenario: Cache entry is read from a shared provider +- **WHEN** a provider returns a completed authorization value +- **THEN** shared code authenticates its key identifier, complete key, causal metadata, proof, and value before considering it + +#### Scenario: Decoder receives hostile input +- **WHEN** a token exceeds size/depth limits, contains unknown fields, has an unsupported numeric representation, names no accepted key, or fails authentication +- **THEN** decoding fails with a bounded typed error before protected payload interpretation + +#### Scenario: Artifact is minted during overlap +- **WHEN** old and new keys are accepted and the new key is active +- **THEN** every newly minted artifact names and uses the new key +- **AND** artifacts under the old key remain decodable until that key is retired + +#### Scenario: Portable cache key is retired +- **WHEN** a shared provider returns an otherwise well-formed authenticated cache entry whose key id is no longer accepted +- **THEN** EACL treats the entry as a miss and recomputes from the selected snapshot +- **AND** does not turn optional cache unavailability into a permission grant or request failure + +#### Scenario: Cursor key is retired +- **WHEN** a caller presents a cursor whose key id is no longer accepted +- **THEN** EACL fails the cursor contract loudly and never treats it as a cache miss or first-page request + +### Requirement: Portable cache and query scopes include configuration identity +The core contract SHALL require deterministic fingerprints for adapter implementation, object-id codecs, recursion/traversal limits, Caveat evaluator configuration, and every option capable of changing authorization or ordering. Mutable identity, Caveat, and adapter data SHALL additionally provide snapshot dependency proofs; a function/configuration fingerprint alone is insufficient. A backend unable to provide complete stable fingerprints and proofs MUST disable completed-answer caching and graph-equivalent cursors. + +Cryptographic keyring generation and active key selection SHALL NOT become authorization semantic-key or dependency-proof fields because they do not change denotation. Protected artifacts SHALL instead carry their authenticated key identifier and validate it against the current ring before use. + +#### Scenario: Adapter configuration changes +- **WHEN** two clients share a cache but use different answer-affecting configurations +- **THEN** their semantic keys and cursor scopes cannot validate against each other + +#### Scenario: Adapter reads undeclared mutable state +- **WHEN** a primitive or codec depends on external mutable state absent from the fingerprint/proof +- **THEN** adapter validation rejects the cache/cursor capability claim + +#### Scenario: Only active security key changes +- **WHEN** two otherwise identical authorization checks straddle key activation +- **THEN** their semantic authorization cache identity remains compatible +- **AND** any newly serialized protected artifact uses the key active at its own mint operation + +## ADDED Requirements + +### Requirement: Live keyring remains backend-neutral and non-durable +The backend-neutral module SHALL contain the reusable live keyring controller abstraction, state validation, safe status, primary/dedicated-scope selection, and shared protected-format integration. Adapter modules SHALL consume that API without provider-specific secret-manager clients or duplicated rotation implementations. Resolving, updating, or using the controller MUST NOT persist its keys or make a per-authorization remote secret lookup. + +#### Scenario: Core-only keyring use +- **WHEN** a third-party adapter or core-only consumer constructs and updates a keyring controller +- **THEN** the controller loads without Datomic, Datahike, DataScript, Datalevin, Vault, Kubernetes, or cloud secret-manager dependencies + +#### Scenario: Backend client uses external secret delivery +- **WHEN** an application watcher obtains in-memory key material and updates a shared controller +- **THEN** every attached backend client can use the new generation without a database transaction or adapter-specific keyring implementation + +### Requirement: Security key rotation is operationally documented +Documentation SHALL explain static keys, live controllers, unique key identifiers, external secret distribution, cluster acknowledgement, activation, overlap, retirement, non-expiring cursor consequences, partial-rollout recovery, rollback, and safe observability. Examples MUST avoid embedding production secrets in source and MUST distinguish cache misses from cursor/token errors after retirement. + +#### Scenario: Operator follows the runbook +- **WHEN** an operator rotates keys across multiple live Peers using the documented sequence +- **THEN** the operator can identify which API updates each Peer, which status proves distribution, how long old keys must remain, and which artifacts will fail after retirement + +#### Scenario: Key material source is chosen +- **WHEN** a consumer uses an external secret manager or configuration watcher +- **THEN** documentation shows how to pass obtained in-memory material to EACL without storing it in EACL databases or requiring EACL to contact that provider on authorization requests diff --git a/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/tasks.md b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/tasks.md new file mode 100644 index 00000000..5a6dcd76 --- /dev/null +++ b/openspec/changes/2026-09-04-04-enable-live-security-keyring-rotation/tasks.md @@ -0,0 +1,51 @@ +## 1. Baseline and API contract + +- [ ] 1.1 Inventory every current primary and dedicated Zed-token key option, fallback rule, normalized keyring value, cursor/Zed/cache envelope, derived-key cache, continuation store, cache export/import path, and public documentation location; verify the inventory names the owning source and tests for each path. +- [ ] 1.2 Define the public `SecurityKeyring` controller, safe status shape, typed conflict/error taxonomy, full-state replacement API, and add/activate/retire convenience APIs; verify API review covers CLJ/CLJS portability and secret redaction. +- [ ] 1.3 Specify compatibility between primary static/controller options and dedicated Zed-token static/controller options, including primary fallback and rejection of mixed sources within one scope; verify construction tests enumerate every accepted and rejected combination. + +## 2. Pure keyring state machine + +- [ ] 2.1 Implement a pure validated keyring-state constructor with non-empty ring, active-key presence, bounded key/id collections, canonical key ids, existing key-strength rules, and no secret-bearing errors; verify unit tests kill each omitted validation. +- [ ] 2.2 Implement generation-guarded complete-state replacement over an atomic immutable state and verify sequential tests prove all-or-nothing visibility and exactly-one generation advancement. +- [ ] 2.3 Implement install, activate, and retire as bounded CAS operations over the common replacement primitive; verify racing operations are linearizable and stale updates return the typed conflict. +- [ ] 2.4 Track retired key ids/fingerprints privately and reject every retired-id reintroduction without exposing fingerprints; verify currently accepted same-key installation is idempotent and no retired id can revive old artifacts. +- [ ] 2.5 Add a pure test-only transition oracle and generated operation traces; verify production transitions equal the oracle without importing the oracle into public/runtime source. + +## 3. Runtime integration + +- [ ] 3.1 Let static primary/Zed options construct private controllers, preserve Zed-to-primary fallback, and let explicit controllers be shared by multiple clients; verify updates reach every sharing client while separate controller scopes remain isolated. +- [ ] 3.2 Capture exactly one controller snapshot per protected encode/decode and select keys by direct id lookup; verify instrumentation detects no second mutable-state read and no ring-wide key trial. +- [ ] 3.3 Cache domain-separated derived keys by controller generation, key id, domain, and format version and remove retired/unreachable entries; verify rotation never pairs one state's id with another state's key. +- [ ] 3.4 Add safe keyring status and optional rotation events with closed non-secret fields; verify log, metric, event, exception, and status redaction tests find no raw/encoded key fragments. + +## 4. Protected formats and cursor state + +- [ ] 4.1 Audit and upgrade every cursor, Zed-token, and portable authenticated-cache envelope to carry one authenticated key id selected from its primary or dedicated controller scope; verify format tests reject id tampering, wrong-scope keys, and implicit-key fallback. +- [ ] 4.2 Tag continuation, visited-page, rendered-page, replay, and equivalent cursor-cache entries with the authenticated series key id; verify mismatched entries cannot influence resume. +- [ ] 4.3 Add synchronous ineligibility plus targeted cleanup on retirement for cursor-related and externally authenticated/imported cache records, without flushing locally computed answer caches; verify a deliberately skipped physical cleanup still errors/misses correctly through key presence or trust epoch. +- [ ] 4.4 Make unknown/retired cursor and causal-token keys return the specified typed request errors with no restart/rebase fallback; verify first-page and consistency fallbacks are not invoked. +- [ ] 4.5 Make unavailable portable cache-entry keys produce authenticated cache misses followed by selected-snapshot evaluation; verify cached and uncached answers remain equal and request success is unaffected. +- [ ] 4.6 Update cache snapshot import/export so key ids remain metadata but secret keys never serialize; verify archive inspection contains no key material and retired-key entries are rejected as misses on restore. + +## 5. Concurrency and rollout conformance + +- [ ] 5.1 Add deterministic barriers around encode/decode versus activation/retirement and verify each result linearizes wholly before or after the update. +- [ ] 5.2 Add a two-Peer rollout fixture covering distribute, acknowledgement, activation skew, overlap, and retirement; verify both directions decode throughout a correct rollout and the missing-distribution case fails with the expected key-unavailable error. +- [ ] 5.3 Exercise non-expiring and finite-TTL cursors across activation and retirement; verify age expiry and key retirement retain distinct error categories. +- [ ] 5.4 Run cross-backend shared conformance for controller updates and protected formats, including DataScript CLJS capability boundaries; verify equivalent supported configurations have identical state/error semantics. +- [ ] 5.5 Add killed controls for active-key removal, stale generation acceptance, ring-wide fallback, unauthenticated kid, cursor silent restart, cache fail-open, and secret-bearing diagnostics; verify each control is detected by a focused test. + +## 6. Performance and simplicity gates + +- [ ] 6.1 Benchmark steady-state mint/decode before and after live-ring integration at ring sizes 1, 2, 4, and 16; verify the hot operation performs one atomic read and one direct map lookup with no work proportional to ring size. +- [ ] 6.2 Measure activation and retirement with populated cursor/imported-cache stores; verify synchronous trust detachment is bounded, physical cleanup may be deferred safely, and locally computed authorization caches are not globally flushed. +- [ ] 6.3 Review production source against the simplicity inventory and remove runtime oracle execution, distributed acknowledgements, database reads, network secret fetching, redundant key checks, and global cache flushes introduced only for testing; verify the final request path matches Design D8. + +## 7. Documentation and release + +- [ ] 7.1 Publish a security-key guide explaining entropy, byte handling, unique key ids, static versus live configuration, secret-manager ownership, non-durability, and realistic JVM zeroization limits; verify examples contain placeholders rather than usable secrets. +- [ ] 7.2 Publish the multi-Peer distribute/observe/activate/overlap/retire runbook with rollback and partial-rollout recovery; verify a reviewer can execute a two-Peer drill using only documented public APIs. +- [ ] 7.3 Document that default non-expiring cursors require indefinite old-key retention for lossless resume, and show finite TTL configuration for bounded retirement; verify the warning appears beside every rotation example. +- [ ] 7.4 Update API docs, release notes, backend/module guides, cryptographic assumptions, and error references; verify key addition/activation does not claim to rotate source lifecycle or authorization proofs. +- [ ] 7.5 Run the combined nREPL test battery, CLJS suite, source-closure gate, secure-format/adversarial tests, and OpenSpec strict validation; verify all pass before release. diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/.openspec.yaml b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/.openspec.yaml new file mode 100644 index 00000000..9696e00f --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-03 diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/design.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/design.md new file mode 100644 index 00000000..afc7f9cd --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/design.md @@ -0,0 +1,794 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](review-2026-09-04.md). The original artifact follows unchanged. + +## Context + +See `proposal.md` for motivation. EACL v8 retains the v7 persisted relationship +ABI: one four-component forward tuple on the subject and one four-component +reverse tuple on the resource. The shared codec, backend adapters, ordered +scans, mutation guards, cleanup, integrity, cache proofs, and cursors all assume +that shape. + +The v7 representation is efficient because one endpoint-local seek returns the +opposite eid directly. The former relationship-entity representation used seven +datoms per relationship and still required covering indexes for efficient +forward and reverse traversal. v9 must preserve the two-datom hot path while +adding native validity and SpiceDB-compatible Caveats. + +SpiceDB is the behavioral reference for Caveats and qualifier identity. Its SQL +datastores keep Caveat name, relationship-bound Caveat context, and expiration +on the relationship row, while the resource/relation/subject columns remain the +relationship key. It does not permit two relationships that differ only by +Caveat or expiration. Relationship-bound context overrides request context for +the same key, and checks can return no permission, has permission, or +conditional permission with missing context fields. + +Relevant inspected EACL areas include: + +- `modules/eacl/src/eacl/relationships/storage.cljc` +- `modules/eacl/src/eacl/relationships/endpoint_pair.cljc` +- `modules/eacl/src/eacl/spicedb/parser.cljc` +- each backend schema, database adapter, writer, cleanup, and integrity module +- the cache, cursor, public authorization, and converged storage specifications + +Relevant inspected SpiceDB areas include: + +- `internal/datastore/common/sql.go` +- `internal/datastore/postgres/readwrite.go` +- `internal/datastore/postgres/schema/indexes.go` +- PostgreSQL, CockroachDB, and Spanner Caveat/expiration migrations +- the AuthZed Caveats and expiring-relationships contracts + +## Goals / Non-Goals + +### Goals + +- Preserve exactly two authoritative endpoint datoms for every logical + relationship. +- Preserve one authoritative endpoint attribute and one ordered candidate + stream per traversal direction; account separately for validation reads. +- Freeze an eight-component v9 ABI that can support both native validity and + Caveats without another relationship-storage break. +- Keep the owning endpoint eid plus its first four tuple components as the + logical relationship identity. +- Store arbitrary relationship-bound Caveat context outside the hot endpoint + tuples, without reifying every relationship. +- Match SpiceDB Caveat context precedence, duplicate relationship, mutation, + deletion, and conditional-result behavior. +- Make validity and Caveats sound under permission algebra, caches, cursors, + speculative snapshots, cleanup, integrity, and unknown-writer proofs. +- Deliver validity first and Caveats second without changing tuple arity or + component positions between phases. +- Reject populated old relationship stores rather than retain dual-read or + migration complexity. + +### Non-Goals + +- Support multiple independently revocable grant assertions for the same + subject/relation/resource identity. +- Support more than one Caveat attached to a single relationship. +- Support recurring schedules, time-zone recurrence rules, grant provenance, + approvals, stable public grant IDs, or independent revocation tokens. +- Depend on Datahike's experimental transaction-level valid-time feature. +- Use a scheduler or garbage collector to make activation or expiration + correct. +- Implement an automatic, online, offline, or mixed-format v7-to-v9 + relationship migration. +- Preserve arbitrary past valid-time reconstruction beyond each backend's + configured history retention. +- Add further inline relationship qualifiers after v9; eight components consume + Datomic's tuple arity budget. + +## Decisions + +### 1. Use one fixed eight-component v9 endpoint ABI + +The authoritative values are: + +```clojure +;; Forward value stored on subject-eid +[subject-type + relation-eid + resource-type + resource-eid + caveat-eid + caveat-context-eid + valid-from-ms + valid-until-ms] + +;; Reverse value stored on resource-eid +[resource-type + relation-eid + subject-type + subject-eid + caveat-eid + caveat-context-eid + valid-from-ms + valid-until-ms] +``` + +Proposed persisted attribute names are: + +```clojure +:eacl.v9.relationship/subject-type+relation+resource-type+resource+caveat+caveat-context+valid-from+valid-until +:eacl.v9.relationship/resource-type+relation+subject-type+subject+caveat+caveat-context+valid-from+valid-until +``` + +Datomic Pro, Datahike, and Datalevin declare: + +```clojure +[:db.type/keyword + :db.type/ref + :db.type/keyword + :db.type/ref + :db.type/ref + :db.type/ref + :db.type/long + :db.type/long] +``` + +DataScript stores the same fixed eight-element vector and enforces the same +logical types and cross-runtime integer range. + +Every optional component remains physically present: + +```text +slot 5 caveat-eid nil when uncaveated +slot 6 caveat-context-eid nil when no non-empty bound context exists +slot 7 valid-from-ms nil when unbounded below +slot 8 valid-until-ms nil when unbounded above +``` + +A context ref without a Caveat ref is invalid. A Caveat ref with a nil context +ref means the Caveat is attached with an empty relationship-bound context. + +The owning endpoint eid and first four components identify the relationship. +The first four components alone are only an endpoint-local key: two subjects +can legitimately hold identical forward tuple values. Components five through +eight qualify that identity. Conflict, deduplication, repair, and cache keys +MUST retain the owning endpoint, including wildcard/subject-relation identity +represented by the existing endpoint codec. + +**Alternatives considered** + +- **Bring back relationship entities.** Rejected. Efficient traversal would + still require two covering indexes, while ordinary relationships would pay + scalar-datom, lifecycle, write-amplification, and cache-density costs. +- **Use one Caveat-binding ref.** Rejected in favor of two explicit slots. A + separate Caveat-definition ref plus optional context ref avoids an extra + binding-definition datom, lets an empty-context Caveat allocate no + relationship-specific entity, and mirrors SpiceDB's separate Caveat name and + context columns. +- **Inline Caveat context.** Rejected. The arbitrary payload would be duplicated + in both endpoint values and enlarge the hottest ordered indexes. +- **Use separate permanent and qualified relationship attributes.** Rejected. + Negative checks and every enumeration or graph hop would need two seeks and + an ordered merge. +- **Use a shorter Phase 1 tuple.** Rejected. It would force another persisted + ABI migration when Phase 2 enables Caveats. + +### 2. Put Caveat refs before validity, but keep all qualifiers trailing + +The index-significant order remains: + +```text +endpoint entity and attribute + -> endpoint type + -> relation eid + -> opposite type + -> opposite eid + -> Caveat definition + -> Caveat context + -> valid-from + -> valid-until +``` + +The decisive performance property is that all qualifiers follow the opposite +eid. Adjacency and pagination therefore remain ordered by the opposite eid, and +a normal graph hop has one ordered candidate stream. This is an index-layout +property, not a guarantee of one total read or constant page latency. + +Among trailing qualifiers, Caveat-before-validity has no material effect on +ordinary lookup complexity because v9 permits only one stored relationship per +owner-qualified first-four identity. The chosen order instead provides these benefits: + +- it mirrors SpiceDB's conceptual row order of Caveat, Caveat context, then + expiration; +- it reserves Phase 2 positions before Phase 1 is released; +- it groups the two Caveat fields and the two validity fields; +- it lets the Caveat-free Phase 1 writer emit two explicit nils followed by the + validity interval. + +Physical component order does not dictate evaluation order. The engine reads +all eight components from one tuple value, rejects an inactive relationship +before dereferencing Caveat context, and invokes the Caveat evaluator only for +temporally active caveated edges. + +A direct logical-identity probe seeks the first four components with full-arity +low/high sentinels. A relation scan seeks the first three components and pages +by component four. Every backend must use full eight-slot bounds where its +vector/tuple comparator requires equal arity. Ascending inclusive bounds use +backend-certified low qualifier sentinels; descending inclusive bounds use high +qualifier sentinels. Exclusive pagination skips the entire boundary identity. +Nil is a low sentinel, not a descending high sentinel. Sentinels are query +values, never persisted validity values. Validate negative timestamps and the +representable extremes, not only post-epoch examples. + +**Alternative considered: leading validity.** Rejected. It would destroy +endpoint-local adjacency ranges, and one lexicographic ordering cannot make the +two-sided interval predicate `from <= t < until` a single complete range. + +**Alternative considered: validity before Caveat.** Correct but not preferred. +It has the same hot-path complexity. The selected Caveat-first order better +matches the Phase 2 storage model and SpiceDB qualifier representation. + +### 3. Store Caveat definitions as shared schema entities + +Phase 2 adds schema-level Caveat entities with canonical identity, typed +parameters, source/AST payload, and compatibility profile. Conceptually: + +```clojure +{:db/id caveat-eid + :eacl/id "eacl.caveat:ip_allowlist" + :eacl.caveat/name :ip_allowlist + :eacl.caveat/parameters-payload canonical-parameters + :eacl.caveat/expression-payload canonical-expression + :eacl.caveat/profile :spicedb-v9} +``` + +The exact persisted payload fields may be consolidated, but the authoritative +representation must be deterministic, bounded, and included in schema +generation and schema-content proofs. + +A relation type reference can allow a Caveat using SpiceDB-compatible syntax: + +```zed +caveat ip_allowlist(user_ip ipaddress, cidr string) { + user_ip.in_cidr(cidr) +} + +definition device { + relation viewer: user | user with ip_allowlist +} +``` + +The duplicate uncaveated and caveated subject branches make the Caveat +optional. A caveated branch without an uncaveated equivalent makes it required +for that subject form. + +Each stored relationship has zero or one `caveat-eid`. The selected Caveat must +be allowed by the exact relation/subject-type/subject-relation branch. + +### 4. Store non-empty relationship-bound context in a sparse context entity + +Relationship-specific Caveat context lives in an internal entity referenced by +slot six: + +```clojure +{:db/id context-eid + :eacl.caveat-context/payload canonical-context} +``` + +The payload is one canonical, type-preserving, bounded value using EACL's secure +portable encoding. It represents a map of parameter names to partially bound +values. It is not stored as one attribute per context key. + +The context entity: + +- has no public object ID; +- exists only when the bound context is non-empty; +- is owned by exactly one logical relationship; +- is immutable after creation; +- is not deduplicated across relationships; +- is replaced atomically when `:touch` changes context; +- is retracted when the relationship is deleted or replaced; +- remains qualifier data rather than a graph vertex. + +For a Caveat with no relationship-bound values: + +```clojure +[..., caveat-eid nil valid-from valid-until] +``` + +No context entity is allocated. Request context can supply all required values. + +For an uncaveated relationship: + +```clojure +[..., nil nil valid-from valid-until] +``` + +No Caveat-specific entity or lookup exists. + +On an integrity-certified source, payload retrieval adds one point read when +an active caveated relationship has non-empty bound context. That count excludes +ownership, pair, schema, and content-proof validation; unknown-writer sources +may require additional work. Caveat definitions are expected to come from the +compiled snapshot schema rather than an entity lookup per leaf. + +**Alternative considered: always allocate a binding entity.** Rejected. It +would allocate at least one extra datom even for a Caveat with empty bound +context and add an unnecessary definition indirection. + +**Alternative considered: shared context entities.** Rejected. Sharing would +require reference counting or global reachability collection and would make +safe deletion materially more complex. + +### 5. Match SpiceDB relationship identity and mutation semantics + +Exactly one stored relationship may exist for a logical identity: + +```text +subject type/eid + relation eid + resource type/eid +``` + +Caveat definition, bound context, `valid-from`, and `valid-until` do not make +new identities. Two relationships differing only in these qualifiers cannot +coexist. + +Public operation semantics are: + +- `:create`: conflict when any stored v9 assertion has the same owner and first four + components, including future, expired, uncaveated, or differently caveated + assertions; +- `:touch`: create when absent, otherwise atomically replace Caveat, context, + validity, endpoint pair, and sparse auxiliary data; +- `:delete`: delete by logical identity without requiring Caveat, context, or + validity values. + +Commit-time conflict control must operate on the owner plus first-four key, +not complete tuple equality. Every create, touch, delete, and collector plan +must validate the state it replaces at commit time. A relation-generation CAS +with replanning or a transaction function over the current database is suitable; +serialized submission of stale raw retractions/additions is insufficient. +Unsupported writer topologies reject qualified writes rather than downgrade to +plan-time checks. Advance each affected relation generation once per admitted +transaction; an unchanged idempotent touch need not advance it. + +Out-of-band duplicate qualifier variants are authoritative corruption. An +encountered fault raises `:eacl/invalid-relationship-state` for the operation; +it is never converted to absent/false inside permission algebra. For example, +dropping a corrupt `banned` edge from `viewer - banned` grants access. Bounded +repair may replace/remove all variants, but cannot retract a context whose +exclusive ownership is unproven. Quarantine ambiguous auxiliary data for +explicit integrity repair rather than damaging another relationship. + +This follows SpiceDB and avoids introducing implicit multiple-grant semantics. +If EACL later needs independently revocable grants, those are first-class grant +assertions with independent IDs, not duplicate v9 tuples. + +### 6. Phase 1 implements fixed storage and native validity + +Phase 1 installs and exclusively reads/writes the eight-slot v9 values. Caveat +slots five and six must be nil in admitted Phase 1 relationship writes. A +non-nil Caveat slot encountered from an out-of-band writer raises a typed +unsupported-feature/integrity error until Phase 2 is enabled; it cannot be +dropped from an exclusion operand. + +Validity uses the half-open interval: + +```text +[valid-from, valid-until) +``` + +with activity at `t`: + +```clojure +(and (or (nil? valid-from) (<= valid-from t)) + (or (nil? valid-until) (< t valid-until))) +``` + +Bounds are normalized to exact UTC epoch-millisecond integers. Supplied bounds +must satisfy `valid-from < valid-until` when both are present. The portable +range is `[-9007199254740991, 9007199254740991]`; nil denotes infinity. Reject +precision-losing normalization rather than silently rounding a supplied instant. + +Every top-level authorization view pairs one immutable database basis with one +trusted captured `valid-at-ms`. Client-targeted operations capture a fresh time +even when they reuse a minimize-latency database pin. Explicit EACL snapshots +freeze both basis and valid-time; `eacl/with` and `eacl/with-schema` preserve +that time. + +Validity is checked before Caveat processing and before an edge participates in +union, intersection, exclusion, arrow traversal, recursion, lookup, or count. + +### 7. Phase 2 implements SpiceDB-compatible Caveats + +Phase 2 enables the already-reserved Caveat slots and adds: + +- parsing and validation of top-level Caveat definitions and `with caveat` + relation type references; +- canonical typed Caveat definitions and a versioned SpiceDB compatibility + profile; +- relationship-write validation and sparse bound-context entities; +- request Caveat context on authorization and lookup operations; +- relationship-bound context precedence over request context for duplicate + keys; +- deterministic bounded CEL evaluation; +- has-permission, no-permission, and conditional-permission results; +- missing-context field reporting; +- conditional-result composition through the complete permission algebra; +- complete cache, cursor, explanation, and proof dependencies. + +Evaluation failure is separate from the three successful permissionship +states. Encountered errors, resource limits, and malformed authoritative data +abort the operation; `can?` propagates the typed failure and never interprets a +failed subtracting branch as false. No successful answer or continuation is +published from that failed operation. + +The evaluator may have runtime-specific implementations, but all supported +runtimes must consume the same canonical typed representation and pass the same +SpiceDB-derived conformance corpus. Unsupported Caveat syntax, types, functions, +or values are rejected at schema/write admission rather than approximated. + +The internal engine cannot collapse missing context to false too early. A leaf +may produce: + +```clojure +{:permissionship :conditional-permission + :residual-condition ... + :missing-context #{...}} +``` + +Union, intersection, exclusion, and arrows combine residual conditions so an +unconditional granting witness can eliminate irrelevant conditions, while a +missing value that could still change the answer remains conditional. + +The public Caveat-aware check returns a three-state result. Existing `can?` +remains a convenience Boolean and returns true only for +`:has-permission`; both `:no-permission` and `:conditional-permission` produce +false. + +### 8. Bound context overrides request context + +Phase 2 uses the SpiceDB merge rule: + +```clojure +(effective-context request-context relationship-bound-context) +``` + +where values from the relationship-bound map replace request values with the +same key. + +This prevents a caller from overriding policy values fixed when the relationship +was written. Context is validated against the Caveat parameter types before +evaluation. Unknown keys may be retained in request identity but are not visible +to an expression unless named by its parameter environment. + +Both stored and request context are untrusted inputs and receive explicit limits +for depth, entry count, string/byte size, collection size, and encoded size +before compilation, evaluation, or cache-key construction. Structural validation +happens at admission; Caveat-specific type conversion happens after bound values +replace request values, so a shadowed request value is not incorrectly +type-checked as the effective parameter. The merge is shallow by parameter +name, not a recursive merge of nested map parameters. + +The existing secure-format v1 is only an envelope, not the CEL value model: it +rejects doubles and integers outside the JS safe range. Before enabling Phase 2, +freeze a versioned typed encoding for every supported CEL value (including +exact int/uint and double semantics), lower it to envelope-supported values, +and test CLJ/CLJS byte identity. Do not widen the existing cache/token numeric +contract silently. Pin the SpiceDB reference release/commit and explicitly +list supported syntax, types, coercions, functions, and rejected features. +Until that profile passes differential tests, compatibility is a scoped goal, +not a claim of complete CEL or SpiceDB support. + +### 9. Caveat and temporal data extend cache and cursor proofs + +Relation-version equality remains necessary but is not sufficient. + +Validity can change an answer without a database write. Reusable results +therefore carry a conservative temporal stability interval and are reusable only +when the selected `valid-at` remains inside it. + +Separate lookup scope, dependency certificates, and authenticated result data. +A lookup key is computable before evaluation; it includes request context, +query scope, ABI/evaluator identity, and the existing source scope. Definition +and context content belong in dependency validation. Permissionship, residual +conditions, and missing fields belong in the authenticated value, not an +answer-dependent lookup key. Together these include: + +- the complete canonical request context relevant to the request; +- `caveat-eid` and the Caveat definition schema generation/content proof; +- `caveat-context-eid` and canonical payload/content proof; +- evaluator compatibility profile and implementation fingerprint; +- conditional/result kind, residual condition, and missing fields where stored; +- the ordinary complete relation dependency proof; +- the temporal stability interval. + +Unknown-writer content proofs commit to both complete endpoint values and the +authoritative context payload. Mutating context in place is unsupported; managed +writers replace the immutable context entity and tuple reference. An out-of-band +mutation still changes the content proof. + +Cursors authenticate request context and conditional semantics so context cannot +change between pages without a restart or typed mismatch. They also authenticate +a temporal mode. Explicit snapshot continuations remain pinned to their original +valid-at; wall-clock passage cannot expire that temporal certificate. Live +continuations capture a fresh valid-at and may reuse state only inside its full +certified interval. Crossing either interval bound returns a typed restart +requirement and no resumed page. Token TTL/retention is a separate check. +Certify the retained frontier, skipped candidates, negative evidence, residuals, +and lookahead, not only already-emitted results. A final answer certificate does +not automatically certify reusable intermediate state. + +### 10. Use a sparse expiration index, not time-leading endpoint tuples + +A finite `valid-until` adds one EACL-owned cardinality-many indexed value on +the subject entity, ordered by expiration: + +```clojure +;; Attribute :eacl.v9.relationship/expiration +[valid-until-ms subject-type relation-eid resource-type resource-eid + caveat-eid caveat-context-eid valid-from-ms] +;; Datomic tuple types: [long keyword ref keyword ref ref ref long] +``` + +Together with the datom owner, this eight-slot permutation reconstructs the +exact forward and reverse values without an extra relationship entity or a +ninth tuple slot. AVET (or the adapter equivalent) bounds collection by finite +expiry. The collector validates the current complete pair and ownership inside +the admitted transaction before retracting anything. +Permanent and start-only relationships add no expiration-index datom. + +Authorization never consults the expiration index. Expired endpoint tuples are +absent immediately; collection only reclaims storage. Missing/stale index values +are maintenance faults, not authoritative graph faults. They do not change an +authorization content proof or abort a check. Keep collection/reconciliation +proofs separate from authorization proofs. + +No mandatory global Caveat index is added. Caveat definition in-use validation +can enumerate the small set of relation definitions that allow that Caveat and +perform relation-prefix scans over their v9 forward values. A sparse +Caveat-usage index may be added later only if measured schema-write cost +justifies another caveated-relationship datom. + +### 11. Keep authoritative history where the backend supports it + +The endpoint pair, Caveat definition, and relationship-bound context are +authoritative. They retain normal history semantics where available. The +expiration index is derived and may use no-history/replaceable storage where a +backend supports it. + +Transaction time and valid time remain distinct: + +```text +database basis T + relationship valid-at V +``` + +Physical collection does not change present authorization but may limit +reconstruction from a current basis. Historical reconstruction remains subject +to backend retention. + +### 12. Reject old relationship data; do not dual-read + +v9 never reads the v7 endpoint attributes for authorization. Startup rejects: + +- any v7 forward or reverse relationship datom; +- mixed v7/v9 relationship data; +- an incompatible relationship-storage version stamp. + +Legacy Datomic attribute definitions may remain installed but inert. Operators +must rebuild or reseed relationship data into the v9 shape before running v9. +Old cache snapshots and cursors are rejected through ABI versioning. + +The eight-slot storage version alone cannot distinguish phases. Persist a +semantic capability epoch alongside it. Phase 2 activation fences obsolete +readers and writers: every newly selected basis and every mutation commit +validates the epoch, with speculative parity. Old tokens/cache entries are +rejected. Retained immutable Phase 1 snapshots may be served only as explicitly +pinned historical views, never as a current Phase 2 view. Startup-only checks +are insufficient for clients that remain alive through activation. Activation +must drain or fence old current-serving clients, including clients retaining a +minimize-latency pin. Reading an epoch from that old immutable pin does not +establish the current capability epoch. A deployment unable to establish that +fence must use a coordinated stop/upgrade/activate cutover; mixed-phase current +service is unsupported. + +This is intentionally not a migration design. Rebuild guidance and validation +are part of the release, but no conversion code or fallback path is shipped. + +## Risks / Trade-offs + +- **Eight slots consume Datomic's tuple arity ceiling** → Treat v9 as the final + inline qualifier budget. Future provenance or independent grants use sparse + side entities or a new assertion model, not a ninth slot. +- **Permanent relationships carry four nil qualifier values in each half** → + Benchmark segment density and scan throughput against v7; retain one candidate stream and + two authoritative endpoint datoms as the layout target. A finite expiration + adds a third derived datom; two datoms do not mean equal storage bytes. +- **Bound Caveat context adds a point read for active caveated edges** → Keep + context sparse, immutable, one-datom, and bypass the read when slot six is + nil; cache decoded context under complete snapshot/content identity. +- **Caveat algebra and caching are security-sensitive** → Preserve residual + conditions, include all context/evaluator dependencies, add adversarial + cache tests, and compare a conformance corpus with SpiceDB. +- **Clock skew can change boundary behavior across peers** → Capture one trusted + time per operation, require synchronized production clocks, document skew, + and reject an invalid clock. Do not claim that shrinking each edge interval + is fail-closed: early expiry of an exclusion can grant access. Any future skew + margin must prove the whole permission invariant over the uncertainty window + or return a typed failure. +- **Out-of-band duplicate qualifier variants can exist because database set + uniqueness applies to all eight slots** → Protect EACL attributes where + possible, serialize admitted mutations by owner-qualified identity, detect duplicates + on reads, and abort affected operations rather than selecting a winner. +- **No in-place upgrade path** → Fail startup before serving, provide explicit + export/reseed instructions, and require backups before rebuild. +- **Different CEL runtimes can diverge** → Use one canonical typed IR, a + versioned compatibility profile, shared test vectors, and runtime + differential tests before Phase 2 release. + +## Delivery Plan + +### Phase 1 — v9 storage and native validity + +1. Install the fixed eight-slot schema and bump storage/cache/cursor ABIs. +2. Reject populated v7 or mixed stores. +3. Replace endpoint codecs, scans, mutations, cleanup, integrity, and proofs. +4. Require Caveat slots five and six to be nil. +5. Add relationship validity, trusted valid-time snapshots, temporal filtering, + and temporal stability proofs. +6. Add sparse expiration collection and rebuild/reseed documentation. +7. Benchmark all backends and release only after candidate-stream, total-read, + inactive-scan, and predeclared regression thresholds are demonstrated. + +### Phase 2 — Caveats + +1. Add Caveat grammar, canonical schema entities, relation type validation, and + schema replacement safety. +2. Enable slot five and sparse slot-six context entities in writes and reads. +3. Add immutable context lifecycle and qualifier-aware touch/delete behavior. +4. Add typed CEL compilation/evaluation and SpiceDB-compatible context merge. +5. Add three-state public checks, missing-context reporting, and conditional + lookup results. +6. Compose residual conditions through every permission operator. +7. Complete cache/cursor/proof/explanation scope and adversarial tests. +8. Run cross-runtime and SpiceDB differential conformance before declaring + Caveat compatibility. + +## Rebuild / Release Plan + +1. Publish the v9 storage and API break before release. +2. Require operators to export logical Relationships and application data needed + to recreate them. +3. Create a fresh database with the v9 physical schema. +4. Re-transact schema and Relationships through the v9 API. +5. Verify no v7 relationship datoms exist, forward/reverse parity holds, and + the storage stamp is 9. +6. Run application authorization validation before switching traffic. +7. Do not start a v9 client against the old populated database. +8. Rollback uses the old database/backup and old EACL version; there is no + in-place v9-to-v8 relationship downgrade. + +## Open Questions + +The endpoint ABI and rebuild policy are fixed. Phase 2 activation remains gated +on a concrete compatibility profile, typed codec, evaluator, and differential +corpus; the tuple layout alone does not resolve those deliverables. Release +latency/space budgets must be recorded before qualification, not invented after +observing candidate measurements. + + +## Adversarial qualification requirements + +A single ordered endpoint stream cannot establish whether its opposite half +exists, nor whether another relationship owns a context. Require a validated +source/basis integrity certificate or perform the necessary validation reads. +A content digest detects change; it does not certify valid structure. On +unknown-writer sources, validate authoritative pair/qualifier integrity for the +selected dependency scope before publishing successful results. Report source +qualification and proof costs separately; do not conceal them in setup or count +only calls to `seek-datoms`. Direct membership must inspect enough of the local +identity group to detect a second variant before granting. Repair has an +explicit work bound and fails atomically when it is exceeded. + +Trailing validity preserves ordering but cannot skip an arbitrarily long run +of inactive relationships. A page of 20 can require scanning an entire +million-edge adjacency list. Charge inactive and corrupt candidates, lookahead, +validation reads, payload bytes, and evaluator work to execution limits. If a +page cannot be completed, return the existing typed resource-limit failure; +never return a successful truncated denial, count, or end-of-stream. Measure +permanent, randomly mixed, and adversarially clustered expired/future prefixes +and suffixes in both directions. Total store size and maximum endpoint degree +are separate fixture dimensions. Collection does not solve future-only runs. + +Positive recursion with residual conditions requires a bounded, terminating +representation: revisiting a cycle must not grow equivalent formulas forever. +Freeze residual identity, sharing/simplification, fixed-point termination, and +work limits before enabling recursive Caveats; reject unsupported recursive +forms at schema admission. Differential cases must include repeated predicates, +different bound contexts under the same Caveat name, conditional cycles, and +negation/exclusion. Resource exhaustion remains a typed failure. + + +## Review evidence — 2026-09-04 + +Reviewed against core commit `031144ceda0925d2cd171c0b72332d609bcc4fcc`. +Experiments ran through nREPL, using in-memory Datomic for small semantics and +a dedicated `:dev` peer on port 7791 for the durable fixture. A focused nREPL +assertion suite finished with **4 tests, 21 assertions, 0 failures, 0 errors**. +These are probes +of the proposed model and current primitives; v9 is not implemented. + +### Confirmed counterexamples and constraints + +- **Exclusion failure (high confidence):** a public EACL in-memory check of + `viewer - banned` returned false with both relationships present and true + when the ban was removed. This models the proposal's instruction to drop a + corrupt negative edge; it is not a claim that a v9 implementation was tested. +- **Owner identity (high):** identical forward values were stored on two + different subject entities. They represent different relationships. +- **Physical uniqueness (high):** Datomic accepted three distinct eight-slot + values with one owner/first-four identity and different trailing qualifiers. +- **Stale replacement (high):** applying two `d/with` touch plans, both retracting + the same old value and adding different new values, left two variants. The + second plan needs a current-state guard even though application is serial. +- **Read cost/integrity (high):** retracting the referenced context or opposite + endpoint left the selected forward values unchanged. A forward-only stream + cannot certify peer existence or context ownership. +- **Descending bounds (high):** a nil-tailed descending bound returned only the + permanent variant; a full high-sentinel bound returned all three variants. + This confirms Datomic behavior; each other adapter still needs qualification. +- **Codec gap (high):** `eacl.secure-format/canonicalize` accepted integer 1 and + the string `"9223372036854775807"`, rejected double 1.25 as + `:unsupported-value`, and rejected integer 9007199254740992 as + `:integer-out-of-range`. Accepting a string does not implement CEL int64. +- **Time certificates (high, executable model):** at valid-at 90, a pinned + interval ending at 100 remains valid when wall time reaches 110. A permanent + union answer stayed true at 90 and 100 while its scheduled child changed + false to true; answer and denotation certificates cannot be interchanged. +- **Context precedence (high, executable model):** shallow bound-map replacement + removed a request-only nested `admin` key and shadowed a wrong-typed request + parameter with its valid bound value. SpiceDB differential validation remains + a Phase 2 gate. + +### Durable million-relationship storage probe + +Datomic Peer/transactor 1.0.7705, Java 26.0.2, transactor heap 4 GiB, peer maximum +heap 9 GiB, local dev protocol on port 4334. The isolated fixture stored the +same million identities in four-slot and eight-slot attributes for paired raw +access comparison; it was not opened as a v9 EACL database. Both layouts had +1,000,000 forward and 1,000,000 reverse datoms. The v9 layout additionally had +999,980 expiration-index datoms, whose permutation reconstructed exact tuples. +These counts were independently enumerated after seeding. + +All identities shared one subject and relation. The first 999,980 v9 identities +had exclusive end 0; the last 20 were unbounded. Evaluation time was 10. +Transactions contained 2,000 identities; 500 batches seeded in 58.83 seconds. +Queries used one fixed basis (`999503`), three untimed warmups per operation, +and sequential timed samples. No concurrent workload or cold-peer result was +measured. Results below include raw candidate processing only, without EACL +request admission, full integrity proofs, Caveat evaluation, or cursor signing. + +| Raw operation | Samples | Median | Min–max | Candidates/results | +| --- | ---: | ---: | ---: | --- | +| v7 first stored page | 11 | 0.021083 ms | 0.019958–0.037459 ms | 20 returned | +| v9 first stored page | 11 | 0.024041 ms | 0.022875–0.027916 ms | 20 advanced / 20 returned | +| v9 first effective page | 7 | 111.445917 ms | 109.271958–113.981084 ms | 1,000,000 advanced / 20 returned | +| 1,000 v7 exact raw probes | 11 | 1.817750 ms | 1.697208–2.725416 ms | fixed physical identity | +| 1,000 v9 prefix/group probes | 11 | 4.065000 ms | 3.639708–22.944958 ms | fixed physical identity, up to two candidates | + +The v9 effective scan used one `seek-datoms`, checked the selected entity and +attribute, inspected component eight for activity, and stopped after 20 active +results. The prefix probe used a full nil-tailed lower bound, checked the +owner/attribute/first-four group, and inspected up to two values for duplicates. +The exact v7 probe used `datoms :eavt` with the complete four-slot value. + +**Conclusion:** one seek does not bound work independently of inactive endpoint +degree. Confidence is high in the counts and this counterexample; moderate in +these warm machine-specific timings. Public v9 latency, cold-cache behavior, +segment density, durable space/write amplification, and cross-backend or CEL +conformance remain unmeasured. These numbers are not release acceptance budgets. + +### External contract checks + +The official [Datomic tuple specification](https://docs.datomic.com/schema/schema-reference.html) +confirms the 2–8 scalar component limit and nil-low ordering. The official +[AuthZed Caveat contract](https://authzed.com/docs/spicedb/concepts/caveats) +confirms bound-context precedence, qualifier-independent identity, and string +transport for 64-bit integers. The official +[expiration contract](https://authzed.com/docs/spicedb/concepts/expiring-relationships) +confirms that an expired but uncollected relationship conflicts with CREATE. +These checks do not substitute for the pinned Phase 2 reference corpus. diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/original-artifact-sha256.json b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/original-artifact-sha256.json new file mode 100644 index 00000000..0e578988 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/original-artifact-sha256.json @@ -0,0 +1,11 @@ +{ + "proposal.md": "d11ab371de4e341b2a2925633f72539a987139b146c2a4f669205a087ba95d2a", + "design.md": "c25c48b80a3ad71910f15ab7d2d0f9cbdbe2af8123ac58a0944dfe1d221d0abe", + "tasks.md": "4b23e4b09a10a77f7c947b93333bd3d9293ec33032982c4413bdd760c52cbb05", + "specs/converged-relationship-storage/spec.md": "e4762de7d430165dc7fcb8f95598b64c20145d423ff11494fb8f9f73cae95c01", + "specs/cursor-dependency-validity/spec.md": "8919f7dab45f0ed1e0d2840ddbd878f8d022ea8c12c6a486cb56c079a4802c5b", + "specs/dependency-validated-authorization-cache/spec.md": "eb41c9a960e8eadaa24e380be2ceff34368019977a56cc1232e366878c186ca7", + "specs/public-authorization/spec.md": "2d50a1c04bc6ac1173a3fa98315cf795e1e92d464988df7ef28a87853bd6cbf5", + "specs/relationship-caveats/spec.md": "9fc7aeaea3eeb13bc74a76ef8e2d46489a7d22a91f8018242fa2003d50121037", + "specs/temporal-relationship-validity/spec.md": "bf1490a6c7ad488a58ad75aade108fd537f1e39cae98e4ab270fc810bfca03b2" +} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/SHA256SUMS.json b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/SHA256SUMS.json new file mode 100644 index 00000000..12a9f7b3 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/SHA256SUMS.json @@ -0,0 +1,23 @@ +{ + "benchmark.clj": "c451b6e6e9fcf6b5c73959535163b02be18554b3908242bb7400f6111a8248cd", + "cleanup.edn": "898da02e42019fdef7b9bb00ea1af075b938859b72b80869b913c5e0428e4fbc", + "database.edn": "6c78f1838a47009aab605c7fae8451268d9c6df74c613f8d177baba87539c7c4", + "fixture.edn": "44395a3d9d1b78419d1084fb7d8884b908d09056ea80c6c79e3e6d903dd927ad", + "followup.clj": "608869f58447224717b012dd8e354074b756cd828d1bd4e0a901ab00c334cb46", + "followup.edn": "bdf2bf315aaf14f034108302b603aa72ab1edfaedf208a073eac53ca6a493c06", + "full-count-1000.edn": "aeaa0a324644f0dd3de9b7e5a0232f07a17eba20a3e5dc6a43add45bbe051397", + "full-count-1200.edn": "0cbf28e372756a8f5bd98fbdfdae590a1215e3e0f0f29916d5c550cffa10ad84", + "page-instrumentation.edn": "47f4d2ad42e3c397894b4698981b7dd4e953fef1ea37caf89751246d4474548c", + "page-latencies-raw.edn": "e8324da171f1db379d0963235a59bbbe5efbdae7f6e959aac04caac834c96dc8", + "page-latencies.edn": "08ef8d40399b50302d0ef96d7510c8fbc57aa82fbb773d0738e8e3f6bf45bfe8", + "point-latencies-raw.edn": "397609806e9450f395823b3c8d38e6b9fd8304fb5e20431c619e8ada92172774", + "point-latencies.edn": "b08ac39fdac55912cfca50658e5b8e97be04912675b87a832364fadf0d5ddedb", + "report.clj": "11d5894c81e926c54790f746a18de6555e3220f16fbed72733e06aaee15afeb8", + "report.md": "a5769008d3ff3fdfbedf2ef4b48e4e56f2f0887ce51443d4d8dbc443ab5000bb", + "rescheduling-dev.edn": "e50af5df9f4af91498e3d3dc4e3df0bba9dabefb1f724193936769578016df77", + "rescheduling-mem.edn": "6e15caea934d1899fb635e00185eff260782f06c64f7ec2a8a922718622b8257", + "rescheduling.clj": "f97abaf6d6c0b93c4d14a40feb080855557e11aff46743a7510253e6657a76a8", + "shared-cache-warmup.edn": "10fdc961505f7b70a3df26d1adea3dfd3f310cfef96d8d614ac5c077c40b343d", + "sparse-build.edn": "4e4c1ff11142e27e2df49c41cee2048ded2a32919ee5e26a0432b1bb2a40e85c", + "verification.edn": "0736c0aa845fe06ea173e7972b2b928448e406925e858d1872482b4bbefcc238" +} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/benchmark.clj b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/benchmark.clj new file mode 100644 index 00000000..bd7cce61 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/benchmark.clj @@ -0,0 +1,209 @@ +(ns v9-txmeta-benchmark + (:require [datomic.api :as d] + [clojure.pprint :as pp]) + (:import [java.util HashMap Arrays])) + +(def out-dir "target/v9-txmeta-benchmark-2026-09-04/") +(def tuple4 [:db.type/keyword :db.type/ref :db.type/keyword :db.type/ref]) +(def tuple8 (into tuple4 [:db.type/ref :db.type/ref :db.type/long :db.type/long])) +(def schema + (into [{:db/ident :bench/resource :db/valueType :db.type/boolean :db/cardinality :db.cardinality/one} + {:db/ident :bench/from :db/valueType :db.type/long :db/cardinality :db.cardinality/one} + {:db/ident :bench/until :db/valueType :db.type/long :db/cardinality :db.cardinality/one}] + (for [[a types] [[:bench/f4 tuple4] [:bench/r4 tuple4] [:bench/f8 tuple8] [:bench/r8 tuple8]]] + {:db/ident a :db/valueType :db.type/tuple :db/tupleTypes types + :db/cardinality :db.cardinality/many :db/index true}))) + +(defn save! [name x] (spit (str out-dir name ".edn") (with-out-str (pp/pprint x))) x) +(defn ms-since [t] (/ (- (System/nanoTime) t) 1e6)) +(defn edge-class [i] (case (mod i 40) 0 :expired 20 :future :permanent)) +(defn bounds [kind] (case kind :expired [nil 900] :future [1100 nil] :permanent [nil nil])) +(defn active? [from until t] + (and (or (nil? from) (<= (long from) (long t))) + (or (nil? until) (< (long t) (long until))))) +(defn inline-active? [dt t] (let [v (:v dt)] (active? (nth v 6) (nth v 7) t))) + +(defonce state (atom nil)) +(defn seed! [n] + (assert (zero? (mod n 2000))) + (let [start (System/nanoTime) + uri (str "datomic:dev://localhost:4334/v9-txmeta-bench-" (random-uuid)) + _ (d/create-database uri) + conn (d/connect uri)] + (reset! state {:uri uri :conn conn}) + (save! "database" {:uri uri :n n :status :seeding}) + @(d/transact conn schema) + @(d/transact conn [{:db/ident :bench/hub} {:db/ident :bench/relation}]) + (doseq [base (range 0 n 2000)] + @(d/transact conn (mapv (fn [i] {:db/id (str "r-" i) :bench/resource true}) (range base (+ base 2000)))) + (when (zero? (mod base 200000)) (println :allocated (+ base 2000)) (flush))) + (let [db (d/db conn) + ids (mapv :e (d/datoms db :aevt :bench/resource)) + s (d/entid db :bench/hub) + rel (d/entid db :bench/relation)] + (assert (= n (count ids))) + (assert (every? (fn [[x y]] (< (long x) (long y))) (partition 2 1 ids))) + (doseq [base (range 0 n 2000)] + (doseq [kind [:permanent :expired :future]] + (let [[from until] (bounds kind) + metadata (cond-> {:db/id "datomic.tx"} from (assoc :bench/from from) until (assoc :bench/until until)) + data (into [metadata] + (mapcat (fn [i] + (let [r (nth ids i) f [:user rel :resource r] rev [:resource rel :user s]] + [[:db/add s :bench/f4 f] [:db/add r :bench/r4 rev] + [:db/add s :bench/f8 (into f [nil nil from until])] + [:db/add r :bench/r8 (into rev [nil nil from until])]]))) + (filter #(= kind (edge-class %)) (range base (+ base 2000))))] + @(d/transact conn data))) + (when (zero? (mod base 100000)) (println :seeded (+ base 2000) :elapsed-ms (ms-since start)) (flush))) + (let [db (d/db conn) + attrs (into {} (for [a [:bench/f4 :bench/r4 :bench/f8 :bench/r8]] [a (d/entid db a)])) + result {:uri uri :n n :basis-t (d/basis-t db) :seed-ms (ms-since start) + :permanent (* n 95/100) :expired (* n 25/1000) :future (* n 25/1000) + :relationship-assertion-transactions (* 3 (/ n 2000)) + :java (System/getProperty "java.version") :clojure (clojure-version)}] + (reset! state {:uri uri :conn conn :db db :ids ids :s s :rel rel :attrs attrs :n n :shared-cache (HashMap.)}) + (save! "fixture" result))))) + +(def variants [:raw-four :inline-eight :filter-inline-eight :filter-tx-naive :filter-tx-request-memo :filter-tx-shared-memo :filter-tx-sparse-bounds]) +(def correct-variants (vec (rest variants))) +(defn build-sparse! [] + (let [start (System/nanoTime) db (:db @state) table (HashMap.) inspected (volatile! 0)] + (doseq [[attr position] [[:bench/from 0] [:bench/until 1]] dt (d/datoms db :aevt attr)] + (vswap! inspected inc) + (.put table (:e dt) (assoc (or (.get table (:e dt)) [nil nil]) position (:v dt)))) + (swap! state assoc :sparse-bounds table) + {:elapsed-ms (ms-since start) :metadata-datoms @inspected :qualified-transactions (.size table)})) +(defn view [variant t] + (let [{:keys [db attrs shared-cache sparse-bounds]} @state + wide? (#{:inline-eight :filter-inline-eight} variant) + attr (attrs (if wide? :bench/f8 :bench/f4)) + relevant (set (map attrs (if wide? [:bench/f8 :bench/r8] [:bench/f4 :bench/r4]))) + cache (case variant :filter-tx-request-memo (HashMap.) :filter-tx-shared-memo shared-cache nil) + pred (case variant + (:raw-four :inline-eight) nil + :filter-inline-eight (fn [_ dt] (or (not (relevant (:a dt))) (inline-active? dt t))) + :filter-tx-sparse-bounds + (fn [_ dt] + (or (not (relevant (:a dt))) + (let [pair (.get ^HashMap sparse-bounds (:tx dt))] + (or (nil? pair) (active? (nth pair 0) (nth pair 1) t))))) + (:filter-tx-naive :filter-tx-request-memo :filter-tx-shared-memo) + (fn [raw dt] + (or (not (relevant (:a dt))) + (let [tx (:tx dt) + pair (if cache + (or (.get ^HashMap cache tx) + (let [e (d/entity raw tx) b [(:bench/from e) (:bench/until e)]] + (.put ^HashMap cache tx b) b)) + (let [e (d/entity raw tx)] [(:bench/from e) (:bench/until e)]))] + (active? (nth pair 0) (nth pair 1) t))))) + filtered (if pred (d/filter db pred) db)] + {:db filtered :attr attr :wide? wide? :manual? (= variant :inline-eight)})) + +(defn page [variant idx t] + (let [{:keys [ids s rel]} @state + {:keys [db attr wide? manual?]} (view variant t) + lower (cond-> [:user rel :resource (nth ids idx)] wide? (into [nil nil nil nil])) + rows (d/seek-datoms db :eavt s attr lower)] + (loop [xs (seq rows) acc (transient []) n 0] + (if (or (nil? xs) (= n 20)) (persistent! acc) + (let [dt (first xs) v (:v dt)] + (if (and (= s (:e dt)) (= attr (:a dt)) (= :user (nth v 0)) (= rel (nth v 1)) (= :resource (nth v 2))) + (if (or (not manual?) (inline-active? dt t)) + (recur (next xs) (conj! acc (nth v 3)) (inc n)) + (recur (next xs) acc n)) + (persistent! acc))))))) + +(defn point [variant idx t] + (let [{:keys [ids s rel]} @state + {:keys [db attr wide? manual?]} (view variant t) + identity [:user rel :resource (nth ids idx)] + dt (first (if wide? (d/seek-datoms db :eavt s attr (into identity [nil nil nil nil])) + (d/datoms db :eavt s attr identity)))] + (boolean (and dt (= s (:e dt)) (= attr (:a dt)) (= identity (subvec (:v dt) 0 4)) + (or (not manual?) (inline-active? dt t)))))) + +(defn full-count [variant t] + (let [{:keys [s]} @state + {:keys [db attr manual?]} (view variant t)] + (reduce (fn [n dt] (if (or (not manual?) (inline-active? dt t)) (inc n) n)) 0 (d/datoms db :eavt s attr)))) + +(defn warm-shared! [] + (let [t0 (System/nanoTime) n (full-count :filter-tx-shared-memo 1000)] + {:count n :elapsed-ms (ms-since t0) :cached-transactions (.size ^HashMap (:shared-cache @state))})) + +(defn percentile [sorted-values p] + (nth sorted-values (min (dec (count sorted-values)) (max 0 (dec (int (Math/ceil (* p (count sorted-values))))))))) +(defn summary-stats [xs] + (let [xs (vec (sort xs))] + {:n (count xs) :median-ms (percentile xs 0.5) :p95-ms (percentile xs 0.95) + :min-ms (first xs) :max-ms (peek xs)})) + +(defn verify! [] + (let [{:keys [ids n db attrs]} @state + counts (into {} (for [a [:bench/f4 :bench/r4 :bench/f8 :bench/r8]] + [a (reduce (fn [c _] (inc c)) 0 (d/datoms db :aevt (attrs a)))])) + _ (assert (every? #(= n %) (vals counts))) + tests (atom 0)] + (doseq [t [1000 1200] idx (concat (range 80) [123400 444440 777760 (- n 80)])] + (let [expected (->> (range idx n) + (filter #(let [[from until] (bounds (edge-class %))] (active? from until t))) + (take 20) (mapv #(nth ids %))) + [from until] (bounds (edge-class idx))] + (doseq [variant correct-variants] + (assert (= expected (page variant idx t)) (str [:page variant idx t])) + (assert (= (active? from until t) (point variant idx t)) (str [:point variant idx t])) + (swap! tests + 2)))) + (save! "verification" {:physical-counts counts :page-and-point-assertions @tests :status :passed}))) + +(defn instrument-page [idx t] + (let [{:keys [db ids s rel attrs]} @state + attr (attrs :bench/f4) calls (atom 0) txs (atom #{}) + filtered (d/filter db (fn [raw dt] + (if (not (= attr (:a dt))) true + (do (swap! calls inc) (swap! txs conj (:tx dt)) + (let [e (d/entity raw (:tx dt))] + (active? (:bench/from e) (:bench/until e) t)))))) + result (mapv (comp #(nth % 3) :v) + (take 20 (d/seek-datoms filtered :eavt s attr [:user rel :resource (nth ids idx)]))) + last-index (java.util.Collections/binarySearch ids (peek result))] + {:idx idx :results (count result) :logical-candidates-through-last-result (inc (- last-index idx)) + :filter-predicate-calls @calls :distinct-assertion-transactions (count @txs)})) + +(defn run-latencies! [workload rounds per-round warmup] + (let [{:keys [n]} @state + op (case workload :page page :point point) + idx-for (fn [i] (mod (* (long i) 9973) (- n 80))) + observations (atom (zipmap variants (repeat []))) + checksum (volatile! 0)] + (doseq [variant variants i (range warmup)] + (op variant (idx-for i) 1000)) + (doseq [round (range rounds)] + (doseq [variant (take (count variants) (drop round (cycle variants)))] + (let [latencies (mapv (fn [j] + (let [idx (idx-for (+ (* round per-round) j)) + start (System/nanoTime) + result (op variant idx 1000) + elapsed (ms-since start)] + (vswap! checksum + (if (vector? result) (count result) (if result 1 0))) + elapsed)) (range per-round))] + (swap! observations update variant into latencies))) + (println :timed workload :round round) (flush)) + (let [result {:workload workload :valid-at 1000 :warmup-per-variant warmup + :rounds rounds :per-round per-round :checksum @checksum + :variants (into {} (map (fn [[variant xs]] [variant (summary-stats xs)]) @observations))}] + (save! (str (name workload) "-latencies-raw") @observations) + (save! (str (name workload) "-latencies") result)))) + +(defn run-full-counts! [rounds t] + (let [results (atom (zipmap variants (repeat []))) n (:n @state)] + (doseq [round (range rounds)] + (doseq [variant (take (count variants) (drop round (cycle variants)))] + (let [start (System/nanoTime) result (full-count variant t) elapsed (ms-since start) + expected (if (= :raw-four variant) n (if (= t 1000) (* n 95/100) (* n 975/1000)))] + (assert (= expected result) (str [variant t result expected])) + (swap! results update variant conj elapsed) + (println :full-count variant :round round :count result :ms elapsed) (flush)))) + (save! (str "full-count-" t) {:valid-at t :rounds rounds :raw @results + :variants (into {} (map (fn [[variant xs]] [variant (summary-stats xs)]) @results))}))) diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/cleanup.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/cleanup.edn new file mode 100644 index 00000000..17b05ead --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/cleanup.edn @@ -0,0 +1,3 @@ +{:database-uri + "datomic:dev://localhost:4334/v9-txmeta-bench-1412b754-2f6e-4941-8460-29bb6f56e984", + :deleted true} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/database.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/database.edn new file mode 100644 index 00000000..35842b14 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/database.edn @@ -0,0 +1,4 @@ +{:uri + "datomic:dev://localhost:4334/v9-txmeta-bench-1412b754-2f6e-4941-8460-29bb6f56e984", + :n 1000000, + :status :seeding} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/fixture.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/fixture.edn new file mode 100644 index 00000000..8a20f96a --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/fixture.edn @@ -0,0 +1,11 @@ +{:future 25000N, + :seed-ms 53219.499833, + :clojure "1.11.4", + :n 1000000, + :basis-t 1003003, + :java "26.0.2", + :permanent 950000N, + :expired 25000N, + :uri + "datomic:dev://localhost:4334/v9-txmeta-bench-1412b754-2f6e-4941-8460-29bb6f56e984", + :relationship-assertion-transactions 1500} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/followup.clj b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/followup.clj new file mode 100644 index 00000000..2b49c2d1 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/followup.clj @@ -0,0 +1,49 @@ +(ns v9-txmeta-followup + (:require [datomic.api :as d] + [v9-txmeta-benchmark :as b])) + +(defn run! [] + (let [{:keys [db ids s rel attrs]} @b/state + fa (attrs :bench/f4) ra (attrs :bench/r4) + f (fn [i] [:user rel :resource (nth ids i)]) + r (fn [_] [:resource rel :user s]) + tx (-> (d/datoms db :eavt s fa (f 20)) first :tx) + visible (fn [raw owner attr value t] + (let [filtered (d/filter raw + (fn [raw dt] + (or (not (#{fa ra} (:a dt))) + (let [e (d/entity raw (:tx dt))] + (b/active? (:bench/from e) (:bench/until e) t)))))] + (boolean (seq (d/datoms filtered :eavt owner attr value))))) + before {:basis-t (d/basis-t db) :assertion-tx tx + :forward-at-1000 (visible db s fa (f 20) 1000) + :forward-at-1200 (visible db s fa (f 20) 1200) + :reverse-at-1000 (visible db (nth ids 20) ra (r 20) 1000) + :reverse-at-1200 (visible db (nth ids 20) ra (r 20) 1200)} + amended (:db-after (d/with db [[:db/add tx :bench/from 900]])) + amended-result {:method :speculative-d-with + :first-edge-visible (visible amended s fa (f 20) 1000) + :other-edge-same-tx-visible (visible amended s fa (f 60) 1000) + :original-db-still-inactive (not (visible db s fa (f 20) 1000))} + old-f (into (f 20) [nil nil 1100 nil]) + old-r (into (r 20) [nil nil 1100 nil]) + new-f (into (f 20) [nil nil 900 nil]) + new-r (into (r 20) [nil nil 900 nil]) + replaced (:db-after (d/with db [[:db/retract s :bench/f8 old-f] + [:db/retract (nth ids 20) :bench/r8 old-r] + [:db/add s :bench/f8 new-f] + [:db/add (nth ids 20) :bench/r8 new-r]])) + inline-result {:method :speculative-d-with + :old-forward-absent (empty? (seq (d/datoms replaced :eavt s :bench/f8 old-f))) + :old-reverse-absent (empty? (seq (d/datoms replaced :eavt (nth ids 20) :bench/r8 old-r))) + :new-forward-present (boolean (seq (d/datoms replaced :eavt s :bench/f8 new-f))) + :new-reverse-present (boolean (seq (d/datoms replaced :eavt (nth ids 20) :bench/r8 new-r)))}] + (assert (false? (:forward-at-1000 before))) + (assert (true? (:forward-at-1200 before))) + (assert (= (:forward-at-1000 before) (:reverse-at-1000 before))) + (assert (= (:forward-at-1200 before) (:reverse-at-1200 before))) + (assert (= tx (-> (d/datoms db :eavt s fa (f 60)) first :tx))) + (assert (every? true? (vals (dissoc amended-result :method)))) + (assert (every? true? (vals (dissoc inline-result :method)))) + (b/save! "followup" {:clock-only-change before :amend-original-tx amended-result + :inline-replacement inline-result :status :passed}))) diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/followup.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/followup.edn new file mode 100644 index 00000000..e7c69fdd --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/followup.edn @@ -0,0 +1,19 @@ +{:clock-only-change + {:basis-t 1003003, + :assertion-tx 13194140534818, + :forward-at-1000 false, + :forward-at-1200 true, + :reverse-at-1000 false, + :reverse-at-1200 true}, + :amend-original-tx + {:method :speculative-d-with, + :first-edge-visible true, + :other-edge-same-tx-visible true, + :original-db-still-inactive true}, + :inline-replacement + {:method :speculative-d-with, + :old-forward-absent true, + :old-reverse-absent true, + :new-forward-present true, + :new-reverse-present true}, + :status :passed} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/full-count-1000.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/full-count-1000.edn new file mode 100644 index 00000000..cef67ad1 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/full-count-1000.edn @@ -0,0 +1,96 @@ +{:valid-at 1000, + :rounds 7, + :raw + {:raw-four + [97.453459 94.4955 94.14975 94.606583 95.838417 97.107833 96.489916], + :inline-eight + [103.553084 + 103.649042 + 102.411584 + 100.807958 + 102.992416 + 103.410542 + 102.700375], + :filter-inline-eight + [130.536375 + 129.470625 + 128.070917 + 128.275792 + 130.666917 + 127.721334 + 130.818], + :filter-tx-naive + [854.694875 + 863.216584 + 860.894208 + 870.434083 + 857.734084 + 866.618583 + 856.53675], + :filter-tx-request-memo + [136.991083 + 135.87575 + 132.518041 + 137.51725 + 136.607833 + 136.383625 + 134.215834], + :filter-tx-shared-memo + [133.642458 + 135.218041 + 130.841208 + 133.632375 + 131.974958 + 133.750375 + 131.753667], + :filter-tx-sparse-bounds + [130.352958 + 129.590542 + 128.601125 + 133.174708 + 130.419417 + 130.187667 + 130.239333]}, + :variants + {:raw-four + {:n 7, + :median-ms 95.838417, + :p95-ms 97.453459, + :min-ms 94.14975, + :max-ms 97.453459}, + :inline-eight + {:n 7, + :median-ms 102.992416, + :p95-ms 103.649042, + :min-ms 100.807958, + :max-ms 103.649042}, + :filter-inline-eight + {:n 7, + :median-ms 129.470625, + :p95-ms 130.818, + :min-ms 127.721334, + :max-ms 130.818}, + :filter-tx-naive + {:n 7, + :median-ms 860.894208, + :p95-ms 870.434083, + :min-ms 854.694875, + :max-ms 870.434083}, + :filter-tx-request-memo + {:n 7, + :median-ms 136.383625, + :p95-ms 137.51725, + :min-ms 132.518041, + :max-ms 137.51725}, + :filter-tx-shared-memo + {:n 7, + :median-ms 133.632375, + :p95-ms 135.218041, + :min-ms 130.841208, + :max-ms 135.218041}, + :filter-tx-sparse-bounds + {:n 7, + :median-ms 130.239333, + :p95-ms 133.174708, + :min-ms 128.601125, + :max-ms 133.174708}}} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/full-count-1200.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/full-count-1200.edn new file mode 100644 index 00000000..56cad6b3 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/full-count-1200.edn @@ -0,0 +1,53 @@ +{:valid-at 1200, + :rounds 1, + :raw + {:raw-four [94.326291], + :inline-eight [103.7245], + :filter-inline-eight [130.894125], + :filter-tx-naive [863.569416], + :filter-tx-request-memo [135.849541], + :filter-tx-shared-memo [134.629417], + :filter-tx-sparse-bounds [128.526792]}, + :variants + {:raw-four + {:n 1, + :median-ms 94.326291, + :p95-ms 94.326291, + :min-ms 94.326291, + :max-ms 94.326291}, + :inline-eight + {:n 1, + :median-ms 103.7245, + :p95-ms 103.7245, + :min-ms 103.7245, + :max-ms 103.7245}, + :filter-inline-eight + {:n 1, + :median-ms 130.894125, + :p95-ms 130.894125, + :min-ms 130.894125, + :max-ms 130.894125}, + :filter-tx-naive + {:n 1, + :median-ms 863.569416, + :p95-ms 863.569416, + :min-ms 863.569416, + :max-ms 863.569416}, + :filter-tx-request-memo + {:n 1, + :median-ms 135.849541, + :p95-ms 135.849541, + :min-ms 135.849541, + :max-ms 135.849541}, + :filter-tx-shared-memo + {:n 1, + :median-ms 134.629417, + :p95-ms 134.629417, + :min-ms 134.629417, + :max-ms 134.629417}, + :filter-tx-sparse-bounds + {:n 1, + :median-ms 128.526792, + :p95-ms 128.526792, + :min-ms 128.526792, + :max-ms 128.526792}}} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-instrumentation.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-instrumentation.edn new file mode 100644 index 00000000..0eea1328 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-instrumentation.edn @@ -0,0 +1,45 @@ +[{:idx 0, + :results 20, + :logical-candidates-through-last-result 22, + :filter-predicate-calls 35, + :distinct-assertion-transactions 3} + {:idx 1, + :results 20, + :logical-candidates-through-last-result 21, + :filter-predicate-calls 34, + :distinct-assertion-transactions 2} + {:idx 19, + :results 20, + :logical-candidates-through-last-result 21, + :filter-predicate-calls 35, + :distinct-assertion-transactions 3} + {:idx 20, + :results 20, + :logical-candidates-through-last-result 22, + :filter-predicate-calls 35, + :distinct-assertion-transactions 3} + {:idx 39, + :results 20, + :logical-candidates-through-last-result 21, + :filter-predicate-calls 35, + :distinct-assertion-transactions 3} + {:idx 40, + :results 20, + :logical-candidates-through-last-result 22, + :filter-predicate-calls 35, + :distinct-assertion-transactions 3} + {:idx 1990, + :results 20, + :logical-candidates-through-last-result 21, + :filter-predicate-calls 35, + :distinct-assertion-transactions 4} + {:idx 2000, + :results 20, + :logical-candidates-through-last-result 22, + :filter-predicate-calls 35, + :distinct-assertion-transactions 3} + {:idx 500000, + :results 20, + :logical-candidates-through-last-result 22, + :filter-predicate-calls 35, + :distinct-assertion-transactions 3}] diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-latencies-raw.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-latencies-raw.edn new file mode 100644 index 00000000..e399e447 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-latencies-raw.edn @@ -0,0 +1,24507 @@ +{:raw-four + [0.013 + 0.00775 + 0.006833 + 0.007125 + 0.006667 + 0.006667 + 0.006708 + 0.006916 + 0.006917 + 0.006709 + 0.0065 + 0.006459 + 0.006791 + 0.006292 + 0.006709 + 0.006834 + 0.006875 + 0.006625 + 0.007084 + 0.006542 + 0.007125 + 0.007 + 0.007 + 0.006416 + 0.00675 + 0.007042 + 0.007042 + 0.006625 + 0.006791 + 0.006667 + 0.00675 + 0.006333 + 0.008042 + 0.0065 + 0.006666 + 0.006666 + 0.007042 + 0.010417 + 0.007083 + 0.006916 + 0.006416 + 0.006584 + 0.006833 + 0.010125 + 0.006917 + 0.007333 + 0.016875 + 0.008334 + 0.007416 + 0.007375 + 0.007375 + 0.007541 + 0.010458 + 0.008 + 0.007167 + 0.006792 + 0.007292 + 0.006375 + 0.006917 + 0.007042 + 0.006666 + 0.006916 + 0.00675 + 0.006916 + 0.007042 + 0.006958 + 0.007167 + 0.007 + 0.007041 + 0.0135 + 0.00775 + 0.006834 + 0.007083 + 0.010209 + 0.007625 + 0.007833 + 0.007542 + 0.014292 + 0.008083 + 0.007167 + 0.00775 + 0.007333 + 0.011459 + 0.007125 + 0.014917 + 0.008292 + 0.007334 + 0.00725 + 0.007916 + 0.006208 + 0.005959 + 0.006083 + 0.006041 + 0.005916 + 0.005666 + 0.00575 + 0.006458 + 0.00575 + 0.005833 + 0.005166 + 0.005 + 0.006709 + 0.00625 + 0.006334 + 0.006583 + 0.006833 + 0.006666 + 0.006583 + 0.0065 + 0.006541 + 0.006417 + 0.006625 + 0.006458 + 0.006209 + 0.006333 + 0.006375 + 0.006583 + 0.006708 + 0.034667 + 0.0335 + 0.0095 + 0.0085 + 0.008042 + 0.007708 + 0.008416 + 0.01 + 0.013666 + 0.011375 + 0.009459 + 0.01 + 0.010166 + 0.010167 + 0.019458 + 0.02925 + 0.015333 + 0.034292 + 0.011917 + 0.011625 + 0.01275 + 0.009917 + 0.009292 + 0.009875 + 0.012708 + 0.011333 + 0.0115 + 0.011 + 0.013208 + 0.014625 + 0.012125 + 0.012458 + 0.010958 + 0.012375 + 0.01 + 0.010375 + 0.010542 + 0.009458 + 0.01125 + 0.009875 + 0.010917 + 0.009875 + 0.009583 + 0.008833 + 0.009792 + 0.010875 + 0.011542 + 0.009625 + 0.009875 + 0.010709 + 0.009791 + 0.010417 + 0.011042 + 0.011333 + 0.012667 + 0.013375 + 0.0105 + 0.010084 + 0.010167 + 0.011333 + 0.011625 + 0.012666 + 0.011917 + 0.0145 + 0.015792 + 0.012791 + 0.012667 + 0.012 + 0.011459 + 0.011792 + 0.0115 + 0.013458 + 0.010667 + 0.010042 + 0.010083 + 0.011625 + 0.011 + 0.011167 + 0.012083 + 0.017375 + 0.008583 + 0.008541 + 0.014792 + 0.012708 + 0.012375 + 0.012916 + 0.011875 + 0.011875 + 0.009459 + 0.010083 + 0.009084 + 0.008625 + 0.008416 + 0.008833 + 0.008625 + 0.008708 + 0.008167 + 0.008208 + 0.008875 + 0.009041 + 0.008292 + 0.008417 + 0.00875 + 0.008125 + 0.00775 + 0.008208 + 0.008083 + 0.010875 + 0.009583 + 0.0085 + 0.0085 + 0.00825 + 0.008 + 0.008291 + 0.007708 + 0.008542 + 0.008875 + 0.008875 + 0.007916 + 0.0095 + 0.010917 + 0.01 + 0.010791 + 0.01 + 0.014334 + 0.013209 + 0.017417 + 0.010625 + 0.009042 + 0.009875 + 0.0095 + 0.009292 + 0.009458 + 0.012334 + 0.011292 + 0.011667 + 0.021459 + 0.012541 + 0.010875 + 0.010333 + 0.011125 + 0.00975 + 0.009583 + 0.00925 + 0.009333 + 0.008167 + 0.00875 + 0.009541 + 0.009667 + 0.010125 + 0.009333 + 0.009375 + 0.009125 + 0.009167 + 0.009166 + 0.008959 + 0.010875 + 0.010083 + 0.009458 + 0.010125 + 0.011583 + 0.010292 + 0.01 + 0.0105 + 0.013459 + 0.009834 + 0.009792 + 0.011375 + 0.009875 + 0.010166 + 0.010208 + 0.010083 + 0.008875 + 0.008542 + 0.008125 + 0.008 + 0.007834 + 0.008083 + 0.008 + 0.009833 + 0.006916 + 0.006541 + 0.006958 + 0.008916 + 0.0085 + 0.008042 + 0.008375 + 0.008167 + 0.008291 + 0.008333 + 0.008834 + 0.007458 + 0.007791 + 0.008333 + 0.008125 + 0.009292 + 0.008666 + 0.009208 + 0.008291 + 0.008417 + 0.008459 + 0.010084 + 0.011958 + 0.010875 + 0.00975 + 0.010083 + 0.013209 + 0.008084 + 0.015584 + 0.008291 + 0.00825 + 0.0085 + 0.016 + 0.012 + 0.008292 + 0.008375 + 0.008542 + 0.008959 + 0.008792 + 0.010625 + 0.009458 + 0.008958 + 0.00875 + 0.008792 + 0.011917 + 0.018792 + 0.01075 + 0.011833 + 0.022292 + 0.015333 + 0.017666 + 0.009958 + 0.009667 + 0.012542 + 0.012208 + 0.010916 + 0.010917 + 0.010125 + 0.007916 + 0.008833 + 0.009125 + 0.008667 + 0.0095 + 0.009333 + 0.009167 + 0.010417 + 0.01025 + 0.008375 + 0.00975 + 0.01425 + 0.010083 + 0.009208 + 0.009334 + 0.009167 + 0.009209 + 0.009209 + 0.011 + 0.010917 + 0.012542 + 0.012291 + 0.01275 + 0.011 + 0.009834 + 0.008708 + 0.008083 + 0.012917 + 0.010416 + 0.009458 + 0.01 + 0.009042 + 0.008958 + 0.011708 + 0.010583 + 0.010542 + 0.009875 + 0.00975 + 0.007917 + 0.00825 + 0.006708 + 0.007125 + 0.009667 + 0.006875 + 0.00725 + 0.009416 + 0.008666 + 0.010875 + 0.011208 + 0.009666 + 0.008958 + 0.009167 + 0.009166 + 0.009958 + 0.010625 + 0.010334 + 0.010875 + 0.008417 + 0.009041 + 0.008584 + 0.008208 + 0.008417 + 0.00875 + 0.009334 + 0.015708 + 0.00975 + 0.008875 + 0.009666 + 0.0135 + 0.008875 + 0.007917 + 0.007958 + 0.008083 + 0.007375 + 0.007333 + 0.007583 + 0.007459 + 0.007667 + 0.008292 + 0.008 + 0.008291 + 0.007208 + 0.00825 + 0.008166 + 0.007792 + 0.008042 + 0.008125 + 0.007833 + 0.008 + 0.007833 + 0.007916 + 0.007916 + 0.008541 + 0.024208 + 0.010791 + 0.009 + 0.008375 + 0.008375 + 0.008542 + 0.008792 + 0.007375 + 0.00825 + 0.013542 + 0.01 + 0.008875 + 0.008 + 0.008 + 0.008292 + 0.008334 + 0.007833 + 0.007792 + 0.00775 + 0.007375 + 0.007458 + 0.007125 + 0.007875 + 0.008083 + 0.008208 + 0.008709 + 0.01025 + 0.01 + 0.010542 + 0.008834 + 0.008625 + 0.009 + 0.01075 + 0.0095 + 0.01025 + 0.00875 + 0.00825 + 0.008584 + 0.008625 + 0.011416 + 0.009542 + 0.007542 + 0.006875 + 0.007375 + 0.007625 + 0.007625 + 0.007875 + 0.009625 + 0.007917 + 0.010916 + 0.011125 + 0.006709 + 0.005 + 0.006542 + 0.006167 + 0.006083 + 0.006375 + 0.006167 + 0.005958 + 0.006083 + 0.00575 + 0.006042 + 0.006166 + 0.006375 + 0.006333 + 0.00625 + 0.006084 + 0.006084 + 0.006 + 0.005667 + 0.006208 + 0.005875 + 0.006 + 0.006333 + 0.006209 + 0.005791 + 0.006292 + 0.005792 + 0.005958 + 0.006042 + 0.005708 + 0.006041 + 0.006375 + 0.005833 + 0.005875 + 0.006083 + 0.006125 + 0.005875 + 0.006125 + 0.006583 + 0.009667 + 0.007 + 0.006666 + 0.006375 + 0.006208 + 0.006667 + 0.006292 + 0.006125 + 0.00625 + 0.007042 + 0.00725 + 0.009792 + 0.007833 + 0.007792 + 0.007167 + 0.00675 + 0.006333 + 0.006708 + 0.006584 + 0.00675 + 0.007209 + 0.008041 + 0.006917 + 0.006541 + 0.006917 + 0.00725 + 0.0095 + 0.007792 + 0.007458 + 0.006708 + 0.007625 + 0.007166 + 0.007167 + 0.008 + 0.00675 + 0.008667 + 0.007959 + 0.0075 + 0.010375 + 0.009167 + 0.008166 + 0.00825 + 0.007833 + 0.007375 + 0.008333 + 0.011625 + 0.009 + 0.007542 + 0.007958 + 0.010083 + 0.007875 + 0.007166 + 0.00675 + 0.006417 + 0.007 + 0.006042 + 0.007167 + 0.005916 + 0.006583 + 0.010667 + 0.006417 + 0.005208 + 0.006667 + 0.008416 + 0.007167 + 0.007208 + 0.006834 + 0.007667 + 0.0065 + 0.006541 + 0.006584 + 0.005875 + 0.006417 + 0.0065 + 0.006375 + 0.006958 + 0.00625 + 0.006417 + 0.006875 + 0.006958 + 0.006708 + 0.006583 + 0.007 + 0.006541 + 0.006959 + 0.00675 + 0.006417 + 0.00675 + 0.00625 + 0.006334 + 0.006625 + 0.006708 + 0.00675 + 0.006542 + 0.006917 + 0.006209 + 0.006875 + 0.007292 + 0.006583 + 0.0075 + 0.006833 + 0.010208 + 0.007167 + 0.007125 + 0.006917 + 0.006708 + 0.007708 + 0.007916 + 0.00725 + 0.008458 + 0.008167 + 0.008667 + 0.012375 + 0.009708 + 0.008667 + 0.007416 + 0.007167 + 0.007791 + 0.006625 + 0.007042 + 0.006291 + 0.006583 + 0.006625 + 0.006667 + 0.0085 + 0.009875 + 0.007167 + 0.006917 + 0.007209 + 0.00625 + 0.007 + 0.006666 + 0.007375 + 0.006708 + 0.00775 + 0.007084 + 0.00775 + 0.008167 + 0.007959 + 0.009541 + 0.008458 + 0.007459 + 0.007542 + 0.006833 + 0.007583 + 0.008083 + 0.01 + 0.007792 + 0.00775 + 0.007208 + 0.007625 + 0.007833 + 0.005792 + 0.00575 + 0.005959 + 0.005375 + 0.005875 + 0.00575 + 0.005667 + 0.006166 + 0.004375 + 0.00475 + 0.00475 + 0.006708 + 0.006667 + 0.006875 + 0.006375 + 0.0065 + 0.006666 + 0.006292 + 0.006667 + 0.006375 + 0.006667 + 0.006166 + 0.006584 + 0.006792 + 0.007083 + 0.007292 + 0.006833 + 0.006542 + 0.005959 + 0.007625 + 0.006625 + 0.007 + 0.006958 + 0.007292 + 0.006792 + 0.006208 + 0.056125 + 0.028333 + 0.020625 + 0.009458 + 0.009542 + 0.009375 + 0.008875 + 0.008 + 0.008416 + 0.009084 + 0.008458 + 0.0085 + 0.016208 + 0.009333 + 0.007917 + 0.012792 + 0.010833 + 0.009583 + 0.009709 + 0.00875 + 0.008666 + 0.017084 + 0.008958 + 0.010792 + 0.01325 + 0.012125 + 0.010959 + 0.01 + 0.009667 + 0.011667 + 0.0115 + 0.008292 + 0.008084 + 0.007584 + 0.010625 + 0.009584 + 0.0095 + 0.008916 + 0.010459 + 0.010333 + 0.008416 + 0.0085 + 0.008208 + 0.008333 + 0.0085 + 0.008417 + 0.01 + 0.008958 + 0.012458 + 0.011167 + 0.0115 + 0.010792 + 0.009416 + 0.009334 + 0.008875 + 0.010292 + 0.008416 + 0.01375 + 0.010917 + 0.010958 + 0.009333 + 0.008958 + 0.009333 + 0.011542 + 0.010875 + 0.007583 + 0.007583 + 0.007958 + 0.00775 + 0.007375 + 0.007333 + 0.008791 + 0.009458 + 0.006792 + 0.007458 + 0.0085 + 0.010375 + 0.01125 + 0.010958 + 0.011 + 0.010583 + 0.008542 + 0.008708 + 0.008208 + 0.008125 + 0.008 + 0.008167 + 0.007959 + 0.007834 + 0.007959 + 0.007625 + 0.007667 + 0.008583 + 0.008166 + 0.008166 + 0.008208 + 0.007958 + 0.007625 + 0.008042 + 0.007917 + 0.00825 + 0.007833 + 0.006875 + 0.0075 + 0.007375 + 0.007792 + 0.00775 + 0.008167 + 0.008333 + 0.007875 + 0.007583 + 0.007833 + 0.007834 + 0.007625 + 0.008333 + 0.00825 + 0.007666 + 0.007958 + 0.007625 + 0.007417 + 0.008042 + 0.007792 + 0.008958 + 0.008291 + 0.01125 + 0.010375 + 0.010334 + 0.00875 + 0.008208 + 0.007708 + 0.007583 + 0.009125 + 0.0085 + 0.007792 + 0.007417 + 0.01 + 0.009541 + 0.009709 + 0.00975 + 0.007792 + 0.008333 + 0.007917 + 0.008083 + 0.00775 + 0.00775 + 0.008583 + 0.008416 + 0.008125 + 0.00825 + 0.00825 + 0.011417 + 0.010333 + 0.010417 + 0.008542 + 0.008833 + 0.00925 + 0.008291 + 0.011208 + 0.010166 + 0.010625 + 0.009125 + 0.008291 + 0.009458 + 0.011208 + 0.00925 + 0.008625 + 0.007875 + 0.006625 + 0.007625 + 0.007125 + 0.007167 + 0.006542 + 0.008959 + 0.008167 + 0.00575 + 0.007209 + 0.007916 + 0.009208 + 0.008709 + 0.008833 + 0.007875 + 0.0095 + 0.010625 + 0.009417 + 0.010209 + 0.009916 + 0.009542 + 0.010083 + 0.011083 + 0.008833 + 0.008625 + 0.008583 + 0.00775 + 0.008083 + 0.009708 + 0.010166 + 0.009 + 0.008709 + 0.008125 + 0.008375 + 0.008417 + 0.0145 + 0.009875 + 0.009833 + 0.008875 + 0.00975 + 0.010333 + 0.010291 + 0.010167 + 0.009792 + 0.009875 + 0.008209 + 0.009083 + 0.008541 + 0.007542 + 0.00875 + 0.009375 + 0.010125 + 0.010292 + 0.010666 + 0.010667 + 0.010334 + 0.009209 + 0.008708 + 0.010292 + 0.010125 + 0.010542 + 0.009709 + 0.008958 + 0.008083 + 0.007583 + 0.008042 + 0.007791 + 0.00725 + 0.008417 + 0.02375 + 0.010084 + 0.008334 + 0.008375 + 0.007625 + 0.007958 + 0.008167 + 0.008333 + 0.00875 + 0.008334 + 0.007834 + 0.008208 + 0.008333 + 0.006542 + 0.009083 + 0.008666 + 0.014625 + 0.010166 + 0.010625 + 0.008334 + 0.008667 + 0.007875 + 0.008792 + 0.010833 + 0.010708 + 0.015875 + 0.0085 + 0.008792 + 0.008625 + 0.010042 + 0.009083 + 0.008833 + 0.00925 + 0.008208 + 0.008292 + 0.007666 + 0.00775 + 0.009667 + 0.010584 + 0.008875 + 0.010958 + 0.009417 + 0.008958 + 0.009458 + 0.008417 + 0.008208 + 0.007417 + 0.009333 + 0.010208 + 0.010083 + 0.010209 + 0.008709 + 0.008834 + 0.0105 + 0.009958 + 0.009917 + 0.008125 + 0.008416 + 0.007584 + 0.008583 + 0.007 + 0.008208 + 0.007875 + 0.007 + 0.007625 + 0.007834 + 0.007625 + 0.007667 + 0.007875 + 0.007333 + 0.007875 + 0.006959 + 0.008375 + 0.007083 + 0.007708 + 0.0075 + 0.007791 + 0.008167 + 0.009333 + 0.010667 + 0.010959 + 0.010125 + 0.010625 + 0.009209 + 0.007792 + 0.008667 + 0.008125 + 0.008791 + 0.008875 + 0.008584 + 0.010958 + 0.011541 + 0.011709 + 0.010333 + 0.010167 + 0.008541 + 0.007959 + 0.008166 + 0.008125 + 0.00825 + 0.010042 + 0.011333 + 0.0095 + 0.015208 + 0.009333 + 0.008375 + 0.007583 + 0.008458 + 0.008084 + 0.009042 + 0.007666 + 0.007834 + 0.008084 + 0.007459 + 0.009041 + 0.008959 + 0.012416 + 0.012458 + 0.011042 + 0.01075 + 0.012625 + 0.010792 + 0.011625 + 0.012792 + 0.011625 + 0.009292 + 0.009584 + 0.008417 + 0.008791 + 0.013209 + 0.008834 + 0.008375 + 0.012959 + 0.006375 + 0.006667 + 0.007041 + 0.006833 + 0.010417 + 0.007 + 0.007209 + 0.006375 + 0.016334 + 0.008584 + 0.007625 + 0.009834 + 0.010041 + 0.00825 + 0.00825 + 0.008209 + 0.007875 + 0.008417 + 0.007917 + 0.009167 + 0.008666 + 0.008667 + 0.008334 + 0.007833 + 0.008208 + 0.007542 + 0.007791 + 0.009625 + 0.0095 + 0.01 + 0.010709 + 0.010667 + 0.010542 + 0.009792 + 0.009834 + 0.009042 + 0.008666 + 0.007667 + 0.0085 + 0.008 + 0.007958 + 0.00825 + 0.008667 + 0.008084 + 0.00825 + 0.008 + 0.008708 + 0.008333 + 0.008292 + 0.007583 + 0.008416 + 0.008917 + 0.009083 + 0.008417 + 0.021292 + 0.009041 + 0.01325 + 0.010875 + 0.012292 + 0.012458 + 0.011666 + 0.009958 + 0.008875 + 0.007833 + 0.010833 + 0.011166 + 0.01125 + 0.011375 + 0.009125 + 0.01 + 0.010625 + 0.011584 + 0.011 + 0.010208 + 0.009917 + 0.009667 + 0.007666 + 0.009125 + 0.011083 + 0.011041 + 0.009 + 0.01075 + 0.012833 + 0.011542 + 0.012041 + 0.011708 + 0.012 + 0.01125 + 0.016875 + 0.011625 + 0.012333 + 0.0115 + 0.012167 + 0.011459 + 0.01125 + 0.012625 + 0.01175 + 0.010958 + 0.009583 + 0.009458 + 0.010125 + 0.009 + 0.008458 + 0.007042 + 0.006167 + 0.006208 + 0.005875 + 0.006375 + 0.008417 + 0.017667 + 0.008708 + 0.008167 + 0.007791 + 0.007667 + 0.008084 + 0.008667 + 0.008416 + 0.009667 + 0.009666 + 0.010458 + 0.010042 + 0.00975 + 0.009708 + 0.0085 + 0.008792 + 0.010834 + 0.010417 + 0.009958 + 0.010042 + 0.010875 + 0.009709 + 0.010375 + 0.009625 + 0.010667 + 0.010541 + 0.009917 + 0.008542 + 0.008666 + 0.008792 + 0.009459 + 0.008417 + 0.009042 + 0.00825 + 0.008333 + 0.008959 + 0.00925 + 0.008417 + 0.008459 + 0.0095 + 0.008042 + 0.008167 + 0.008167 + 0.008166 + 0.008292 + 0.008333 + 0.008916 + 0.011625 + 0.010458 + 0.010458 + 0.008958 + 0.009166 + 0.009875 + 0.008458 + 0.007625 + 0.010834 + 0.009792 + 0.008083 + 0.00875 + 0.00825 + 0.009375 + 0.010084 + 0.010875 + 0.010834 + 0.011125 + 0.010458 + 0.009041 + 0.008 + 0.008375 + 0.008292 + 0.008625 + 0.008541 + 0.010667 + 0.010666 + 0.01075 + 0.008833 + 0.009125 + 0.008958 + 0.011042 + 0.010292 + 0.0105 + 0.00975 + 0.00875 + 0.009167 + 0.010667 + 0.012708 + 0.01225 + 0.011084 + 0.009709 + 0.008667 + 0.008125 + 0.007375 + 0.007833 + 0.007584 + 0.00775 + 0.016167 + 0.010417 + 0.0055 + 0.006209 + 0.008791 + 0.007625 + 0.008375 + 0.007458 + 0.008208 + 0.008 + 0.008333 + 0.007583 + 0.011958 + 0.007792 + 0.007708 + 0.007208 + 0.008 + 0.007583 + 0.007458 + 0.007208 + 0.007625 + 0.007458 + 0.007208 + 0.00725 + 0.00775 + 0.009209 + 0.009459 + 0.009875 + 0.010334 + 0.008542 + 0.01025 + 0.009459 + 0.010625 + 0.010042 + 0.009333 + 0.009625 + 0.010417 + 0.008833 + 0.0285 + 0.036042 + 0.012542 + 0.0115 + 0.013125 + 0.010875 + 0.018167 + 0.010167 + 0.007875 + 0.008125 + 0.00825 + 0.010417 + 0.010125 + 0.0115 + 0.01075 + 0.010625 + 0.009708 + 0.008 + 0.009875 + 0.010041 + 0.009292 + 0.016375 + 0.007416 + 0.008959 + 0.008709 + 0.014959 + 0.00975 + 0.010041 + 0.00975 + 0.009458 + 0.008916 + 0.007 + 0.00775 + 0.007375 + 0.0075 + 0.009041 + 0.010125 + 0.010083 + 0.009833 + 0.009333 + 0.01125 + 0.009125 + 0.010958 + 0.011041 + 0.009875 + 0.011459 + 0.010834 + 0.010833 + 0.011125 + 0.009917 + 0.009791 + 0.00925 + 0.011583 + 0.011833 + 0.010209 + 0.01 + 0.007958 + 0.008125 + 0.00825 + 0.008292 + 0.0085 + 0.008125 + 0.007667 + 0.009041 + 0.006334 + 0.006084 + 0.007959 + 0.008292 + 0.008208 + 0.008541 + 0.008292 + 0.008167 + 0.007916 + 0.008667 + 0.008209 + 0.008209 + 0.008583 + 0.008542 + 0.008292 + 0.008708 + 0.009708 + 0.0095 + 0.00825 + 0.008125 + 0.00825 + 0.007875 + 0.009583 + 0.009333 + 0.019416 + 0.017833 + 0.010875 + 0.00975 + 0.008125 + 0.007417 + 0.0075 + 0.0075 + 0.007875 + 0.007833 + 0.0075 + 0.007417 + 0.007083 + 0.007583 + 0.007833 + 0.007917 + 0.008667 + 0.008041 + 0.009542 + 0.008125 + 0.007542 + 0.007791 + 0.007708 + 0.014292 + 0.0105 + 0.012167 + 0.011291 + 0.009625 + 0.008041 + 0.007917 + 0.012042 + 0.011167 + 0.01075 + 0.010709 + 0.009125 + 0.009542 + 0.007625 + 0.0075 + 0.007125 + 0.007334 + 0.0075 + 0.007584 + 0.00775 + 0.007667 + 0.007208 + 0.01 + 0.010375 + 0.010042 + 0.009875 + 0.009833 + 0.008167 + 0.007542 + 0.008833 + 0.008833 + 0.008417 + 0.0085 + 0.014166 + 0.010916 + 0.010583 + 0.009875 + 0.009541 + 0.007792 + 0.007834 + 0.010167 + 0.0105 + 0.012125 + 0.0105 + 0.009417 + 0.006792 + 0.006167 + 0.006084 + 0.006375 + 0.006417 + 0.006333 + 0.006291 + 0.009833 + 0.006125 + 0.005833 + 0.006291 + 0.008 + 0.008125 + 0.008209 + 0.008292 + 0.009166 + 0.009792 + 0.010667 + 0.009708 + 0.008875 + 0.008 + 0.008375 + 0.007833 + 0.008083 + 0.00675 + 0.007125 + 0.007208 + 0.007625 + 0.007208 + 0.007167 + 0.00725 + 0.007208 + 0.007583 + 0.007042 + 0.007666 + 0.008083 + 0.00975 + 0.009666 + 0.00925 + 0.009083 + 0.007584 + 0.007625 + 0.007375 + 0.008209 + 0.009167 + 0.009166 + 0.009292 + 0.007833 + 0.008125 + 0.00875 + 0.007708 + 0.008084 + 0.007083 + 0.007208 + 0.007166 + 0.012917 + 0.007625 + 0.011542 + 0.010625 + 0.009917 + 0.009041 + 0.008417 + 0.008333 + 0.010333 + 0.013417 + 0.009459 + 0.007292 + 0.007542 + 0.007542 + 0.008292 + 0.008417 + 0.008459 + 0.010291 + 0.009333 + 0.010166 + 0.009625 + 0.007875 + 0.0075 + 0.009792 + 0.009125 + 0.010458 + 0.008208 + 0.009875 + 0.009333 + 0.011166 + 0.010917 + 0.010291 + 0.010292 + 0.011 + 0.012 + 0.010334 + 0.009334 + 0.008667 + 0.009625 + 0.0095 + 0.009625 + 0.010916 + 0.008833 + 0.008208 + 0.007542 + 0.006542 + 0.006042 + 0.005833 + 0.006542 + 0.006375 + 0.006708 + 0.006625 + 0.00575 + 0.00625 + 0.007166 + 0.00625 + 0.006833 + 0.007375 + 0.007375 + 0.007667 + 0.007041 + 0.006875 + 0.007 + 0.007833 + 0.007125 + 0.006708 + 0.006708 + 0.007 + 0.007208 + 0.006583 + 0.006958 + 0.007083 + 0.007083 + 0.007209 + 0.007292 + 0.006958 + 0.006958 + 0.00725 + 0.007042 + 0.007 + 0.007 + 0.006666 + 0.00675 + 0.006875 + 0.007375 + 0.006875 + 0.007125 + 0.007166 + 0.007125 + 0.007 + 0.006709 + 0.00725 + 0.007292 + 0.007 + 0.007375 + 0.007084 + 0.00725 + 0.007333 + 0.0075 + 0.007125 + 0.008417 + 0.008666 + 0.008958 + 0.008708 + 0.007542 + 0.00775 + 0.007792 + 0.007625 + 0.009834 + 0.0095 + 0.008959 + 0.00775 + 0.007041 + 0.007125 + 0.007666 + 0.006875 + 0.007458 + 0.007 + 0.007125 + 0.0075 + 0.007167 + 0.007292 + 0.007541 + 0.015 + 0.006792 + 0.009458 + 0.008833 + 0.008875 + 0.008375 + 0.007958 + 0.007875 + 0.007708 + 0.007958 + 0.010083 + 0.009625 + 0.009333 + 0.009125 + 0.008834 + 0.01075 + 0.010167 + 0.010583 + 0.009125 + 0.010584 + 0.010084 + 0.0105 + 0.008875 + 0.0075 + 0.007042 + 0.006958 + 0.006709 + 0.00825 + 0.008 + 0.009333 + 0.008 + 0.006583 + 0.006666 + 0.0075 + 0.009208 + 0.00875 + 0.008416 + 0.008084 + 0.008292 + 0.008083 + 0.008167 + 0.007583 + 0.006417 + 0.006625 + 0.0065 + 0.006917 + 0.007166 + 0.0065 + 0.006791 + 0.006333 + 0.006792 + 0.00675 + 0.006792 + 0.006667 + 0.007208 + 0.006958 + 0.007 + 0.006583 + 0.007167 + 0.006875 + 0.006792 + 0.006708 + 0.007041 + 0.00625 + 0.006584 + 0.006584 + 0.006875 + 0.006667 + 0.006708 + 0.00675 + 0.006917 + 0.007125 + 0.007125 + 0.00675 + 0.012292 + 0.008792 + 0.007916 + 0.008084 + 0.00675 + 0.007125 + 0.00775 + 0.006917 + 0.0075 + 0.007542 + 0.009459 + 0.010042 + 0.009333 + 0.007458 + 0.00725 + 0.007084 + 0.006625 + 0.006792 + 0.0065 + 0.006875 + 0.007041 + 0.006917 + 0.007166 + 0.006834 + 0.007292 + 0.006541 + 0.007083 + 0.009042 + 0.008667 + 0.00825 + 0.0085 + 0.007083 + 0.006541 + 0.007917 + 0.00725 + 0.007542 + 0.007542 + 0.012125 + 0.009667 + 0.009125 + 0.007875 + 0.007416 + 0.008 + 0.007417 + 0.009417 + 0.009125 + 0.00925 + 0.007875 + 0.007167 + 0.006625 + 0.007166 + 0.007292 + 0.005916 + 0.014458 + 0.00625 + 0.006292 + 0.005584 + 0.004625 + 0.005416 + 0.005958 + 0.008125 + 0.007666 + 0.00875 + 0.009 + 0.009084 + 0.007709 + 0.007541 + 0.0085 + 0.007791 + 0.00825 + 0.0095 + 0.020709 + 0.01 + 0.010083 + 0.009583 + 0.009708 + 0.009583 + 0.009333 + 0.0235 + 0.088458 + 0.009292 + 0.008334 + 0.008834 + 0.009292 + 0.009541 + 0.00825 + 0.008125 + 0.027916 + 0.039208 + 0.037583 + 0.009583 + 0.009208 + 0.008083 + 0.008042 + 0.007959 + 0.008459 + 0.012 + 0.012667 + 0.009667 + 0.008625 + 0.008916 + 0.013917 + 0.011708 + 0.008208 + 0.007875 + 0.008167 + 0.012166 + 0.010125 + 0.009209 + 0.009125 + 0.012333 + 0.053417 + 0.01225 + 0.025958 + 0.009584 + 0.008875 + 0.008833 + 0.008875 + 0.009 + 0.009333 + 0.009583 + 0.009375 + 0.01075 + 0.009584 + 0.010125 + 0.00875 + 0.008 + 0.007958 + 0.007833 + 0.008333 + 0.007833 + 0.009375 + 0.008958 + 0.010167 + 0.010541 + 0.008625 + 0.015833 + 0.010875 + 0.012041 + 0.010875 + 0.011583 + 0.013458 + 0.040917 + 0.053416 + 0.013166 + 0.013125 + 0.013625 + 0.032541 + 0.013375 + 0.013167 + 0.010958 + 0.010875 + 0.012541 + 0.012209 + 0.010917 + 0.00925 + 0.019584 + 0.01375 + 0.011125 + 0.007625 + 0.012417 + 0.015125 + 0.012042 + 0.010958 + 0.010625 + 0.009666 + 0.009208 + 0.009083 + 0.009042 + 0.01025 + 0.009083 + 0.010208 + 0.015667 + 0.009666 + 0.009375 + 0.009 + 0.00975 + 0.00975 + 0.009709 + 0.010666 + 0.0105 + 0.01025 + 0.009959 + 0.009334 + 0.008959 + 0.011334 + 0.018667 + 0.009625 + 0.010542 + 0.010708 + 0.011458 + 0.011167 + 0.011166 + 0.010375 + 0.00875 + 0.008666 + 0.008584 + 0.010834 + 0.011125 + 0.009292 + 0.009333 + 0.011292 + 0.009417 + 0.009542 + 0.009 + 0.009084 + 0.011417 + 0.01 + 0.010042 + 0.010041 + 0.011 + 0.010291 + 0.009959 + 0.010542 + 0.011833 + 0.009375 + 0.010167 + 0.009792 + 0.016792 + 0.010166 + 0.016458 + 0.009125 + 0.010208 + 0.011375 + 0.011375 + 0.009584 + 0.010291 + 0.009875 + 0.008917 + 0.009125 + 0.008958 + 0.010042 + 0.013125 + 0.011334 + 0.011 + 0.0125 + 0.010709 + 0.009458 + 0.010916 + 0.011125 + 0.011625 + 0.011833 + 0.013542 + 0.010209 + 0.008791 + 0.009084 + 0.009542 + 0.009333 + 0.011709 + 0.009959 + 0.008875 + 0.008667 + 0.009292 + 0.007542 + 0.007125 + 0.008834 + 0.008583 + 0.009459 + 0.008042 + 0.006291 + 0.008375 + 0.01825 + 0.01025 + 0.009916 + 0.009 + 0.009167 + 0.02725 + 0.009792 + 0.008875 + 0.008375 + 0.008333 + 0.008833 + 0.008708 + 0.008792 + 0.008375 + 0.0085 + 0.009375 + 0.009083 + 0.009 + 0.008875 + 0.0095 + 0.008959 + 0.016167 + 0.008583 + 0.009208 + 0.00775 + 0.008334 + 0.006917 + 0.006834 + 0.006292 + 0.006959 + 0.006334 + 0.00675 + 0.006458 + 0.006666 + 0.006959 + 0.00725 + 0.006917 + 0.007584 + 0.007292 + 0.00725 + 0.006875 + 0.007416 + 0.007292 + 0.006834 + 0.008791 + 0.008792 + 0.010458 + 0.008875 + 0.007541 + 0.008125 + 0.008084 + 0.009625 + 0.009542 + 0.009167 + 0.007958 + 0.007208 + 0.007208 + 0.007291 + 0.007334 + 0.006791 + 0.007334 + 0.006917 + 0.0075 + 0.006959 + 0.007083 + 0.006666 + 0.009958 + 0.008417 + 0.009125 + 0.010042 + 0.007334 + 0.00775 + 0.007875 + 0.007666 + 0.00775 + 0.007875 + 0.016084 + 0.009834 + 0.009 + 0.008083 + 0.007208 + 0.008625 + 0.007583 + 0.009458 + 0.009209 + 0.009667 + 0.008208 + 0.007458 + 0.006459 + 0.007 + 0.006084 + 0.005708 + 0.005584 + 0.006459 + 0.006166 + 0.006667 + 0.006583 + 0.004708 + 0.004917 + 0.005834 + 0.006875 + 0.013667 + 0.007333 + 0.007 + 0.006875 + 0.006917 + 0.006791 + 0.007208 + 0.006917 + 0.020292 + 0.007083 + 0.007417 + 0.00675 + 0.006917 + 0.007292 + 0.0075 + 0.0075 + 0.007208 + 0.008291 + 0.008375 + 0.008833 + 0.009667 + 0.008916 + 0.008875 + 0.009417 + 0.00825 + 0.009333 + 0.008875 + 0.009166 + 0.009125 + 0.009 + 0.008959 + 0.009375 + 0.00975 + 0.009666 + 0.009167 + 0.008709 + 0.007916 + 0.010042 + 0.009542 + 0.009667 + 0.009875 + 0.009875 + 0.009584 + 0.007916 + 0.0075 + 0.007875 + 0.007875 + 0.007917 + 0.0135 + 0.009834 + 0.009667 + 0.008375 + 0.007584 + 0.007291 + 0.007166 + 0.00725 + 0.007375 + 0.007208 + 0.006958 + 0.007417 + 0.007084 + 0.008458 + 0.0085 + 0.008959 + 0.008708 + 0.007166 + 0.006959 + 0.007708 + 0.007208 + 0.007125 + 0.00725 + 0.007208 + 0.007833 + 0.00775 + 0.008208 + 0.010042 + 0.009917 + 0.009917 + 0.013541 + 0.0075 + 0.009083 + 0.00975 + 0.0125 + 0.01125 + 0.010916 + 0.011125 + 0.009334 + 0.009917 + 0.006958 + 0.0075 + 0.00825 + 0.007417 + 0.0075 + 0.0085 + 0.008458 + 0.00775 + 0.007167 + 0.006459 + 0.008125 + 0.011167 + 0.010209 + 0.020041 + 0.012083 + 0.010291 + 0.009292 + 0.009209 + 0.020583 + 0.009 + 0.008958 + 0.008209 + 0.008875 + 0.009375 + 0.027792 + 0.01725 + 0.040625 + 0.039917 + 0.011042 + 0.00975 + 0.011167 + 0.009959 + 0.009459 + 0.009459 + 0.009875 + 0.009292 + 0.0095 + 0.010042 + 0.010292 + 0.009917 + 0.008625 + 0.008459 + 0.007417 + 0.007708 + 0.007375 + 0.007625 + 0.007625 + 0.007542 + 0.012292 + 0.013166 + 0.008625 + 0.008541 + 0.007792 + 0.008459 + 0.013041 + 0.007792 + 0.007583 + 0.01075 + 0.008417 + 0.014 + 0.009042 + 0.008791 + 0.009042 + 0.008166 + 0.008375 + 0.008666 + 0.008625 + 0.010583 + 0.00825 + 0.00775 + 0.007666 + 0.009 + 0.008042 + 0.007666 + 0.008583 + 0.008833 + 0.007834 + 0.007834 + 0.008167 + 0.007833 + 0.008333 + 0.008083 + 0.008542 + 0.007833 + 0.0095 + 0.01025 + 0.00875 + 0.009333 + 0.010292 + 0.009666 + 0.009834 + 0.009791 + 0.012584 + 0.014916 + 0.010208 + 0.011208 + 0.0115 + 0.024375 + 0.024084 + 0.023416 + 0.010792 + 0.009584 + 0.008334 + 0.010541 + 0.00925 + 0.009083 + 0.007542 + 0.01 + 0.021125 + 0.008417 + 0.010292 + 0.012042 + 0.012542 + 0.011625 + 0.011 + 0.009708 + 0.008292 + 0.009 + 0.008917 + 0.007458 + 0.007875 + 0.008208 + 0.008333 + 0.008167 + 0.008333 + 0.0085 + 0.007792 + 0.007584 + 0.00825 + 0.008417 + 0.008667 + 0.007292 + 0.007791 + 0.007583 + 0.007375 + 0.007792 + 0.008083 + 0.008708 + 0.008958 + 0.0075 + 0.007958 + 0.008583 + 0.008125 + 0.00825 + 0.008542 + 0.008208 + 0.008958 + 0.007583 + 0.008333 + 0.0085 + 0.0085 + 0.018416 + 0.011125 + 0.010667 + 0.009167 + 0.009416 + 0.010167 + 0.011 + 0.012958 + 0.009333 + 0.008625 + 0.016959 + 0.013084 + 0.008667 + 0.012125 + 0.008167 + 0.007292 + 0.008583 + 0.008416 + 0.008375 + 0.008083 + 0.007625 + 0.008083 + 0.007625 + 0.008458 + 0.008875 + 0.008583 + 0.008625 + 0.008541 + 0.008083 + 0.007708 + 0.008292 + 0.00825 + 0.009 + 0.011333 + 0.009417 + 0.008625 + 0.009834 + 0.016125 + 0.00925 + 0.0115 + 0.009459 + 0.008125 + 0.009209 + 0.008625 + 0.008458 + 0.008542 + 0.010959 + 0.010291 + 0.009 + 0.008625 + 0.00925 + 0.006875 + 0.00725 + 0.006583 + 0.007583 + 0.017708 + 0.005958 + 0.009666 + 0.008208 + 0.006375 + 0.007541 + 0.007792 + 0.007334 + 0.0085 + 0.0075 + 0.008458 + 0.008 + 0.008 + 0.007917 + 0.007708 + 0.007625 + 0.00775 + 0.007709 + 0.008166 + 0.008 + 0.00825 + 0.007083 + 0.007791 + 0.007542 + 0.007375 + 0.007166 + 0.008125 + 0.008 + 0.016917 + 0.00775 + 0.007458 + 0.007291 + 0.007792 + 0.007708 + 0.008459 + 0.00775 + 0.008167 + 0.007417 + 0.007791 + 0.007667 + 0.008042 + 0.007583 + 0.008958 + 0.008584 + 0.008667 + 0.007542 + 0.008334 + 0.007875 + 0.007583 + 0.008125 + 0.008083 + 0.009167 + 0.012125 + 0.009208 + 0.009042 + 0.008292 + 0.008209 + 0.008709 + 0.01175 + 0.0085 + 0.008042 + 0.008458 + 0.008166 + 0.007917 + 0.007541 + 0.00825 + 0.008667 + 0.00825 + 0.008833 + 0.007875 + 0.008084 + 0.00825 + 0.008167 + 0.008375 + 0.008084 + 0.008042 + 0.007875 + 0.00825 + 0.011208 + 0.01075 + 0.009208 + 0.008708 + 0.008875 + 0.013792 + 0.009416 + 0.008958 + 0.008167 + 0.008166 + 0.008833 + 0.009125 + 0.0115 + 0.009125 + 0.008708 + 0.008292 + 0.007 + 0.00675 + 0.007375 + 0.007125 + 0.007333 + 0.008083 + 0.006916 + 0.008042 + 0.009333 + 0.005958 + 0.005041 + 0.008458 + 0.008333 + 0.007708 + 0.007666 + 0.008042 + 0.007375 + 0.007958 + 0.0075 + 0.008667 + 0.013834 + 0.008125 + 0.007208 + 0.008 + 0.007625 + 0.007917 + 0.00725 + 0.008708 + 0.016542 + 0.016584 + 0.008917 + 0.008958 + 0.00875 + 0.008875 + 0.009709 + 0.009042 + 0.008708 + 0.008042 + 0.009 + 0.009375 + 0.01 + 0.009708 + 0.009916 + 0.009875 + 0.0085 + 0.009875 + 0.0095 + 0.008166 + 0.007375 + 0.007708 + 0.008458 + 0.009625 + 0.010125 + 0.009292 + 0.0085 + 0.009208 + 0.011167 + 0.01075 + 0.011458 + 0.010333 + 0.009666 + 0.009458 + 0.008458 + 0.010208 + 0.009458 + 0.009625 + 0.007875 + 0.007875 + 0.007209 + 0.007916 + 0.008041 + 0.009417 + 0.012375 + 0.007875 + 0.007875 + 0.0075 + 0.007709 + 0.008209 + 0.007542 + 0.007458 + 0.007667 + 0.009916 + 0.008833 + 0.0085 + 0.007958 + 0.008292 + 0.007959 + 0.007708 + 0.007667 + 0.007584 + 0.01 + 0.010208 + 0.009375 + 0.008375 + 0.008542 + 0.00775 + 0.0085 + 0.010167 + 0.009959 + 0.00975 + 0.008541 + 0.006917 + 0.006667 + 0.006458 + 0.00725 + 0.007 + 0.007459 + 0.007167 + 0.009292 + 0.006 + 0.006583 + 0.005583 + 0.008458 + 0.007583 + 0.007375 + 0.007917 + 0.008125 + 0.008166 + 0.008 + 0.00725 + 0.007417 + 0.007833 + 0.007541 + 0.007292 + 0.007958 + 0.007833 + 0.007875 + 0.007208 + 0.008209 + 0.007417 + 0.007541 + 0.007167 + 0.007875 + 0.008042 + 0.007709 + 0.017709 + 0.007625 + 0.0075 + 0.007583 + 0.008167 + 0.007834 + 0.007459 + 0.007459 + 0.008042 + 0.008416 + 0.009084 + 0.008666 + 0.010709 + 0.010959 + 0.00925 + 0.008667 + 0.009625 + 0.0105 + 0.010291 + 0.008417 + 0.008916 + 0.012083 + 0.010583 + 0.011458 + 0.010917 + 0.011542 + 0.011541 + 0.011125 + 0.013209 + 0.012375 + 0.011 + 0.009125 + 0.008667 + 0.00875 + 0.007667 + 0.008041 + 0.009834 + 0.010375 + 0.009583 + 0.00875 + 0.009167 + 0.0085 + 0.009333 + 0.012041 + 0.010917 + 0.010667 + 0.010041 + 0.00775 + 0.008291 + 0.007333 + 0.008583 + 0.008625 + 0.00825 + 0.008542 + 0.010833 + 0.010333 + 0.009916 + 0.009084 + 0.006916 + 0.008792 + 0.008541 + 0.00825 + 0.011125 + 0.010458 + 0.009583 + 0.009041 + 0.007417 + 0.006959 + 0.007416 + 0.007292 + 0.00675 + 0.007 + 0.008042 + 0.007166 + 0.008375 + 0.005708 + 0.005666 + 0.007292 + 0.008041 + 0.008334 + 0.008875 + 0.010083 + 0.010375 + 0.009875 + 0.009458 + 0.0095 + 0.008667 + 0.008959 + 0.009084 + 0.008875 + 0.008166 + 0.009417 + 0.007666 + 0.008542 + 0.008125 + 0.009917 + 0.008375 + 0.009334 + 0.00925 + 0.008834 + 0.008625 + 0.009167 + 0.008583 + 0.0085 + 0.007958 + 0.010042 + 0.009333 + 0.00925 + 0.008417 + 0.008333 + 0.0125 + 0.008625 + 0.008125 + 0.008208 + 0.010792 + 0.010209 + 0.008625 + 0.008833 + 0.008125 + 0.009334 + 0.010209 + 0.011084 + 0.010709 + 0.008625 + 0.012542 + 0.024709 + 0.016583 + 0.009792 + 0.009917 + 0.013416 + 0.011041 + 0.010333 + 0.01025 + 0.012459 + 0.010084 + 0.0105 + 0.009417 + 0.008625 + 0.0085 + 0.008167 + 0.008291 + 0.007708 + 0.00775 + 0.007583 + 0.007583 + 0.007375 + 0.007834 + 0.010167 + 0.009208 + 0.010042 + 0.008833 + 0.008417 + 0.00825 + 0.008416 + 0.008583 + 0.010458 + 0.009833 + 0.009583 + 0.008292 + 0.007708 + 0.008125 + 0.008375 + 0.010709 + 0.009709 + 0.009709 + 0.009042 + 0.007166 + 0.006958 + 0.006458 + 0.006458 + 0.00625 + 0.006583 + 0.006583 + 0.005834 + 0.007084 + 0.006416 + 0.005458 + 0.005459 + 0.007625 + 0.006583 + 0.0075 + 0.00725 + 0.006833 + 0.007041 + 0.007542 + 0.006583 + 0.00675 + 0.007417 + 0.006875 + 0.007042 + 0.006916 + 0.006958 + 0.007416 + 0.006875 + 0.006833 + 0.007 + 0.0065 + 0.0065 + 0.00675 + 0.007 + 0.007083 + 0.0065 + 0.0075 + 0.006584 + 0.006584 + 0.007042 + 0.0075 + 0.006541 + 0.007 + 0.006584 + 0.00675 + 0.006708 + 0.006792 + 0.006791 + 0.007667 + 0.006792 + 0.007208 + 0.007125 + 0.007208 + 0.007167 + 0.006666 + 0.00725 + 0.012333 + 0.00875 + 0.009792 + 0.008291 + 0.008 + 0.007666 + 0.007292 + 0.017167 + 0.009209 + 0.007458 + 0.007208 + 0.0075 + 0.006625 + 0.007209 + 0.006959 + 0.007375 + 0.006917 + 0.006875 + 0.008416 + 0.0085 + 0.00975 + 0.008958 + 0.008583 + 0.007375 + 0.00825 + 0.009959 + 0.009167 + 0.009583 + 0.00875 + 0.008583 + 0.008708 + 0.009625 + 0.0105 + 0.011959 + 0.010459 + 0.010292 + 0.009 + 0.00825 + 0.008542 + 0.008541 + 0.007917 + 0.010583 + 0.0105 + 0.010125 + 0.009708 + 0.008208 + 0.00825 + 0.007042 + 0.007083 + 0.007417 + 0.0065 + 0.0055 + 0.006083 + 0.004959 + 0.005917 + 0.005417 + 0.006917 + 0.006917 + 0.00775 + 0.007959 + 0.00825 + 0.008916 + 0.009708 + 0.009125 + 0.008708 + 0.007667 + 0.007542 + 0.007208 + 0.007917 + 0.007583 + 0.00775 + 0.0085 + 0.007958 + 0.007875 + 0.008291 + 0.008292 + 0.007791 + 0.007375 + 0.009542 + 0.009875 + 0.008667 + 0.008459 + 0.007791 + 0.007625 + 0.00725 + 0.007333 + 0.007292 + 0.007958 + 0.008416 + 0.008583 + 0.007792 + 0.008042 + 0.008542 + 0.008417 + 0.008458 + 0.007792 + 0.007792 + 0.008292 + 0.008 + 0.00825 + 0.010791 + 0.009583 + 0.009167 + 0.009375 + 0.009292 + 0.009583 + 0.0105 + 0.0165 + 0.009542 + 0.010709 + 0.008459 + 0.008875 + 0.009 + 0.02225 + 0.009458 + 0.011166 + 0.009875 + 0.00975 + 0.008792 + 0.008917 + 0.008333 + 0.007833 + 0.015625 + 0.00825 + 0.007958 + 0.008083 + 0.008041 + 0.008375 + 0.008125 + 0.012708 + 0.009458 + 0.009708 + 0.00925 + 0.019291 + 0.028625 + 0.03125 + 0.041208 + 0.018208 + 0.008958 + 0.010042 + 0.008834 + 0.007917 + 0.01275 + 0.009334 + 0.008291 + 0.007875 + 0.006542 + 0.0065 + 0.0095 + 0.008917 + 0.008416 + 0.006667 + 0.008708 + 0.005375 + 0.00625 + 0.005333 + 0.006916 + 0.006875 + 0.007667 + 0.007333 + 0.006625 + 0.007292 + 0.007042 + 0.007125 + 0.007459 + 0.007666 + 0.007459 + 0.007375 + 0.007584 + 0.007458 + 0.007167 + 0.007584 + 0.009333 + 0.007333 + 0.007625 + 0.007416 + 0.007 + 0.007416 + 0.008083 + 0.007333 + 0.0075 + 0.007958 + 0.006917 + 0.007209 + 0.007 + 0.007209 + 0.006708 + 0.007166 + 0.007292 + 0.007542 + 0.007166 + 0.007625 + 0.006792 + 0.008125 + 0.00775 + 0.008167 + 0.009916 + 0.010292 + 0.009709 + 0.009459 + 0.008125 + 0.008042 + 0.008875 + 0.011334 + 0.011208 + 0.01125 + 0.01075 + 0.009709 + 0.007958 + 0.010917 + 0.010042 + 0.009583 + 0.009792 + 0.009791 + 0.008 + 0.007291 + 0.0075 + 0.007292 + 0.007958 + 0.007625 + 0.007792 + 0.007458 + 0.007375 + 0.007667 + 0.008166 + 0.007667 + 0.008041 + 0.007833 + 0.008208 + 0.011084 + 0.011542 + 0.010708 + 0.010791 + 0.009542 + 0.008542 + 0.010666 + 0.010042 + 0.009666 + 0.011208 + 0.009917 + 0.007958 + 0.008417 + 0.010833 + 0.0105 + 0.010125 + 0.009042 + 0.008459 + 0.007333 + 0.008417 + 0.009084 + 0.008916 + 0.008958 + 0.008208 + 0.007125 + 0.005875 + 0.006083 + 0.007708 + 0.007875 + 0.008084 + 0.007541 + 0.007625 + 0.007292 + 0.007584 + 0.007791 + 0.008 + 0.00775 + 0.007709 + 0.007291 + 0.007542 + 0.0075 + 0.007834 + 0.007959 + 0.007834 + 0.007959 + 0.007417 + 0.007875 + 0.010625 + 0.008042 + 0.007375 + 0.00725 + 0.007916 + 0.0085 + 0.007292 + 0.008 + 0.007667 + 0.007542 + 0.01625 + 0.008084 + 0.007667 + 0.007458 + 0.0075 + 0.008 + 0.015542 + 0.011208 + 0.007708 + 0.007542 + 0.013625 + 0.007958 + 0.008083 + 0.008084 + 0.006916 + 0.00825 + 0.009875 + 0.011833 + 0.018625 + 0.011375 + 0.010958 + 0.037 + 0.032666 + 0.015584 + 0.011666 + 0.009708 + 0.00975 + 0.008083 + 0.008625 + 0.008375 + 0.009167 + 0.008833 + 0.0085 + 0.009083 + 0.008625 + 0.00925 + 0.007625 + 0.007958 + 0.009625 + 0.009833 + 0.009208 + 0.009125 + 0.0085 + 0.013791 + 0.012083 + 0.013292 + 0.012834 + 0.009959 + 0.009292 + 0.012041 + 0.010583 + 0.010292 + 0.009375 + 0.008542 + 0.008209 + 0.008958 + 0.011708 + 0.010958 + 0.009791 + 0.008875 + 0.007416 + 0.007167 + 0.007625 + 0.007875 + 0.007292 + 0.007458 + 0.007083 + 0.0095 + 0.005625 + 0.005917 + 0.00725 + 0.008 + 0.007709 + 0.008833 + 0.008 + 0.007875 + 0.007875 + 0.008084 + 0.007875 + 0.016458 + 0.009167 + 0.009042 + 0.008708 + 0.0085 + 0.008542 + 0.007833 + 0.008167 + 0.008666 + 0.009667 + 0.009917 + 0.010584 + 0.010875 + 0.009875 + 0.007417 + 0.007417 + 0.007458 + 0.0075 + 0.007625 + 0.009417 + 0.01075 + 0.010208 + 0.009791 + 0.010042 + 0.009084 + 0.008792 + 0.008083 + 0.007792 + 0.0085 + 0.008458 + 0.007583 + 0.008375 + 0.007583 + 0.007917 + 0.014584 + 0.00875 + 0.008417 + 0.008 + 0.011208 + 0.010417 + 0.010584 + 0.010041 + 0.00925 + 0.009292 + 0.011333 + 0.01 + 0.00975 + 0.009542 + 0.00775 + 0.008042 + 0.008208 + 0.008541 + 0.00825 + 0.008042 + 0.008208 + 0.007792 + 0.008084 + 0.007917 + 0.00825 + 0.008416 + 0.008125 + 0.008542 + 0.00775 + 0.00825 + 0.007083 + 0.014667 + 0.010459 + 0.0105 + 0.008208 + 0.009042 + 0.008333 + 0.010708 + 0.015042 + 0.010291 + 0.0085 + 0.008833 + 0.0085 + 0.008292 + 0.010167 + 0.022708 + 0.0095 + 0.00775 + 0.007417 + 0.00725 + 0.007834 + 0.007166 + 0.007292 + 0.00675 + 0.009209 + 0.005666 + 0.005917 + 0.008708 + 0.008333 + 0.008292 + 0.007416 + 0.007875 + 0.007292 + 0.008375 + 0.00725 + 0.007709 + 0.00825 + 0.007458 + 0.0075 + 0.008042 + 0.008166 + 0.008584 + 0.007625 + 0.008125 + 0.008583 + 0.00775 + 0.008041 + 0.007917 + 0.008166 + 0.009042 + 0.008666 + 0.008958 + 0.00825 + 0.009125 + 0.008333 + 0.008291 + 0.007417 + 0.007417 + 0.007833 + 0.018 + 0.007917 + 0.007541 + 0.007417 + 0.008125 + 0.009167 + 0.009833 + 0.010083 + 0.010291 + 0.009917 + 0.009917 + 0.009292 + 0.009041 + 0.0105 + 0.009875 + 0.010458 + 0.009584 + 0.00825 + 0.008375 + 0.008333 + 0.011291 + 0.010709 + 0.009917 + 0.00825 + 0.007709 + 0.007917 + 0.007625 + 0.007666 + 0.007458 + 0.009125 + 0.008083 + 0.009833 + 0.009333 + 0.009 + 0.008416 + 0.007333 + 0.007875 + 0.007458 + 0.007708 + 0.007833 + 0.007834 + 0.00725 + 0.008375 + 0.008083 + 0.007833 + 0.010542 + 0.010375 + 0.009917 + 0.008083 + 0.007916 + 0.008 + 0.0085 + 0.013083 + 0.010125 + 0.009375 + 0.008792 + 0.008209 + 0.008625 + 0.00675 + 0.006792 + 0.006041 + 0.006458 + 0.006625 + 0.006708 + 0.0065 + 0.007416 + 0.005708 + 0.005167 + 0.005458 + 0.007041 + 0.008167 + 0.0085 + 0.008667 + 0.008334 + 0.007459 + 0.007125 + 0.007542 + 0.006709 + 0.006833 + 0.006917 + 0.00725 + 0.007375 + 0.0075 + 0.007083 + 0.007208 + 0.007625 + 0.007709 + 0.006542 + 0.007458 + 0.007125 + 0.007209 + 0.007125 + 0.007084 + 0.007166 + 0.006709 + 0.006666 + 0.006875 + 0.007208 + 0.006833 + 0.007375 + 0.007167 + 0.007208 + 0.006833 + 0.00675 + 0.007 + 0.006958 + 0.0075 + 0.009125 + 0.009542 + 0.008667 + 0.007833 + 0.007208 + 0.007291 + 0.007333 + 0.006917 + 0.007333 + 0.007667 + 0.007333 + 0.009458 + 0.009541 + 0.010375 + 0.008833 + 0.007833 + 0.007375 + 0.006792 + 0.007458 + 0.007125 + 0.007458 + 0.009333 + 0.009125 + 0.008791 + 0.008042 + 0.007583 + 0.007292 + 0.007708 + 0.007083 + 0.00725 + 0.007 + 0.007833 + 0.007084 + 0.007334 + 0.006916 + 0.008 + 0.008 + 0.01025 + 0.00975 + 0.009542 + 0.008959 + 0.007959 + 0.007875 + 0.006917 + 0.013209 + 0.009959 + 0.009625 + 0.008792 + 0.007833 + 0.007708 + 0.009792 + 0.008167 + 0.0075], + :inline-eight + [0.041084 + 0.011125 + 0.010625 + 0.01075 + 0.009625 + 0.0095 + 0.008625 + 0.009125 + 0.009083 + 0.008083 + 0.008209 + 0.00875 + 0.016041 + 0.008375 + 0.009625 + 0.010417 + 0.010291 + 0.011125 + 0.010917 + 0.010875 + 0.01 + 0.009375 + 0.008417 + 0.008459 + 0.008375 + 0.008375 + 0.008792 + 0.008541 + 0.008417 + 0.008666 + 0.008458 + 0.018 + 0.008334 + 0.008292 + 0.008833 + 0.008625 + 0.008375 + 0.01025 + 0.008792 + 0.008917 + 0.008833 + 0.008208 + 0.009 + 0.008667 + 0.0105 + 0.009 + 0.00875 + 0.008875 + 0.008583 + 0.008833 + 0.008958 + 0.008542 + 0.009958 + 0.008666 + 0.01075 + 0.009208 + 0.008584 + 0.009125 + 0.00875 + 0.014375 + 0.011083 + 0.011916 + 0.015 + 0.009375 + 0.009167 + 0.008875 + 0.009125 + 0.008792 + 0.008709 + 0.010292 + 0.008625 + 0.013333 + 0.014041 + 0.010416 + 0.010125 + 0.009625 + 0.010708 + 0.012709 + 0.01275 + 0.012083 + 0.011875 + 0.010833 + 0.0105 + 0.009708 + 0.009584 + 0.009125 + 0.011209 + 0.010125 + 0.007333 + 0.007875 + 0.008042 + 0.007708 + 0.008083 + 0.008542 + 0.010416 + 0.010166 + 0.011041 + 0.012375 + 0.011459 + 0.007959 + 0.008084 + 0.008833 + 0.009083 + 0.009083 + 0.009167 + 0.011041 + 0.011875 + 0.010625 + 0.010416 + 0.009042 + 0.009167 + 0.010459 + 0.010833 + 0.009375 + 0.009083 + 0.010166 + 0.010416 + 0.010666 + 0.01025 + 0.008667 + 0.009667 + 0.008875 + 0.008459 + 0.0095 + 0.009375 + 0.00775 + 0.00825 + 0.008042 + 0.009084 + 0.008958 + 0.008291 + 0.010125 + 0.009542 + 0.008292 + 0.007917 + 0.0075 + 0.008667 + 0.008625 + 0.009167 + 0.008958 + 0.009541 + 0.010875 + 0.011333 + 0.008625 + 0.009916 + 0.010125 + 0.0095 + 0.010417 + 0.00875 + 0.008875 + 0.008417 + 0.008167 + 0.00875 + 0.008583 + 0.009375 + 0.00875 + 0.008583 + 0.008084 + 0.008583 + 0.008625 + 0.008958 + 0.009 + 0.009041 + 0.011792 + 0.011166 + 0.012667 + 0.010875 + 0.010208 + 0.011208 + 0.010625 + 0.011917 + 0.010708 + 0.008958 + 0.009458 + 0.012542 + 0.012208 + 0.011958 + 0.012167 + 0.009375 + 0.00925 + 0.009709 + 0.012458 + 0.012416 + 0.012083 + 0.012334 + 0.010208 + 0.011334 + 0.010167 + 0.00925 + 0.00825 + 0.008125 + 0.00825 + 0.0085 + 0.009 + 0.0095 + 0.0085 + 0.008959 + 0.009958 + 0.008416 + 0.008791 + 0.007834 + 0.009 + 0.009 + 0.00925 + 0.009709 + 0.010375 + 0.009917 + 0.010542 + 0.009959 + 0.009458 + 0.008166 + 0.008291 + 0.007833 + 0.007625 + 0.007625 + 0.00775 + 0.007667 + 0.008375 + 0.008167 + 0.008584 + 0.008709 + 0.008083 + 0.007875 + 0.008334 + 0.007791 + 0.008708 + 0.0085 + 0.008292 + 0.00775 + 0.007625 + 0.008333 + 0.008792 + 0.007875 + 0.008291 + 0.012958 + 0.008375 + 0.007917 + 0.008625 + 0.008667 + 0.008083 + 0.008708 + 0.00825 + 0.008542 + 0.008792 + 0.010417 + 0.013083 + 0.012208 + 0.009084 + 0.011042 + 0.009584 + 0.008875 + 0.009 + 0.009667 + 0.008542 + 0.008875 + 0.008458 + 0.008166 + 0.010625 + 0.023084 + 0.019042 + 0.017125 + 0.009042 + 0.016292 + 0.008917 + 0.010084 + 0.013417 + 0.011709 + 0.010916 + 0.010916 + 0.012666 + 0.008833 + 0.011334 + 0.00925 + 0.018417 + 0.012166 + 0.012083 + 0.01175 + 0.011625 + 0.011625 + 0.009542 + 0.009625 + 0.014334 + 0.011125 + 0.010667 + 0.008708 + 0.008708 + 0.008667 + 0.006875 + 0.011916 + 0.009 + 0.009208 + 0.009167 + 0.010167 + 0.008125 + 0.008667 + 0.008167 + 0.01 + 0.0105 + 0.007875 + 0.008792 + 0.009 + 0.008958 + 0.009125 + 0.008625 + 0.008209 + 0.007667 + 0.008083 + 0.007583 + 0.007708 + 0.007541 + 0.007833 + 0.00725 + 0.008417 + 0.008292 + 0.007584 + 0.007667 + 0.007542 + 0.007542 + 0.01825 + 0.008584 + 0.008833 + 0.008125 + 0.007708 + 0.007916 + 0.00775 + 0.007833 + 0.007542 + 0.008334 + 0.008542 + 0.010375 + 0.010417 + 0.009875 + 0.008083 + 0.008458 + 0.008334 + 0.007792 + 0.007208 + 0.007708 + 0.008375 + 0.007916 + 0.00825 + 0.007625 + 0.007875 + 0.007958 + 0.011958 + 0.009834 + 0.009375 + 0.008209 + 0.008 + 0.0085 + 0.007833 + 0.008166 + 0.008125 + 0.007666 + 0.007542 + 0.007917 + 0.007917 + 0.008167 + 0.007667 + 0.007792 + 0.007875 + 0.007166 + 0.007917 + 0.008084 + 0.007625 + 0.010125 + 0.010083 + 0.009708 + 0.0115 + 0.008333 + 0.008167 + 0.00775 + 0.0085 + 0.008708 + 0.008708 + 0.008375 + 0.011167 + 0.010959 + 0.010292 + 0.007917 + 0.008875 + 0.008375 + 0.008791 + 0.010667 + 0.01125 + 0.010542 + 0.008833 + 0.018458 + 0.007417 + 0.007208 + 0.006584 + 0.007125 + 0.007125 + 0.011166 + 0.006875 + 0.007 + 0.007709 + 0.008958 + 0.00875 + 0.0075 + 0.006209 + 0.00825 + 0.007958 + 0.007584 + 0.009292 + 0.01 + 0.009584 + 0.009875 + 0.009583 + 0.007791 + 0.007083 + 0.006791 + 0.007708 + 0.007083 + 0.007417 + 0.007167 + 0.007292 + 0.007083 + 0.008584 + 0.009042 + 0.007417 + 0.0075 + 0.00725 + 0.007416 + 0.007291 + 0.007416 + 0.00825 + 0.009083 + 0.009083 + 0.007625 + 0.007667 + 0.007459 + 0.007583 + 0.0075 + 0.007541 + 0.008875 + 0.009583 + 0.009542 + 0.008959 + 0.00925 + 0.008417 + 0.010084 + 0.009958 + 0.011833 + 0.010792 + 0.009666 + 0.009958 + 0.0095 + 0.012375 + 0.011917 + 0.010667 + 0.010458 + 0.009084 + 0.008042 + 0.008209 + 0.007792 + 0.007958 + 0.00975 + 0.012334 + 0.010667 + 0.010583 + 0.009792 + 0.010792 + 0.010959 + 0.008583 + 0.010166 + 0.008833 + 0.0105 + 0.07725 + 0.027833 + 0.021791 + 0.009167 + 0.011958 + 0.008875 + 0.009125 + 0.00925 + 0.008542 + 0.012042 + 0.01 + 0.009292 + 0.00825 + 0.01 + 0.009083 + 0.00875 + 0.008417 + 0.011417 + 0.009084 + 0.008458 + 0.0075 + 0.006583 + 0.006583 + 0.007125 + 0.006458 + 0.007291 + 0.007208 + 0.006875 + 0.007 + 0.007958 + 0.008834 + 0.008125 + 0.016583 + 0.00775 + 0.010917 + 0.009417 + 0.009584 + 0.008417 + 0.008542 + 0.00825 + 0.008541 + 0.011958 + 0.008709 + 0.008417 + 0.009208 + 0.008667 + 0.011209 + 0.008292 + 0.010416 + 0.011125 + 0.010583 + 0.011584 + 0.011833 + 0.011375 + 0.011 + 0.011083 + 0.010875 + 0.013417 + 0.010667 + 0.010916 + 0.009042 + 0.008459 + 0.008583 + 0.0085 + 0.010083 + 0.012458 + 0.013375 + 0.010541 + 0.011 + 0.010792 + 0.017333 + 0.011875 + 0.013666 + 0.013625 + 0.011417 + 0.012125 + 0.013584 + 0.015209 + 0.012375 + 0.020625 + 0.015792 + 0.011625 + 0.011208 + 0.011417 + 0.01775 + 0.016083 + 0.011167 + 0.016792 + 0.011417 + 0.01125 + 0.015833 + 0.010833 + 0.054417 + 0.04175 + 0.022583 + 0.01325 + 0.00925 + 0.009292 + 0.015208 + 0.013041 + 0.008584 + 0.008667 + 0.0085 + 0.011334 + 0.016333 + 0.011042 + 0.016167 + 0.009875 + 0.009083 + 0.009208 + 0.011834 + 0.01025 + 0.01 + 0.011 + 0.009417 + 0.009667 + 0.009042 + 0.011958 + 0.010333 + 0.009125 + 0.008375 + 0.008125 + 0.00775 + 0.007584 + 0.007333 + 0.007916 + 0.007916 + 0.007917 + 0.007875 + 0.008083 + 0.009958 + 0.006459 + 0.006875 + 0.006875 + 0.008542 + 0.008125 + 0.007958 + 0.007583 + 0.007792 + 0.008 + 0.008125 + 0.008041 + 0.007916 + 0.007958 + 0.007875 + 0.007709 + 0.007917 + 0.007834 + 0.0075 + 0.007625 + 0.008083 + 0.008542 + 0.008375 + 0.00925 + 0.009917 + 0.010708 + 0.018333 + 0.01025 + 0.01075 + 0.010459 + 0.0205 + 0.010125 + 0.009667 + 0.01175 + 0.009916 + 0.010458 + 0.009959 + 0.010042 + 0.009792 + 0.01 + 0.024958 + 0.010375 + 0.009375 + 0.010208 + 0.01 + 0.011416 + 0.012375 + 0.014958 + 0.011083 + 0.01275 + 0.010291 + 0.00975 + 0.010583 + 0.008292 + 0.009 + 0.009625 + 0.008708 + 0.009458 + 0.00875 + 0.011041 + 0.009208 + 0.008417 + 0.008042 + 0.007875 + 0.009416 + 0.008875 + 0.008584 + 0.008709 + 0.008542 + 0.008333 + 0.007875 + 0.008209 + 0.009084 + 0.008833 + 0.00725 + 0.009542 + 0.013208 + 0.01 + 0.008833 + 0.008958 + 0.009042 + 0.010166 + 0.008791 + 0.013833 + 0.011791 + 0.011333 + 0.010833 + 0.011209 + 0.010709 + 0.013417 + 0.011167 + 0.008792 + 0.007084 + 0.007583 + 0.008042 + 0.007042 + 0.007583 + 0.006917 + 0.007875 + 0.007708 + 0.01025 + 0.0075 + 0.015541 + 0.006542 + 0.008542 + 0.007625 + 0.008583 + 0.007625 + 0.008167 + 0.008042 + 0.008958 + 0.007917 + 0.008042 + 0.007625 + 0.007833 + 0.008083 + 0.00825 + 0.007625 + 0.00825 + 0.007917 + 0.0075 + 0.007917 + 0.0105 + 0.009708 + 0.014875 + 0.008917 + 0.017333 + 0.008292 + 0.008167 + 0.0075 + 0.007833 + 0.008208 + 0.008334 + 0.00825 + 0.008458 + 0.0075 + 0.008 + 0.010125 + 0.008375 + 0.007958 + 0.008333 + 0.0165 + 0.008958 + 0.009042 + 0.008417 + 0.008875 + 0.008125 + 0.007833 + 0.008708 + 0.008542 + 0.009334 + 0.008 + 0.008458 + 0.008 + 0.009 + 0.007625 + 0.008542 + 0.007917 + 0.007834 + 0.010208 + 0.0085 + 0.008209 + 0.012959 + 0.0085 + 0.007416 + 0.00775 + 0.008125 + 0.007833 + 0.008625 + 0.00875 + 0.018542 + 0.009167 + 0.011459 + 0.009208 + 0.016167 + 0.014333 + 0.014667 + 0.009625 + 0.009334 + 0.009667 + 0.012042 + 0.009459 + 0.018458 + 0.012958 + 0.011667 + 0.009708 + 0.009417 + 0.0095 + 0.01175 + 0.012458 + 0.010625 + 0.008791 + 0.008458 + 0.015375 + 0.007875 + 0.008292 + 0.007667 + 0.008583 + 0.007667 + 0.007917 + 0.016709 + 0.011042 + 0.009125 + 0.008 + 0.008 + 0.009375 + 0.008917 + 0.008542 + 0.008458 + 0.009 + 0.007834 + 0.007417 + 0.007917 + 0.007916 + 0.0085 + 0.007709 + 0.008417 + 0.008291 + 0.007792 + 0.007875 + 0.008958 + 0.008458 + 0.00775 + 0.00825 + 0.008625 + 0.011042 + 0.007584 + 0.008583 + 0.008166 + 0.008292 + 0.007875 + 0.008625 + 0.008167 + 0.008375 + 0.007792 + 0.008708 + 0.011541 + 0.008333 + 0.0075 + 0.00775 + 0.007959 + 0.008542 + 0.007792 + 0.008166 + 0.0085 + 0.008209 + 0.008542 + 0.008417 + 0.009 + 0.008583 + 0.00875 + 0.008084 + 0.008459 + 0.008917 + 0.008042 + 0.008333 + 0.008416 + 0.008583 + 0.008167 + 0.008625 + 0.008584 + 0.008542 + 0.008125 + 0.011167 + 0.008125 + 0.0085 + 0.0085 + 0.008709 + 0.008333 + 0.008083 + 0.008 + 0.008083 + 0.008459 + 0.008417 + 0.00875 + 0.008875 + 0.00925 + 0.014167 + 0.009542 + 0.008209 + 0.008292 + 0.008833 + 0.011417 + 0.008292 + 0.016709 + 0.009375 + 0.009167 + 0.008708 + 0.009125 + 0.0085 + 0.024458 + 0.007459 + 0.007208 + 0.00725 + 0.007166 + 0.007083 + 0.007291 + 0.007042 + 0.006708 + 0.00675 + 0.007917 + 0.009291 + 0.007625 + 0.006417 + 0.006125 + 0.008041 + 0.007666 + 0.007625 + 0.007542 + 0.007416 + 0.00775 + 0.007542 + 0.0075 + 0.007833 + 0.008041 + 0.052208 + 0.017292 + 0.008666 + 0.008666 + 0.008125 + 0.008375 + 0.008291 + 0.008625 + 0.008 + 0.008125 + 0.008542 + 0.008292 + 0.008542 + 0.008875 + 0.008208 + 0.008167 + 0.0085 + 0.008208 + 0.008333 + 0.008125 + 0.008209 + 0.008667 + 0.008583 + 0.008625 + 0.008667 + 0.008584 + 0.009834 + 0.009 + 0.009083 + 0.009083 + 0.008625 + 0.008875 + 0.009209 + 0.012792 + 0.00975 + 0.009541 + 0.009542 + 0.009333 + 0.009 + 0.008792 + 0.009167 + 0.010667 + 0.00975 + 0.011292 + 0.01075 + 0.010125 + 0.010416 + 0.009125 + 0.009542 + 0.0095 + 0.010292 + 0.011791 + 0.015541 + 0.013375 + 0.011792 + 0.011042 + 0.01725 + 0.009209 + 0.00925 + 0.011375 + 0.0185 + 0.012792 + 0.012625 + 0.013958 + 0.009959 + 0.009542 + 0.009542 + 0.009542 + 0.01 + 0.01125 + 0.012166 + 0.011291 + 0.020583 + 0.009875 + 0.009166 + 0.009291 + 0.009083 + 0.008 + 0.00725 + 0.007666 + 0.008125 + 0.007833 + 0.008083 + 0.008125 + 0.007833 + 0.013958 + 0.011 + 0.014083 + 0.007541 + 0.007167 + 0.011708 + 0.008666 + 0.008625 + 0.008375 + 0.008166 + 0.008667 + 0.011042 + 0.00825 + 0.00975 + 0.008417 + 0.008125 + 0.018 + 0.0105 + 0.019083 + 0.026375 + 0.010791 + 0.008083 + 0.008625 + 0.008459 + 0.00825 + 0.0085 + 0.008042 + 0.007917 + 0.008 + 0.009125 + 0.01025 + 0.010541 + 0.017042 + 0.009625 + 0.009125 + 0.008417 + 0.008917 + 0.009875 + 0.008625 + 0.00825 + 0.008292 + 0.009833 + 0.008875 + 0.008417 + 0.008625 + 0.008625 + 0.011333 + 0.008667 + 0.014875 + 0.008625 + 0.008375 + 0.0085 + 0.012625 + 0.01025 + 0.011666 + 0.0085 + 0.010333 + 0.00875 + 0.010833 + 0.008583 + 0.008584 + 0.009 + 0.010834 + 0.008375 + 0.008458 + 0.009959 + 0.009708 + 0.011375 + 0.008625 + 0.011584 + 0.008125 + 0.011 + 0.018916 + 0.010875 + 0.02 + 0.01 + 0.012292 + 0.009125 + 0.009125 + 0.009708 + 0.011542 + 0.014584 + 0.011875 + 0.011166 + 0.012333 + 0.00975 + 0.012208 + 0.011959 + 0.011917 + 0.01325 + 0.009125 + 0.00875 + 0.007834 + 0.00775 + 0.0105 + 0.009791 + 0.010208 + 0.009042 + 0.009667 + 0.010583 + 0.010708 + 0.011333 + 0.007291 + 0.007667 + 0.008083 + 0.010708 + 0.0105 + 0.011 + 0.011916 + 0.01075 + 0.010792 + 0.011583 + 0.010291 + 0.010333 + 0.010375 + 0.009542 + 0.009666 + 0.012167 + 0.010875 + 0.00925 + 0.009542 + 0.008667 + 0.010834 + 0.010875 + 0.01175 + 0.045791 + 0.048625 + 0.016334 + 0.010209 + 0.011083 + 0.012625 + 0.011291 + 0.010583 + 0.010458 + 0.009125 + 0.007958 + 0.008125 + 0.007917 + 0.007958 + 0.007917 + 0.007583 + 0.007666 + 0.009084 + 0.008208 + 0.008333 + 0.008208 + 0.007958 + 0.007875 + 0.007625 + 0.00925 + 0.009416 + 0.008584 + 0.013584 + 0.008292 + 0.00825 + 0.008625 + 0.007875 + 0.00875 + 0.008292 + 0.009041 + 0.008167 + 0.007958 + 0.010958 + 0.008584 + 0.008084 + 0.007875 + 0.008834 + 0.009041 + 0.008084 + 0.009834 + 0.008542 + 0.008292 + 0.008375 + 0.008459 + 0.008666 + 0.007917 + 0.009666 + 0.009833 + 0.016292 + 0.016625 + 0.0165 + 0.009584 + 0.008667 + 0.009583 + 0.01 + 0.01725 + 0.008875 + 0.011333 + 0.00925 + 0.008833 + 0.009 + 0.008416 + 0.00775 + 0.0075 + 0.007 + 0.007 + 0.008083 + 0.007375 + 0.006791 + 0.009084 + 0.007166 + 0.00825 + 0.008166 + 0.007959 + 0.006584 + 0.006584 + 0.008583 + 0.007708 + 0.007583 + 0.007625 + 0.007584 + 0.008041 + 0.007708 + 0.007625 + 0.010167 + 0.009125 + 0.010792 + 0.009333 + 0.010125 + 0.009833 + 0.01025 + 0.011875 + 0.009958 + 0.010875 + 0.009583 + 0.009416 + 0.011084 + 0.008917 + 0.010416 + 0.009417 + 0.008125 + 0.008458 + 0.009375 + 0.009458 + 0.009083 + 0.009708 + 0.009042 + 0.009584 + 0.00825 + 0.008417 + 0.009083 + 0.009458 + 0.00975 + 0.009542 + 0.010333 + 0.011 + 0.010292 + 0.011916 + 0.009625 + 0.010375 + 0.009083 + 0.008125 + 0.009208 + 0.020584 + 0.01275 + 0.008167 + 0.007459 + 0.008125 + 0.008042 + 0.0085 + 0.008083 + 0.008292 + 0.007709 + 0.00775 + 0.007667 + 0.007542 + 0.007959 + 0.00875 + 0.008042 + 0.008209 + 0.007667 + 0.016208 + 0.007916 + 0.010625 + 0.00825 + 0.007958 + 0.009708 + 0.010708 + 0.01025 + 0.00875 + 0.009792 + 0.024166 + 0.012042 + 0.011708 + 0.009416 + 0.01125 + 0.011084 + 0.010542 + 0.011416 + 0.008792 + 0.00875 + 0.016 + 0.010375 + 0.007166 + 0.007 + 0.007041 + 0.007417 + 0.013583 + 0.007542 + 0.006958 + 0.007333 + 0.007625 + 0.009334 + 0.006459 + 0.006417 + 0.005667 + 0.009208 + 0.008042 + 0.007875 + 0.007625 + 0.008125 + 0.00825 + 0.008 + 0.007417 + 0.007708 + 0.007625 + 0.007708 + 0.007792 + 0.00775 + 0.008209 + 0.007583 + 0.007458 + 0.008083 + 0.008042 + 0.008084 + 0.00825 + 0.008166 + 0.008 + 0.007458 + 0.007375 + 0.007625 + 0.00775 + 0.007875 + 0.007708 + 0.007458 + 0.007583 + 0.00725 + 0.009292 + 0.007583 + 0.007833 + 0.007875 + 0.007833 + 0.010542 + 0.008334 + 0.007708 + 0.008125 + 0.008375 + 0.007875 + 0.007917 + 0.008625 + 0.011458 + 0.008333 + 0.008167 + 0.007792 + 0.008125 + 0.00775 + 0.010709 + 0.009084 + 0.009 + 0.008417 + 0.007625 + 0.008 + 0.008667 + 0.008208 + 0.0085 + 0.008084 + 0.008791 + 0.008375 + 0.007666 + 0.008 + 0.008042 + 0.009 + 0.008666 + 0.014458 + 0.008208 + 0.008834 + 0.012208 + 0.016375 + 0.008375 + 0.009083 + 0.009125 + 0.010625 + 0.012333 + 0.009292 + 0.00875 + 0.009 + 0.010542 + 0.009958 + 0.00975 + 0.013 + 0.010333 + 0.009 + 0.007708 + 0.007791 + 0.007084 + 0.007959 + 0.008 + 0.008708 + 0.008083 + 0.007667 + 0.008125 + 0.007292 + 0.009875 + 0.006917 + 0.007 + 0.006791 + 0.008375 + 0.008417 + 0.0085 + 0.033834 + 0.027583 + 0.009417 + 0.008791 + 0.008458 + 0.008708 + 0.012208 + 0.008084 + 0.008291 + 0.008542 + 0.008166 + 0.008875 + 0.00875 + 0.008792 + 0.008291 + 0.008917 + 0.008583 + 0.00925 + 0.008209 + 0.008209 + 0.008209 + 0.008125 + 0.008375 + 0.008167 + 0.0085 + 0.007958 + 0.008375 + 0.00875 + 0.008375 + 0.007875 + 0.008 + 0.00825 + 0.008583 + 0.008916 + 0.008833 + 0.008833 + 0.008167 + 0.008833 + 0.008708 + 0.009125 + 0.012042 + 0.009291 + 0.008542 + 0.008458 + 0.008292 + 0.008459 + 0.008583 + 0.008958 + 0.010167 + 0.009708 + 0.008167 + 0.00875 + 0.009 + 0.013 + 0.010083 + 0.010375 + 0.008917 + 0.009791 + 0.008125 + 0.008875 + 0.008708 + 0.008916 + 0.008375 + 0.009625 + 0.009 + 0.008542 + 0.009667 + 0.008458 + 0.014916 + 0.009708 + 0.014875 + 0.011458 + 0.010958 + 0.01 + 0.009041 + 0.009583 + 0.011292 + 0.011542 + 0.012 + 0.011083 + 0.021459 + 0.010916 + 0.010167 + 0.012375 + 0.010708 + 0.009583 + 0.009667 + 0.009667 + 0.007542 + 0.007959 + 0.007041 + 0.007708 + 0.007542 + 0.016708 + 0.006625 + 0.007541 + 0.007458 + 0.01075 + 0.008708 + 0.00825 + 0.008417 + 0.008416 + 0.008292 + 0.008541 + 0.0085 + 0.008541 + 0.008667 + 0.008458 + 0.008375 + 0.008541 + 0.008417 + 0.008375 + 0.008459 + 0.009125 + 0.008292 + 0.008416 + 0.008459 + 0.008666 + 0.008583 + 0.008625 + 0.0085 + 0.009125 + 0.008375 + 0.008584 + 0.0085 + 0.010375 + 0.009167 + 0.008667 + 0.00825 + 0.012834 + 0.009334 + 0.016125 + 0.008625 + 0.008666 + 0.019667 + 0.009 + 0.009458 + 0.009667 + 0.009375 + 0.008917 + 0.009542 + 0.014292 + 0.013667 + 0.012458 + 0.012416 + 0.011125 + 0.018541 + 0.009667 + 0.009459 + 0.011583 + 0.0105 + 0.012959 + 0.0115 + 0.011167 + 0.010541 + 0.01 + 0.0115 + 0.011667 + 0.011417 + 0.012375 + 0.010958 + 0.011125 + 0.01 + 0.009333 + 0.010042 + 0.011458 + 0.030625 + 0.01475 + 0.015541 + 0.018125 + 0.018208 + 0.012416 + 0.011333 + 0.009708 + 0.00975 + 0.010709 + 0.012875 + 0.014792 + 0.0115 + 0.010625 + 0.009292 + 0.009208 + 0.009333 + 0.010959 + 0.009209 + 0.008584 + 0.008458 + 0.007791 + 0.007583 + 0.00775 + 0.007375 + 0.007459 + 0.007417 + 0.019042 + 0.009708 + 0.010541 + 0.007292 + 0.006666 + 0.009417 + 0.008 + 0.007875 + 0.007583 + 0.00925 + 0.008458 + 0.008208 + 0.008333 + 0.007708 + 0.007667 + 0.008041 + 0.008083 + 0.008833 + 0.0095 + 0.010542 + 0.010666 + 0.010583 + 0.009 + 0.008166 + 0.008458 + 0.008708 + 0.008208 + 0.008375 + 0.01025 + 0.010542 + 0.011459 + 0.010167 + 0.010166 + 0.010875 + 0.010833 + 0.01075 + 0.009833 + 0.008083 + 0.008375 + 0.007667 + 0.008333 + 0.008625 + 0.008375 + 0.016209 + 0.010209 + 0.010625 + 0.01075 + 0.009416 + 0.009542 + 0.011125 + 0.0095 + 0.008542 + 0.00825 + 0.010208 + 0.01075 + 0.010792 + 0.010541 + 0.010292 + 0.0115 + 0.011 + 0.009666 + 0.009125 + 0.009709 + 0.010583 + 0.010084 + 0.010709 + 0.014084 + 0.057584 + 0.021584 + 0.00975 + 0.009042 + 0.009208 + 0.008625 + 0.008625 + 0.009125 + 0.014167 + 0.009333 + 0.009 + 0.013375 + 0.010292 + 0.0095 + 0.00975 + 0.009875 + 0.009292 + 0.010833 + 0.015375 + 0.0105 + 0.0095 + 0.0095 + 0.009666 + 0.010125 + 0.008583 + 0.007834 + 0.007917 + 0.007583 + 0.007416 + 0.008042 + 0.008542 + 0.008208 + 0.007667 + 0.008209 + 0.01025 + 0.007667 + 0.0075 + 0.006958 + 0.010084 + 0.008667 + 0.008458 + 0.012792 + 0.008459 + 0.008459 + 0.008333 + 0.009125 + 0.008792 + 0.008458 + 0.008541 + 0.008792 + 0.007959 + 0.008 + 0.007875 + 0.02025 + 0.008083 + 0.00825 + 0.008708 + 0.008167 + 0.008042 + 0.007791 + 0.007917 + 0.00875 + 0.008292 + 0.008291 + 0.007833 + 0.008458 + 0.008125 + 0.007666 + 0.008917 + 0.008 + 0.008 + 0.008 + 0.008167 + 0.022125 + 0.009875 + 0.008333 + 0.008333 + 0.008625 + 0.008375 + 0.008292 + 0.008291 + 0.010042 + 0.014084 + 0.008417 + 0.009167 + 0.0085 + 0.008417 + 0.008666 + 0.008209 + 0.015 + 0.008042 + 0.010583 + 0.008375 + 0.008625 + 0.008417 + 0.008375 + 0.008666 + 0.008792 + 0.0095 + 0.008917 + 0.00825 + 0.008209 + 0.010709 + 0.008666 + 0.008208 + 0.008292 + 0.008291 + 0.00825 + 0.008917 + 0.008709 + 0.009 + 0.008958 + 0.013542 + 0.011458 + 0.010583 + 0.010292 + 0.008416 + 0.009375 + 0.009333 + 0.0145 + 0.010292 + 0.008875 + 0.0085 + 0.0095 + 0.007666 + 0.007208 + 0.007208 + 0.007458 + 0.007834 + 0.007042 + 0.007083 + 0.007083 + 0.007125 + 0.007667 + 0.009625 + 0.006458 + 0.007708 + 0.007041 + 0.008042 + 0.007875 + 0.007875 + 0.0075 + 0.007875 + 0.011 + 0.007917 + 0.007833 + 0.007375 + 0.007875 + 0.007375 + 0.018542 + 0.007917 + 0.007708 + 0.007666 + 0.008167 + 0.007334 + 0.007541 + 0.007917 + 0.007833 + 0.011708 + 0.007916 + 0.007875 + 0.007875 + 0.007666 + 0.007625 + 0.007583 + 0.008 + 0.007875 + 0.007709 + 0.007958 + 0.008042 + 0.007708 + 0.008083 + 0.007833 + 0.007334 + 0.00725 + 0.008125 + 0.008333 + 0.008167 + 0.010125 + 0.009666 + 0.008417 + 0.010167 + 0.009875 + 0.02925 + 0.01875 + 0.015125 + 0.011959 + 0.011084 + 0.010875 + 0.010958 + 0.010042 + 0.009334 + 0.009625 + 0.010042 + 0.009583 + 0.009541 + 0.009333 + 0.015125 + 0.008708 + 0.008875 + 0.01075 + 0.010667 + 0.009708 + 0.009375 + 0.010042 + 0.009125 + 0.011458 + 0.011666 + 0.010417 + 0.012875 + 0.012625 + 0.012833 + 0.011792 + 0.01175 + 0.013167 + 0.01225 + 0.0115 + 0.011375 + 0.009375 + 0.009333 + 0.009958 + 0.014792 + 0.012583 + 0.012416 + 0.011375 + 0.008708 + 0.008042 + 0.007583 + 0.007792 + 0.007458 + 0.007666 + 0.0075 + 0.0075 + 0.0075 + 0.009708 + 0.006708 + 0.007041 + 0.00675 + 0.009208 + 0.008458 + 0.008208 + 0.008208 + 0.008042 + 0.008334 + 0.007875 + 0.007833 + 0.007708 + 0.007708 + 0.007541 + 0.007875 + 0.008125 + 0.008125 + 0.007833 + 0.007666 + 0.007625 + 0.007667 + 0.007875 + 0.008459 + 0.008292 + 0.008 + 0.007958 + 0.00775 + 0.007833 + 0.007458 + 0.008 + 0.007708 + 0.007792 + 0.00775 + 0.008459 + 0.008125 + 0.007917 + 0.007708 + 0.007792 + 0.008 + 0.007792 + 0.00875 + 0.008125 + 0.008583 + 0.008625 + 0.007583 + 0.008 + 0.00825 + 0.00875 + 0.008583 + 0.008792 + 0.008334 + 0.007875 + 0.008292 + 0.008625 + 0.011125 + 0.011625 + 0.010833 + 0.010875 + 0.009875 + 0.008 + 0.008208 + 0.008583 + 0.008833 + 0.008167 + 0.008458 + 0.008958 + 0.008667 + 0.00775 + 0.008125 + 0.008667 + 0.008125 + 0.0175 + 0.009459 + 0.008459 + 0.013791 + 0.012375 + 0.012042 + 0.011708 + 0.011333 + 0.010625 + 0.011459 + 0.011208 + 0.01225 + 0.010791 + 0.009375 + 0.009833 + 0.011125 + 0.015584 + 0.011666 + 0.01075 + 0.016791 + 0.010708 + 0.009333 + 0.009417 + 0.009083 + 0.008916 + 0.007666 + 0.007417 + 0.013875 + 0.010584 + 0.010125 + 0.009334 + 0.006584 + 0.007041 + 0.009083 + 0.008333 + 0.008625 + 0.008375 + 0.008209 + 0.008334 + 0.008083 + 0.00875 + 0.008292 + 0.008042 + 0.009334 + 0.011208 + 0.00875 + 0.008208 + 0.008125 + 0.025084 + 0.008417 + 0.008583 + 0.008459 + 0.018709 + 0.008416 + 0.008333 + 0.00875 + 0.009 + 0.008583 + 0.009042 + 0.009209 + 0.009166 + 0.00825 + 0.00875 + 0.009125 + 0.00875 + 0.012292 + 0.009709 + 0.0105 + 0.009708 + 0.022208 + 0.010584 + 0.009583 + 0.009 + 0.009 + 0.014958 + 0.0095 + 0.014375 + 0.009541 + 0.00925 + 0.012666 + 0.009292 + 0.010291 + 0.009583 + 0.009792 + 0.010875 + 0.0095 + 0.012166 + 0.008792 + 0.009 + 0.008541 + 0.01675 + 0.009041 + 0.008875 + 0.011792 + 0.017167 + 0.009083 + 0.00875 + 0.009708 + 0.009375 + 0.009 + 0.017917 + 0.008625 + 0.010042 + 0.013916 + 0.00925 + 0.009375 + 0.010375 + 0.00925 + 0.015917 + 0.009958 + 0.009042 + 0.012541 + 0.011917 + 0.009208 + 0.021667 + 0.01275 + 0.009791 + 0.009375 + 0.009375 + 0.00725 + 0.0075 + 0.008125 + 0.008125 + 0.009375 + 0.007834 + 0.007875 + 0.007833 + 0.007625 + 0.0075 + 0.008625 + 0.006334 + 0.006917 + 0.006584 + 0.008208 + 0.00825 + 0.007875 + 0.007959 + 0.008125 + 0.0075 + 0.0075 + 0.00775 + 0.008917 + 0.008625 + 0.008291 + 0.007542 + 0.007875 + 0.008083 + 0.007959 + 0.00775 + 0.008 + 0.007875 + 0.00825 + 0.007958 + 0.008208 + 0.007792 + 0.007959 + 0.007958 + 0.008167 + 0.008167 + 0.008416 + 0.007708 + 0.007417 + 0.007542 + 0.0155 + 0.008708 + 0.007959 + 0.008167 + 0.008541 + 0.008 + 0.007958 + 0.00825 + 0.008125 + 0.008125 + 0.008958 + 0.0085 + 0.008542 + 0.008167 + 0.008625 + 0.008167 + 0.008167 + 0.008541 + 0.008625 + 0.008666 + 0.012042 + 0.009291 + 0.008209 + 0.008834 + 0.008166 + 0.008875 + 0.008542 + 0.008666 + 0.008542 + 0.008333 + 0.008875 + 0.008083 + 0.008625 + 0.008458 + 0.00875 + 0.008709 + 0.008542 + 0.008 + 0.00825 + 0.008333 + 0.012417 + 0.009542 + 0.009208 + 0.008791 + 0.009459 + 0.009041 + 0.011667 + 0.009542 + 0.00825 + 0.009458 + 0.008916 + 0.009708 + 0.009042 + 0.011875 + 0.009833 + 0.04225 + 0.01675 + 0.009042 + 0.008792 + 0.008667 + 0.008084 + 0.008041 + 0.007792 + 0.008875 + 0.008125 + 0.008958 + 0.013167 + 0.008625 + 0.008 + 0.008125 + 0.010042 + 0.008958 + 0.009167 + 0.010541 + 0.010875 + 0.010541 + 0.0095 + 0.009 + 0.009 + 0.008167 + 0.008208 + 0.008583 + 0.012958 + 0.010792 + 0.008625 + 0.009708 + 0.010208 + 0.0105 + 0.01125 + 0.010292 + 0.010834 + 0.008917 + 0.008708 + 0.008333 + 0.007959 + 0.008334 + 0.008 + 0.007875 + 0.008292 + 0.008208 + 0.008458 + 0.007709 + 0.007958 + 0.008167 + 0.020959 + 0.007875 + 0.0085 + 0.009167 + 0.008458 + 0.0085 + 0.01825 + 0.008875 + 0.008208 + 0.010959 + 0.008459 + 0.0085 + 0.009 + 0.008459 + 0.008709 + 0.008542 + 0.008792 + 0.011334 + 0.010333 + 0.011208 + 0.012166 + 0.010958 + 0.016041 + 0.0115 + 0.010459 + 0.009042 + 0.010541 + 0.011667 + 0.012 + 0.0115 + 0.011834 + 0.011625 + 0.011917 + 0.011292 + 0.011166 + 0.012125 + 0.011208 + 0.012708 + 0.013083 + 0.011625 + 0.010708 + 0.009958 + 0.009209 + 0.0135 + 0.009708 + 0.011875 + 0.011041 + 0.010584 + 0.008459 + 0.009083 + 0.00925 + 0.011208 + 0.01125 + 0.009542 + 0.009791 + 0.008125 + 0.008166 + 0.008375 + 0.008458 + 0.00925 + 0.010333 + 0.009125 + 0.01025 + 0.01025 + 0.00875 + 0.007791 + 0.007459 + 0.010333 + 0.010917 + 0.010042 + 0.009208 + 0.008833 + 0.008875 + 0.00925 + 0.008292 + 0.008959 + 0.008 + 0.007708 + 0.007792 + 0.007542 + 0.007583 + 0.007084 + 0.007875 + 0.007875 + 0.008042 + 0.007875 + 0.008125 + 0.007792 + 0.007625 + 0.007916 + 0.007875 + 0.0075 + 0.008084 + 0.00775 + 0.007709 + 0.007875 + 0.008 + 0.007792 + 0.007791 + 0.007917 + 0.014833 + 0.008 + 0.00775 + 0.010042 + 0.01 + 0.010417 + 0.010375 + 0.010541 + 0.010708 + 0.017333 + 0.009667 + 0.0085 + 0.008333 + 0.007917 + 0.008542 + 0.009917 + 0.010916 + 0.01075 + 0.010708 + 0.011291 + 0.024542 + 0.014583 + 0.011208 + 0.010125 + 0.011333 + 0.012834 + 0.012042 + 0.011833 + 0.010083 + 0.010083 + 0.010042 + 0.00975 + 0.01075 + 0.009333 + 0.0095 + 0.009542 + 0.008958 + 0.010875 + 0.010125 + 0.010875 + 0.010417 + 0.009458 + 0.009875 + 0.009958 + 0.010916 + 0.009917 + 0.010541 + 0.01125 + 0.010291 + 0.009417 + 0.009875 + 0.009709 + 0.011667 + 0.010083 + 0.009541 + 0.009792 + 0.008084 + 0.007125 + 0.007083 + 0.0075 + 0.008584 + 0.007875 + 0.008416 + 0.00975 + 0.008958 + 0.006417 + 0.006292 + 0.00825 + 0.007958 + 0.007792 + 0.007583 + 0.007625 + 0.007958 + 0.008083 + 0.007958 + 0.0075 + 0.008166 + 0.007458 + 0.007625 + 0.008541 + 0.007834 + 0.008292 + 0.008 + 0.008083 + 0.007459 + 0.008666 + 0.00775 + 0.008042 + 0.0075 + 0.008334 + 0.007917 + 0.00775 + 0.007584 + 0.00775 + 0.008167 + 0.007916 + 0.007792 + 0.007792 + 0.007584 + 0.007625 + 0.008458 + 0.008166 + 0.0085 + 0.0085 + 0.008792 + 0.008416 + 0.008 + 0.008375 + 0.007958 + 0.007958 + 0.011792 + 0.011 + 0.010792 + 0.011041 + 0.009584 + 0.008208 + 0.008333 + 0.00975 + 0.008458 + 0.008459 + 0.009042 + 0.008166 + 0.008417 + 0.008084 + 0.007833 + 0.008375 + 0.011333 + 0.011708 + 0.010292 + 0.010459 + 0.011166 + 0.011416 + 0.010375 + 0.010708 + 0.009208 + 0.007916 + 0.008834 + 0.009125 + 0.008834 + 0.011834 + 0.012209 + 0.011292 + 0.014584 + 0.010125 + 0.009042 + 0.009834 + 0.023375 + 0.019416 + 0.012417 + 0.011542 + 0.010625 + 0.009417 + 0.012083 + 0.014333 + 0.009208 + 0.017458 + 0.008875 + 0.009542 + 0.009333 + 0.009208 + 0.013875 + 0.014625 + 0.009167 + 0.01325 + 0.018 + 0.010541 + 0.009292 + 0.013209 + 0.009917 + 0.009917 + 0.0115 + 0.009792 + 0.010458 + 0.010125 + 0.010459 + 0.009459 + 0.011 + 0.010292 + 0.009542 + 0.009917 + 0.01125 + 0.011667 + 0.009875 + 0.01 + 0.011833 + 0.010458 + 0.010042 + 0.011917 + 0.009958 + 0.028084 + 0.011083 + 0.010625 + 0.010542 + 0.0085 + 0.016875 + 0.019875 + 0.018375 + 0.00875 + 0.008709 + 0.008416 + 0.008584 + 0.008583 + 0.008375 + 0.010833 + 0.008666 + 0.008958 + 0.008834 + 0.008459 + 0.008959 + 0.009125 + 0.01225 + 0.009333 + 0.008333 + 0.009083 + 0.008292 + 0.009666 + 0.008834 + 0.009084 + 0.015959 + 0.009459 + 0.019875 + 0.01275 + 0.015917 + 0.011834 + 0.011375 + 0.011041 + 0.010583 + 0.010834 + 0.011667 + 0.020166 + 0.008833 + 0.008625 + 0.008667 + 0.008875 + 0.00875 + 0.008625 + 0.009917 + 0.010709 + 0.013 + 0.01 + 0.009458 + 0.009834 + 0.0095 + 0.00975 + 0.020917 + 0.010708 + 0.012708 + 0.009375 + 0.009625 + 0.009583 + 0.009416 + 0.012083 + 0.016 + 0.0085 + 0.007917 + 0.007833 + 0.007875 + 0.007417 + 0.007666 + 0.007834 + 0.00725 + 0.007333 + 0.007667 + 0.008958 + 0.008292 + 0.008416 + 0.008833 + 0.00975 + 0.008125 + 0.007667 + 0.008375 + 0.015917 + 0.008666 + 0.008 + 0.007833 + 0.007709 + 0.007709 + 0.008042 + 0.008041 + 0.008209 + 0.008042 + 0.008458 + 0.007625 + 0.007666 + 0.00775 + 0.007917 + 0.008042 + 0.007916 + 0.007959 + 0.0075 + 0.00825 + 0.007458 + 0.007709 + 0.008125 + 0.008083 + 0.007791 + 0.008333 + 0.0075 + 0.007666 + 0.007583 + 0.008042 + 0.008 + 0.008208 + 0.008291 + 0.008583 + 0.0085 + 0.008458 + 0.00825 + 0.007916 + 0.008209 + 0.007083 + 0.008416 + 0.009 + 0.008791 + 0.008333 + 0.007958 + 0.008209 + 0.00775 + 0.007167 + 0.008792 + 0.008125 + 0.008292 + 0.011084 + 0.008584 + 0.008292 + 0.008333 + 0.008709 + 0.007709 + 0.007709 + 0.008292 + 0.008166 + 0.00825 + 0.008167 + 0.008542 + 0.008458 + 0.008583 + 0.008667 + 0.008 + 0.00925 + 0.012208 + 0.009709 + 0.009667 + 0.009208 + 0.009375 + 0.008625 + 0.00825 + 0.013875 + 0.009292 + 0.00825 + 0.008709 + 0.009166 + 0.024792 + 0.030709 + 0.013459 + 0.01125 + 0.009958 + 0.008458 + 0.008292 + 0.008667 + 0.008666 + 0.008333 + 0.008625 + 0.008667 + 0.010958 + 0.010458 + 0.009125 + 0.0075 + 0.007625 + 0.011125 + 0.008625 + 0.009 + 0.008666 + 0.008958 + 0.008417 + 0.008917 + 0.008375 + 0.008416 + 0.008958 + 0.0085 + 0.008875 + 0.008458 + 0.008917 + 0.009 + 0.008833 + 0.009 + 0.008709 + 0.008542 + 0.009458 + 0.008541 + 0.008209 + 0.008375 + 0.009167 + 0.008625 + 0.00875 + 0.009125 + 0.008459 + 0.009167 + 0.008625 + 0.008458 + 0.008667 + 0.008834 + 0.008209 + 0.008416 + 0.008292 + 0.010084 + 0.009125 + 0.009292 + 0.008917 + 0.009 + 0.009 + 0.009625 + 0.01175 + 0.0095 + 0.009125 + 0.009792 + 0.011917 + 0.009791 + 0.010125 + 0.008792 + 0.011333 + 0.01025 + 0.012667 + 0.008958 + 0.010292 + 0.009875 + 0.009417 + 0.012084 + 0.01275 + 0.020958 + 0.0125 + 0.011167 + 0.009208 + 0.011 + 0.010167 + 0.011583 + 0.010125 + 0.01125 + 0.011333 + 0.016208 + 0.025958 + 0.022041 + 0.014125 + 0.013792 + 0.013583 + 0.013208 + 0.01075 + 0.008959 + 0.013417 + 0.012458 + 0.011709 + 0.011084 + 0.01025 + 0.010667 + 0.020583 + 0.017917 + 0.009625 + 0.0085 + 0.007917 + 0.007584 + 0.007292 + 0.008125 + 0.008667 + 0.0085 + 0.009459 + 0.010416 + 0.009917 + 0.007583 + 0.00675 + 0.0085 + 0.008 + 0.008666 + 0.0085 + 0.008208 + 0.008542 + 0.008292 + 0.008583 + 0.007625 + 0.0105 + 0.008042 + 0.01175 + 0.009208 + 0.007792 + 0.008 + 0.007625 + 0.011584 + 0.007917 + 0.009583 + 0.008542 + 0.008042 + 0.008125 + 0.007375 + 0.008334 + 0.008209 + 0.012 + 0.008792 + 0.009083 + 0.008333 + 0.007875 + 0.007875 + 0.007792 + 0.016833 + 0.00875 + 0.00975 + 0.009125 + 0.009 + 0.009541 + 0.009167 + 0.008875 + 0.009 + 0.0085 + 0.010042 + 0.012292 + 0.012041 + 0.011375 + 0.008625 + 0.008375 + 0.008375 + 0.00825 + 0.008625 + 0.008917 + 0.009833 + 0.012166 + 0.01075 + 0.010625 + 0.008916 + 0.01125 + 0.011333 + 0.011292 + 0.010667 + 0.009625 + 0.009417 + 0.009375 + 0.009 + 0.010334 + 0.008709 + 0.008916 + 0.007875 + 0.008667 + 0.00925 + 0.008792 + 0.012709 + 0.012417 + 0.010916 + 0.010625 + 0.010625 + 0.01 + 0.009584 + 0.021541 + 0.02075 + 0.011791 + 0.012917 + 0.0125 + 0.012125 + 0.014583 + 0.008709 + 0.007959 + 0.007458 + 0.007875 + 0.008083 + 0.008042 + 0.007584 + 0.00725 + 0.007459 + 0.00725 + 0.009917 + 0.0065 + 0.006417 + 0.006916 + 0.008917 + 0.008583 + 0.007916 + 0.00825 + 0.007958 + 0.008 + 0.007875 + 0.008375 + 0.008583 + 0.00825 + 0.00825 + 0.008416 + 0.007875 + 0.008166 + 0.007917 + 0.008959 + 0.008417 + 0.009 + 0.007916 + 0.007875 + 0.008208 + 0.008875 + 0.007583 + 0.015375 + 0.010083 + 0.009584 + 0.00975 + 0.009458 + 0.008375 + 0.008 + 0.008708 + 0.008583 + 0.008958 + 0.008625 + 0.007917 + 0.00775 + 0.008208 + 0.008833 + 0.009042 + 0.008666 + 0.008708 + 0.008667 + 0.008791 + 0.008584 + 0.008458 + 0.008458 + 0.008916 + 0.010292 + 0.010917 + 0.012167 + 0.01125 + 0.010667 + 0.0105 + 0.008667 + 0.008042 + 0.00875 + 0.008667 + 0.008291 + 0.007667 + 0.008166 + 0.008333 + 0.00775 + 0.011167 + 0.011 + 0.010416 + 0.008625 + 0.008416 + 0.008417 + 0.008167 + 0.008583 + 0.007583 + 0.009333 + 0.009708 + 0.009209 + 0.011458 + 0.011041 + 0.010917 + 0.008625 + 0.00875 + 0.009875 + 0.009541 + 0.011167 + 0.01125 + 0.010625 + 0.010792 + 0.010959 + 0.009709 + 0.007292 + 0.007625 + 0.006959 + 0.007541 + 0.008083 + 0.014542 + 0.008333 + 0.00825 + 0.008083 + 0.010333 + 0.00675 + 0.006708 + 0.007042 + 0.008875 + 0.008708 + 0.008125 + 0.008375 + 0.00825 + 0.007958 + 0.008083 + 0.008625 + 0.008333 + 0.008125 + 0.008 + 0.008375 + 0.009959 + 0.008542 + 0.008333 + 0.008292 + 0.008041 + 0.011333 + 0.011916 + 0.010791 + 0.01075 + 0.0115 + 0.014417 + 0.012542 + 0.011042 + 0.009125 + 0.010458 + 0.010167 + 0.009625 + 0.01225 + 0.01225 + 0.009625 + 0.00975 + 0.009125 + 0.0095 + 0.009167 + 0.009375 + 0.010375 + 0.009792 + 0.008666 + 0.009583 + 0.011542 + 0.01125 + 0.013042 + 0.015416 + 0.011542 + 0.010041 + 0.009167 + 0.008459 + 0.00825 + 0.010042 + 0.011833 + 0.013875 + 0.012458 + 0.013416 + 0.011458 + 0.011167 + 0.011 + 0.010167 + 0.009459 + 0.00975 + 0.012709 + 0.013 + 0.012917 + 0.011041 + 0.009375 + 0.010208 + 0.009125 + 0.009375 + 0.011167 + 0.011083 + 0.026291 + 0.011625 + 0.019666 + 0.013875 + 0.020375 + 0.012459 + 0.012334 + 0.012417 + 0.012916 + 0.009792 + 0.012 + 0.012083 + 0.012042 + 0.011875 + 0.010042 + 0.008583 + 0.007833 + 0.007542 + 0.007583 + 0.007959 + 0.007708 + 0.008875 + 0.009583 + 0.010208 + 0.008208 + 0.008708 + 0.0085 + 0.008542 + 0.007042 + 0.007333 + 0.009083 + 0.0075 + 0.007917 + 0.007875 + 0.008125 + 0.008375 + 0.007833 + 0.007833 + 0.007958 + 0.007625 + 0.009333 + 0.009917 + 0.009958 + 0.011 + 0.011208 + 0.011458 + 0.010417 + 0.010416 + 0.015125 + 0.008833 + 0.008833 + 0.010625 + 0.009875 + 0.012 + 0.011375 + 0.011292 + 0.012125 + 0.012 + 0.0095 + 0.009625 + 0.010292 + 0.009541 + 0.009084 + 0.009792 + 0.008875 + 0.008958 + 0.010208 + 0.011625 + 0.010333 + 0.00975 + 0.011667 + 0.010666 + 0.008958 + 0.009208 + 0.010917 + 0.010208 + 0.010333 + 0.009333 + 0.008917 + 0.00825 + 0.0085 + 0.008541 + 0.007792 + 0.008791 + 0.0085 + 0.008542 + 0.008709 + 0.008 + 0.007875 + 0.011667 + 0.009375 + 0.009333 + 0.008542 + 0.008959 + 0.010459 + 0.0105 + 0.010334 + 0.008875 + 0.008084 + 0.008541 + 0.00975 + 0.010083 + 0.009125 + 0.009583 + 0.023708 + 0.014333 + 0.016625 + 0.00975 + 0.009917 + 0.012083 + 0.010333 + 0.011709 + 0.010625 + 0.010958 + 0.010333 + 0.009084 + 0.007959 + 0.007125 + 0.007709 + 0.007334 + 0.007208 + 0.007083 + 0.006958 + 0.006917 + 0.007416 + 0.007167 + 0.008666 + 0.008042 + 0.00675 + 0.007125 + 0.008042 + 0.007833 + 0.007833 + 0.008417 + 0.0085 + 0.00775 + 0.00775 + 0.008083 + 0.008416 + 0.00775 + 0.0085 + 0.008042 + 0.008875 + 0.007875 + 0.015459 + 0.007459 + 0.01775 + 0.007875 + 0.008333 + 0.008375 + 0.007834 + 0.00725 + 0.007708 + 0.007792 + 0.007833 + 0.008042 + 0.007958 + 0.010333 + 0.008458 + 0.008 + 0.007875 + 0.007542 + 0.016708 + 0.00825 + 0.007834 + 0.009167 + 0.010083 + 0.008667 + 0.007916 + 0.007959 + 0.007958 + 0.007583 + 0.008625 + 0.008791 + 0.00875 + 0.008292 + 0.012958 + 0.00975 + 0.010208 + 0.008292 + 0.008458 + 0.008333 + 0.008167 + 0.0085 + 0.008542 + 0.00825 + 0.008375 + 0.008208 + 0.008709 + 0.008583 + 0.008833 + 0.0085 + 0.008208 + 0.008292 + 0.011333 + 0.010833 + 0.010042 + 0.009667 + 0.008166 + 0.008417 + 0.008916 + 0.008958 + 0.008917 + 0.008792 + 0.014625 + 0.011583 + 0.010792 + 0.00925 + 0.008167 + 0.008791 + 0.008541 + 0.011958 + 0.011417 + 0.011 + 0.008959 + 0.009333 + 0.007541 + 0.007125 + 0.007 + 0.007416 + 0.007334 + 0.007916 + 0.006833 + 0.007459 + 0.006792 + 0.006916 + 0.00875 + 0.006417 + 0.0065 + 0.006333 + 0.008166 + 0.007542 + 0.007875 + 0.007292 + 0.008 + 0.008792 + 0.008125 + 0.007791 + 0.007875 + 0.00775 + 0.007667 + 0.007375 + 0.007916 + 0.007458 + 0.007583 + 0.007792 + 0.007791 + 0.007708 + 0.008209 + 0.007917 + 0.008083 + 0.007208 + 0.007458 + 0.007625 + 0.007917 + 0.007625 + 0.007417 + 0.007708 + 0.007917 + 0.007625 + 0.007709 + 0.008375 + 0.007417 + 0.008 + 0.007334 + 0.007875 + 0.007459 + 0.007875 + 0.007584 + 0.008625 + 0.008 + 0.008167 + 0.00775 + 0.007792 + 0.013708 + 0.009708 + 0.009416 + 0.0085 + 0.008 + 0.008125 + 0.008542 + 0.008416 + 0.007583 + 0.007333 + 0.008 + 0.008375 + 0.010042 + 0.008708 + 0.008125 + 0.00825 + 0.008167 + 0.007041 + 0.007875 + 0.008125 + 0.007584 + 0.010125 + 0.010584 + 0.009834 + 0.008 + 0.008375 + 0.008083 + 0.008791 + 0.009084 + 0.008625 + 0.009125 + 0.013875 + 0.010333 + 0.01025 + 0.009 + 0.008959 + 0.008458 + 0.008875 + 0.010459 + 0.010375 + 0.010167 + 0.009333 + 0.017625 + 0.007625 + 0.006959 + 0.007 + 0.00775 + 0.008583 + 0.007625 + 0.00675 + 0.008459 + 0.009917 + 0.012 + 0.00775 + 0.007792 + 0.008333 + 0.008625 + 0.0075 + 0.007208 + 0.007458 + 0.00725 + 0.007291 + 0.007667 + 0.007333 + 0.00725 + 0.007709 + 0.007375 + 0.007625 + 0.0075 + 0.007834 + 0.00925 + 0.0095 + 0.01025 + 0.009666 + 0.009875 + 0.010042 + 0.007625 + 0.007959 + 0.007666 + 0.007917 + 0.007833 + 0.007291 + 0.007542 + 0.007333 + 0.007875 + 0.007459 + 0.007666 + 0.007709 + 0.007708 + 0.007416 + 0.007584 + 0.007291 + 0.0075 + 0.008417 + 0.007958 + 0.007791 + 0.007625 + 0.008042 + 0.007791 + 0.007583 + 0.008167 + 0.00875 + 0.007959 + 0.007959 + 0.010542 + 0.010625 + 0.010417 + 0.0105 + 0.01 + 0.008334 + 0.008167 + 0.007916 + 0.007917 + 0.007834 + 0.007916 + 0.008083 + 0.007542 + 0.008625 + 0.00775 + 0.007875 + 0.007708 + 0.007708 + 0.007667 + 0.007625 + 0.010833 + 0.011083 + 0.010375 + 0.012167 + 0.011209 + 0.011459 + 0.011667 + 0.011083 + 0.013083 + 0.0115 + 0.010667 + 0.011833 + 0.010875 + 0.008791 + 0.008708 + 0.011917 + 0.011708 + 0.011083 + 0.01125 + 0.008375 + 0.006875 + 0.007125 + 0.006834], + :filter-inline-eight + [0.011333 + 0.008833 + 0.008541 + 0.008416 + 0.009166 + 0.008541 + 0.008208 + 0.008542 + 0.007709 + 0.008292 + 0.00825 + 0.008333 + 0.008416 + 0.008625 + 0.008125 + 0.008292 + 0.007917 + 0.008208 + 0.00825 + 0.014 + 0.01025 + 0.017666 + 0.010167 + 0.009875 + 0.010209 + 0.025542 + 0.022458 + 0.010542 + 0.00975 + 0.0105 + 0.010209 + 0.010875 + 0.019292 + 0.011375 + 0.010083 + 0.010291 + 0.017667 + 0.014042 + 0.011625 + 0.010459 + 0.011792 + 0.009958 + 0.012167 + 0.010292 + 0.013833 + 0.012125 + 0.011125 + 0.009834 + 0.009167 + 0.016125 + 0.013709 + 0.009542 + 0.009875 + 0.009667 + 0.01075 + 0.009833 + 0.012291 + 0.009958 + 0.008875 + 0.009458 + 0.009333 + 0.010125 + 0.009666 + 0.009875 + 0.009292 + 0.009125 + 0.009417 + 0.009208 + 0.009125 + 0.009667 + 0.008667 + 0.010791 + 0.011666 + 0.009625 + 0.011167 + 0.017875 + 0.011416 + 0.011042 + 0.01175 + 0.012666 + 0.012375 + 0.010708 + 0.011125 + 0.009875 + 0.00975 + 0.022625 + 0.01425 + 0.007583 + 0.007625 + 0.007583 + 0.007917 + 0.007709 + 0.008084 + 0.007667 + 0.009709 + 0.007584 + 0.01125 + 0.019125 + 0.009375 + 0.007625 + 0.01475 + 0.017416 + 0.051208 + 0.030209 + 0.023375 + 0.024791 + 0.012625 + 0.012166 + 0.017834 + 0.01275 + 0.009792 + 0.010709 + 0.010583 + 0.011333 + 0.013166 + 0.010208 + 0.018916 + 0.011791 + 0.011208 + 0.01025 + 0.011291 + 0.010333 + 0.010083 + 0.010625 + 0.016375 + 0.009584 + 0.015 + 0.010625 + 0.010792 + 0.010375 + 0.011083 + 0.01325 + 0.025959 + 0.016292 + 0.0125 + 0.009125 + 0.009625 + 0.011459 + 0.010542 + 0.01075 + 0.010667 + 0.010125 + 0.009834 + 0.009625 + 0.014959 + 0.010292 + 0.017958 + 0.011792 + 0.012125 + 0.020834 + 0.01025 + 0.009875 + 0.010833 + 0.010209 + 0.011958 + 0.010125 + 0.020125 + 0.013291 + 0.012875 + 0.018125 + 0.012625 + 0.011083 + 0.010958 + 0.011334 + 0.011458 + 0.009958 + 0.010125 + 0.010083 + 0.01 + 0.01025 + 0.01125 + 0.018667 + 0.011292 + 0.014334 + 0.014792 + 0.012708 + 0.011583 + 0.011167 + 0.01025 + 0.020083 + 0.015958 + 0.013833 + 0.012375 + 0.011375 + 0.011 + 0.010833 + 0.018083 + 0.011292 + 0.009667 + 0.009458 + 0.009125 + 0.008958 + 0.00875 + 0.011833 + 0.008666 + 0.009791 + 0.009875 + 0.011042 + 0.008375 + 0.008666 + 0.0085 + 0.009375 + 0.009167 + 0.009459 + 0.009208 + 0.009208 + 0.009125 + 0.008792 + 0.011917 + 0.0095 + 0.009416 + 0.0095 + 0.009542 + 0.00875 + 0.009125 + 0.008834 + 0.010083 + 0.009416 + 0.009625 + 0.012875 + 0.009333 + 0.009292 + 0.00925 + 0.00975 + 0.009667 + 0.009416 + 0.009583 + 0.009541 + 0.009083 + 0.009917 + 0.009584 + 0.009834 + 0.01325 + 0.009667 + 0.009625 + 0.010166 + 0.009292 + 0.009792 + 0.009667 + 0.0105 + 0.009709 + 0.012292 + 0.009792 + 0.010167 + 0.009625 + 0.010333 + 0.009542 + 0.009959 + 0.012709 + 0.010834 + 0.010125 + 0.010625 + 0.011417 + 0.009458 + 0.010291 + 0.009958 + 0.01075 + 0.01025 + 0.010417 + 0.010292 + 0.010125 + 0.011625 + 0.009584 + 0.019333 + 0.017041 + 0.013291 + 0.013709 + 0.013042 + 0.011833 + 0.010709 + 0.009834 + 0.013917 + 0.010791 + 0.016125 + 0.012833 + 0.012042 + 0.010625 + 0.023542 + 0.010375 + 0.010084 + 0.020625 + 0.013959 + 0.011583 + 0.011583 + 0.010166 + 0.01025 + 0.013 + 0.010834 + 0.010167 + 0.008833 + 0.008416 + 0.008708 + 0.008417 + 0.008375 + 0.011416 + 0.008958 + 0.00925 + 0.010083 + 0.007209 + 0.007959 + 0.008458 + 0.009291 + 0.014542 + 0.010417 + 0.010667 + 0.009417 + 0.009791 + 0.009208 + 0.009709 + 0.009292 + 0.0095 + 0.009083 + 0.012292 + 0.009792 + 0.009334 + 0.00925 + 0.008709 + 0.0095 + 0.009458 + 0.00925 + 0.013959 + 0.009458 + 0.009958 + 0.009709 + 0.009208 + 0.009625 + 0.00925 + 0.009458 + 0.011791 + 0.0095 + 0.009875 + 0.009584 + 0.008709 + 0.009333 + 0.00925 + 0.009125 + 0.009125 + 0.009042 + 0.01 + 0.0095 + 0.009458 + 0.009167 + 0.009709 + 0.009125 + 0.012542 + 0.018917 + 0.012333 + 0.01125 + 0.012 + 0.012958 + 0.014125 + 0.012041 + 0.011 + 0.014542 + 0.010459 + 0.010917 + 0.01175 + 0.012334 + 0.012958 + 0.013209 + 0.010417 + 0.009958 + 0.009791 + 0.009791 + 0.009541 + 0.009 + 0.010125 + 0.010125 + 0.009709 + 0.009833 + 0.009875 + 0.009375 + 0.010292 + 0.015833 + 0.01275 + 0.01325 + 0.014333 + 0.019417 + 0.017042 + 0.009583 + 0.010333 + 0.013125 + 0.01375 + 0.017792 + 0.014292 + 0.013708 + 0.018917 + 0.014125 + 0.01125 + 0.01125 + 0.011375 + 0.011125 + 0.011375 + 0.011875 + 0.010125 + 0.0095 + 0.008667 + 0.010334 + 0.011792 + 0.012709 + 0.009833 + 0.012708 + 0.012375 + 0.009583 + 0.009541 + 0.00875 + 0.008458 + 0.009625 + 0.008875 + 0.008583 + 0.009166 + 0.008875 + 0.008791 + 0.009083 + 0.009041 + 0.00975 + 0.008917 + 0.0085 + 0.009416 + 0.009625 + 0.00875 + 0.0095 + 0.009167 + 0.008708 + 0.008208 + 0.011875 + 0.009041 + 0.009166 + 0.009083 + 0.00875 + 0.009292 + 0.008917 + 0.009292 + 0.010292 + 0.018209 + 0.008917 + 0.009083 + 0.009334 + 0.00975 + 0.009417 + 0.009792 + 0.01075 + 0.013 + 0.009917 + 0.009208 + 0.009291 + 0.011083 + 0.012291 + 0.012125 + 0.016125 + 0.0105 + 0.009417 + 0.009084 + 0.009 + 0.009333 + 0.00925 + 0.009083 + 0.009209 + 0.012542 + 0.009292 + 0.009042 + 0.009292 + 0.009959 + 0.012125 + 0.010375 + 0.009166 + 0.01 + 0.009542 + 0.009625 + 0.00975 + 0.009916 + 0.009542 + 0.01075 + 0.010125 + 0.01025 + 0.014166 + 0.013125 + 0.01175 + 0.010959 + 0.01 + 0.009 + 0.010666 + 0.010792 + 0.012458 + 0.012583 + 0.011084 + 0.010584 + 0.0105 + 0.008666 + 0.008958 + 0.009583 + 0.009958 + 0.008916 + 0.010333 + 0.009458 + 0.008833 + 0.008708 + 0.010541 + 0.010792 + 0.009875 + 0.013292 + 0.008625 + 0.010125 + 0.01 + 0.0095 + 0.0095 + 0.010125 + 0.009334 + 0.0095 + 0.009875 + 0.009584 + 0.009709 + 0.009667 + 0.008625 + 0.008584 + 0.008209 + 0.008583 + 0.008542 + 0.008708 + 0.00875 + 0.0085 + 0.008666 + 0.008291 + 0.011958 + 0.008542 + 0.011417 + 0.008958 + 0.009667 + 0.008792 + 0.011667 + 0.009583 + 0.010834 + 0.009292 + 0.008541 + 0.009041 + 0.009959 + 0.008875 + 0.01 + 0.013166 + 0.012208 + 0.011958 + 0.011833 + 0.012417 + 0.010875 + 0.00975 + 0.010583 + 0.013583 + 0.012042 + 0.011375 + 0.010334 + 0.010042 + 0.010625 + 0.011 + 0.010709 + 0.01 + 0.013375 + 0.009083 + 0.009417 + 0.008667 + 0.009542 + 0.009209 + 0.008834 + 0.009417 + 0.009292 + 0.008917 + 0.009333 + 0.010791 + 0.020541 + 0.009375 + 0.009875 + 0.010792 + 0.011542 + 0.014042 + 0.013167 + 0.01225 + 0.010959 + 0.012042 + 0.011375 + 0.011583 + 0.011375 + 0.008916 + 0.0095 + 0.009625 + 0.012625 + 0.013333 + 0.012667 + 0.013 + 0.009083 + 0.010125 + 0.01075 + 0.009875 + 0.009292 + 0.008583 + 0.008958 + 0.008542 + 0.008667 + 0.008917 + 0.008667 + 0.00975 + 0.007541 + 0.0075 + 0.00825 + 0.01 + 0.009625 + 0.009542 + 0.009 + 0.008958 + 0.00875 + 0.008958 + 0.009709 + 0.009625 + 0.009042 + 0.008625 + 0.008834 + 0.008875 + 0.009125 + 0.00875 + 0.011 + 0.011709 + 0.011917 + 0.012041 + 0.009666 + 0.008416 + 0.009625 + 0.009041 + 0.009291 + 0.009375 + 0.008917 + 0.008708 + 0.009167 + 0.009041 + 0.008625 + 0.008917 + 0.009459 + 0.0095 + 0.008959 + 0.008709 + 0.009125 + 0.009667 + 0.009209 + 0.009667 + 0.009625 + 0.009291 + 0.00975 + 0.0095 + 0.009667 + 0.009542 + 0.0095 + 0.009166 + 0.013125 + 0.012792 + 0.0125 + 0.01225 + 0.010375 + 0.009167 + 0.0095 + 0.00925 + 0.011333 + 0.012458 + 0.011792 + 0.010167 + 0.010542 + 0.012667 + 0.012625 + 0.012542 + 0.012625 + 0.010042 + 0.009375 + 0.00925 + 0.012375 + 0.012708 + 0.012542 + 0.011917 + 0.010375 + 0.01025 + 0.009917 + 0.009917 + 0.01025 + 0.013167 + 0.014125 + 0.012833 + 0.013209 + 0.010125 + 0.010042 + 0.010875 + 0.013292 + 0.013167 + 0.027875 + 0.028459 + 0.010875 + 0.009 + 0.008333 + 0.008416 + 0.008959 + 0.008208 + 0.009667 + 0.010292 + 0.011709 + 0.0135 + 0.011334 + 0.012208 + 0.009208 + 0.01075 + 0.010958 + 0.012208 + 0.0115 + 0.01225 + 0.012333 + 0.011417 + 0.010542 + 0.008959 + 0.008792 + 0.00925 + 0.008958 + 0.009042 + 0.00925 + 0.009 + 0.008709 + 0.009042 + 0.008666 + 0.011291 + 0.009458 + 0.009334 + 0.00975 + 0.0085 + 0.008875 + 0.010667 + 0.00925 + 0.008584 + 0.009208 + 0.008875 + 0.009041 + 0.008792 + 0.008542 + 0.008667 + 0.008833 + 0.00875 + 0.008875 + 0.009167 + 0.009417 + 0.009542 + 0.0195 + 0.010042 + 0.011208 + 0.009833 + 0.008834 + 0.010125 + 0.00975 + 0.012792 + 0.012334 + 0.01325 + 0.010583 + 0.010541 + 0.009125 + 0.009459 + 0.009208 + 0.00925 + 0.0095 + 0.00925 + 0.009375 + 0.009334 + 0.008875 + 0.008834 + 0.009333 + 0.009583 + 0.009042 + 0.009167 + 0.009125 + 0.009417 + 0.012792 + 0.011583 + 0.011209 + 0.009333 + 0.010625 + 0.010167 + 0.009792 + 0.009875 + 0.010208 + 0.013167 + 0.0125 + 0.010791 + 0.009916 + 0.009875 + 0.010209 + 0.010042 + 0.013375 + 0.0125 + 0.011083 + 0.009792 + 0.008667 + 0.008625 + 0.008208 + 0.007875 + 0.008583 + 0.008208 + 0.008209 + 0.008292 + 0.007959 + 0.009209 + 0.009917 + 0.00925 + 0.007375 + 0.017583 + 0.009291 + 0.008958 + 0.009 + 0.007917 + 0.008125 + 0.00825 + 0.008 + 0.008166 + 0.008167 + 0.007875 + 0.008667 + 0.008333 + 0.008666 + 0.008458 + 0.008083 + 0.00875 + 0.008708 + 0.008834 + 0.008833 + 0.008916 + 0.008583 + 0.008583 + 0.008833 + 0.008583 + 0.008916 + 0.008333 + 0.008416 + 0.009166 + 0.008583 + 0.008167 + 0.008791 + 0.0085 + 0.008625 + 0.009084 + 0.008875 + 0.009209 + 0.009125 + 0.009125 + 0.008708 + 0.008833 + 0.008833 + 0.009333 + 0.008875 + 0.009375 + 0.009334 + 0.008917 + 0.009083 + 0.00925 + 0.009333 + 0.010625 + 0.014125 + 0.010959 + 0.0095 + 0.011042 + 0.013833 + 0.0115 + 0.011167 + 0.010167 + 0.010708 + 0.010833 + 0.010459 + 0.013042 + 0.0115 + 0.011417 + 0.010917 + 0.010791 + 0.013208 + 0.011709 + 0.013667 + 0.013541 + 0.01325 + 0.011959 + 0.010958 + 0.011959 + 0.011458 + 0.011625 + 0.015125 + 0.01375 + 0.010917 + 0.011458 + 0.012041 + 0.011666 + 0.014959 + 0.025125 + 0.021792 + 0.013416 + 0.011167 + 0.0085 + 0.007708 + 0.007584 + 0.007125 + 0.006625 + 0.006917 + 0.007 + 0.006625 + 0.006625 + 0.006542 + 0.006458 + 0.006042 + 0.005459 + 0.008167 + 0.007542 + 0.007167 + 0.007542 + 0.007375 + 0.00725 + 0.007333 + 0.007125 + 0.007292 + 0.007458 + 0.007875 + 0.008375 + 0.00825 + 0.008167 + 0.008291 + 0.00825 + 0.0085 + 0.008834 + 0.008 + 0.008042 + 0.008458 + 0.008 + 0.008375 + 0.008417 + 0.008167 + 0.008375 + 0.008 + 0.008167 + 0.008042 + 0.008541 + 0.007916 + 0.008042 + 0.0085 + 0.008167 + 0.008042 + 0.008208 + 0.008291 + 0.008875 + 0.011959 + 0.009042 + 0.008792 + 0.008708 + 0.00825 + 0.009417 + 0.009042 + 0.008375 + 0.008791 + 0.00875 + 0.008583 + 0.009083 + 0.021292 + 0.011 + 0.010209 + 0.010042 + 0.011875 + 0.014334 + 0.010375 + 0.009958 + 0.009708 + 0.00975 + 0.01075 + 0.010291 + 0.009625 + 0.02225 + 0.010583 + 0.0105 + 0.011125 + 0.011875 + 0.017667 + 0.011333 + 0.013 + 0.016084 + 0.012833 + 0.0115 + 0.012833 + 0.011125 + 0.010833 + 0.012167 + 0.010416 + 0.010375 + 0.009667 + 0.009958 + 0.010167 + 0.009542 + 0.012625 + 0.010084 + 0.008458 + 0.008792 + 0.00775 + 0.007791 + 0.008583 + 0.008125 + 0.008041 + 0.0085 + 0.008167 + 0.008459 + 0.010792 + 0.022708 + 0.011208 + 0.010625 + 0.015 + 0.012541 + 0.01175 + 0.010208 + 0.009792 + 0.010125 + 0.009917 + 0.01025 + 0.015041 + 0.016417 + 0.0105 + 0.010292 + 0.012959 + 0.015084 + 0.012375 + 0.012083 + 0.010875 + 0.0115 + 0.013167 + 0.01425 + 0.023792 + 0.020667 + 0.015625 + 0.012709 + 0.012292 + 0.01225 + 0.011416 + 0.013417 + 0.013375 + 0.011959 + 0.01075 + 0.010625 + 0.010583 + 0.011375 + 0.010792 + 0.013333 + 0.012833 + 0.013917 + 0.012959 + 0.011917 + 0.010458 + 0.010667 + 0.010333 + 0.014625 + 0.010958 + 0.010375 + 0.010375 + 0.010333 + 0.010042 + 0.009875 + 0.01075 + 0.014625 + 0.015125 + 0.013167 + 0.010125 + 0.011375 + 0.015792 + 0.010667 + 0.012708 + 0.010333 + 0.013541 + 0.014958 + 0.015625 + 0.013542 + 0.012208 + 0.01125 + 0.011792 + 0.011375 + 0.012125 + 0.0135 + 0.011583 + 0.0165 + 0.013792 + 0.012167 + 0.015 + 0.012458 + 0.013 + 0.012083 + 0.014084 + 0.015042 + 0.012167 + 0.01375 + 0.013458 + 0.013625 + 0.016416 + 0.01225 + 0.014041 + 0.011291 + 0.010042 + 0.010459 + 0.010166 + 0.010042 + 0.010041 + 0.010292 + 0.010041 + 0.010292 + 0.014542 + 0.007792 + 0.008541 + 0.007584 + 0.010125 + 0.009209 + 0.00975 + 0.009833 + 0.010292 + 0.009167 + 0.009208 + 0.009541 + 0.008875 + 0.009166 + 0.00925 + 0.009417 + 0.009792 + 0.009459 + 0.008792 + 0.009042 + 0.008958 + 0.00925 + 0.009584 + 0.009375 + 0.0095 + 0.009583 + 0.009 + 0.009 + 0.00875 + 0.009416 + 0.009458 + 0.009625 + 0.009083 + 0.008583 + 0.009333 + 0.008833 + 0.009167 + 0.009625 + 0.0095 + 0.008958 + 0.008833 + 0.010208 + 0.010041 + 0.012083 + 0.009667 + 0.0095 + 0.008958 + 0.008583 + 0.009667 + 0.009958 + 0.009375 + 0.0095 + 0.009292 + 0.009416 + 0.009917 + 0.009083 + 0.010083 + 0.009041 + 0.009334 + 0.009542 + 0.009041 + 0.011417 + 0.012292 + 0.011875 + 0.010542 + 0.009125 + 0.009708 + 0.01625 + 0.009375 + 0.009459 + 0.010084 + 0.010041 + 0.009958 + 0.010084 + 0.009459 + 0.010542 + 0.010041 + 0.013125 + 0.01275 + 0.011416 + 0.010542 + 0.009791 + 0.010042 + 0.010083 + 0.012583 + 0.012666 + 0.011458 + 0.010375 + 0.00975 + 0.009709 + 0.01275 + 0.010333 + 0.010084 + 0.008708 + 0.0085 + 0.008875 + 0.008417 + 0.007959 + 0.008792 + 0.008291 + 0.009417 + 0.010459 + 0.00975 + 0.007375 + 0.010584 + 0.010041 + 0.009 + 0.00925 + 0.00875 + 0.009125 + 0.009083 + 0.009375 + 0.009042 + 0.009167 + 0.008958 + 0.009125 + 0.008708 + 0.009 + 0.008958 + 0.009541 + 0.009333 + 0.00975 + 0.008875 + 0.008875 + 0.009125 + 0.009042 + 0.008917 + 0.00975 + 0.009209 + 0.009167 + 0.009166 + 0.00925 + 0.008875 + 0.008959 + 0.009375 + 0.009166 + 0.009375 + 0.008875 + 0.009417 + 0.008917 + 0.009458 + 0.009292 + 0.009292 + 0.009541 + 0.009709 + 0.009917 + 0.013 + 0.009292 + 0.009417 + 0.009417 + 0.0095 + 0.010084 + 0.009375 + 0.010292 + 0.009459 + 0.009083 + 0.009666 + 0.009625 + 0.009333 + 0.010041 + 0.009583 + 0.009209 + 0.00925 + 0.009542 + 0.009458 + 0.00975 + 0.009584 + 0.012625 + 0.011958 + 0.01075 + 0.009333 + 0.009417 + 0.009709 + 0.026625 + 0.0105 + 0.010541 + 0.010542 + 0.01 + 0.012334 + 0.0125 + 0.011208 + 0.01025 + 0.009833 + 0.009167 + 0.010541 + 0.012209 + 0.012959 + 0.018042 + 0.010041 + 0.010125 + 0.010208 + 0.008833 + 0.009083 + 0.008125 + 0.008583 + 0.008333 + 0.008375 + 0.008542 + 0.008333 + 0.008916 + 0.008958 + 0.010084 + 0.009083 + 0.0075 + 0.00675 + 0.009209 + 0.00875 + 0.009 + 0.009 + 0.008916 + 0.009125 + 0.008917 + 0.008875 + 0.009584 + 0.010334 + 0.009042 + 0.009291 + 0.009459 + 0.009125 + 0.009042 + 0.009875 + 0.011042 + 0.010166 + 0.012375 + 0.012291 + 0.011125 + 0.010417 + 0.011375 + 0.009917 + 0.010375 + 0.011167 + 0.011291 + 0.011667 + 0.013 + 0.011917 + 0.013167 + 0.013167 + 0.010792 + 0.01275 + 0.011958 + 0.011125 + 0.01175 + 0.012 + 0.010375 + 0.011375 + 0.01225 + 0.011542 + 0.010708 + 0.014375 + 0.027875 + 0.032791 + 0.019125 + 0.012334 + 0.011208 + 0.010042 + 0.010333 + 0.011041 + 0.009875 + 0.011417 + 0.010208 + 0.009667 + 0.010291 + 0.00925 + 0.009667 + 0.009625 + 0.011 + 0.010959 + 0.009875 + 0.009583 + 0.012667 + 0.009958 + 0.009291 + 0.0095 + 0.009708 + 0.01025 + 0.012334 + 0.010709 + 0.010083 + 0.010458 + 0.013375 + 0.010958 + 0.010208 + 0.01075 + 0.01075 + 0.011541 + 0.010292 + 0.012917 + 0.010834 + 0.010167 + 0.00975 + 0.011458 + 0.008916 + 0.00875 + 0.008292 + 0.00875 + 0.008542 + 0.008458 + 0.008792 + 0.008542 + 0.008667 + 0.015958 + 0.011541 + 0.011667 + 0.00725 + 0.006958 + 0.01375 + 0.010709 + 0.0125 + 0.010542 + 0.010958 + 0.0115 + 0.011125 + 0.01025 + 0.011084 + 0.011166 + 0.010208 + 0.010292 + 0.009667 + 0.011208 + 0.010292 + 0.018084 + 0.011917 + 0.010375 + 0.011791 + 0.012042 + 0.010791 + 0.010875 + 0.01225 + 0.010958 + 0.011042 + 0.012 + 0.012583 + 0.0115 + 0.013042 + 0.020583 + 0.010458 + 0.010167 + 0.0095 + 0.020167 + 0.009834 + 0.009584 + 0.010459 + 0.009917 + 0.009916 + 0.010042 + 0.010084 + 0.010083 + 0.009333 + 0.010791 + 0.00975 + 0.009792 + 0.010084 + 0.01375 + 0.010583 + 0.00975 + 0.009959 + 0.010166 + 0.0095 + 0.008458 + 0.009708 + 0.009916 + 0.010708 + 0.018417 + 0.010291 + 0.010875 + 0.012875 + 0.009709 + 0.012709 + 0.012209 + 0.010458 + 0.009958 + 0.009709 + 0.02025 + 0.010042 + 0.010125 + 0.009083 + 0.013417 + 0.010666 + 0.009917 + 0.010125 + 0.010417 + 0.010292 + 0.0125 + 0.010709 + 0.01075 + 0.010208 + 0.010458 + 0.011583 + 0.010625 + 0.013292 + 0.011625 + 0.01 + 0.008958 + 0.009417 + 0.016417 + 0.008875 + 0.01625 + 0.009042 + 0.008708 + 0.009042 + 0.00925 + 0.012459 + 0.00825 + 0.008542 + 0.008791 + 0.011417 + 0.010708 + 0.010917 + 0.0105 + 0.00825 + 0.00825 + 0.015042 + 0.008667 + 0.00975 + 0.008458 + 0.008083 + 0.00825 + 0.008709 + 0.008416 + 0.008417 + 0.008375 + 0.00875 + 0.008125 + 0.008084 + 0.008458 + 0.008167 + 0.007875 + 0.007709 + 0.00775 + 0.00825 + 0.007959 + 0.008375 + 0.007709 + 0.007833 + 0.008167 + 0.007958 + 0.008208 + 0.009375 + 0.007875 + 0.008042 + 0.007875 + 0.008208 + 0.008208 + 0.014083 + 0.008708 + 0.008625 + 0.008583 + 0.012375 + 0.00875 + 0.009042 + 0.008625 + 0.008667 + 0.008125 + 0.008333 + 0.008167 + 0.008667 + 0.01175 + 0.010792 + 0.010667 + 0.008917 + 0.010292 + 0.010083 + 0.008916 + 0.008333 + 0.008625 + 0.008334 + 0.009584 + 0.011959 + 0.011 + 0.01 + 0.00925 + 0.009042 + 0.008375 + 0.010209 + 0.011542 + 0.010666 + 0.014541 + 0.013042 + 0.011084 + 0.009875 + 0.00925 + 0.010166 + 0.01325 + 0.012292 + 0.011833 + 0.0115 + 0.044 + 0.042708 + 0.012834 + 0.019917 + 0.013166 + 0.010334 + 0.009625 + 0.008375 + 0.008625 + 0.008 + 0.008167 + 0.007917 + 0.008 + 0.00825 + 0.007792 + 0.009959 + 0.008708 + 0.008875 + 0.007042 + 0.006708 + 0.0105 + 0.008875 + 0.008584 + 0.0085 + 0.009 + 0.009333 + 0.008583 + 0.008542 + 0.008333 + 0.008 + 0.008625 + 0.021167 + 0.008958 + 0.009 + 0.008333 + 0.008292 + 0.008542 + 0.008417 + 0.008459 + 0.008333 + 0.008625 + 0.008375 + 0.009 + 0.009209 + 0.008209 + 0.008375 + 0.008625 + 0.008875 + 0.00875 + 0.008542 + 0.008375 + 0.008417 + 0.008375 + 0.008375 + 0.008208 + 0.008583 + 0.009875 + 0.008833 + 0.009125 + 0.008334 + 0.015875 + 0.009375 + 0.009333 + 0.011125 + 0.008958 + 0.009208 + 0.008958 + 0.008916 + 0.008917 + 0.016959 + 0.008833 + 0.010291 + 0.009292 + 0.010667 + 0.011125 + 0.009709 + 0.009042 + 0.009333 + 0.008791 + 0.009 + 0.009417 + 0.010709 + 0.009042 + 0.009083 + 0.00975 + 0.009625 + 0.009584 + 0.008959 + 0.009125 + 0.010584 + 0.0125 + 0.012792 + 0.010959 + 0.010375 + 0.009667 + 0.010125 + 0.01 + 0.0145 + 0.011834 + 0.0125 + 0.011583 + 0.011 + 0.010041 + 0.009625 + 0.012708 + 0.01025 + 0.00875 + 0.010209 + 0.017666 + 0.009958 + 0.010334 + 0.010709 + 0.010833 + 0.012333 + 0.010875 + 0.020708 + 0.012125 + 0.007875 + 0.010291 + 0.008584 + 0.01125 + 0.009917 + 0.010833 + 0.010833 + 0.01 + 0.010791 + 0.009833 + 0.0095 + 0.011334 + 0.011709 + 0.011459 + 0.012791 + 0.011208 + 0.012583 + 0.010709 + 0.010666 + 0.011042 + 0.011 + 0.010375 + 0.009583 + 0.016958 + 0.014375 + 0.009167 + 0.00925 + 0.009625 + 0.009792 + 0.009833 + 0.010125 + 0.009083 + 0.008583 + 0.009583 + 0.009458 + 0.010417 + 0.009333 + 0.0095 + 0.009875 + 0.017959 + 0.009458 + 0.00925 + 0.01 + 0.010042 + 0.010167 + 0.010541 + 0.009667 + 0.011166 + 0.011833 + 0.010875 + 0.012375 + 0.01425 + 0.012833 + 0.012125 + 0.011584 + 0.00975 + 0.009917 + 0.009875 + 0.024083 + 0.01025 + 0.010125 + 0.012791 + 0.010292 + 0.010083 + 0.009875 + 0.009667 + 0.009584 + 0.01 + 0.009917 + 0.01 + 0.01 + 0.009959 + 0.009584 + 0.011375 + 0.016292 + 0.01875 + 0.011292 + 0.010958 + 0.010625 + 0.017792 + 0.010375 + 0.020416 + 0.013708 + 0.011 + 0.010791 + 0.010292 + 0.010083 + 0.010625 + 0.016209 + 0.009625 + 0.008916 + 0.009792 + 0.009584 + 0.01075 + 0.008417 + 0.008708 + 0.016125 + 0.013792 + 0.009208 + 0.01175 + 0.007625 + 0.007334 + 0.00825 + 0.01725 + 0.009334 + 0.018167 + 0.009125 + 0.009041 + 0.018542 + 0.010041 + 0.009583 + 0.017417 + 0.01075 + 0.010166 + 0.013125 + 0.0095 + 0.009834 + 0.0105 + 0.010917 + 0.009625 + 0.009125 + 0.01025 + 0.010167 + 0.009875 + 0.010041 + 0.01025 + 0.00975 + 0.009875 + 0.009959 + 0.008917 + 0.009541 + 0.017875 + 0.0105 + 0.009541 + 0.011 + 0.018167 + 0.010292 + 0.010375 + 0.008792 + 0.009333 + 0.0095 + 0.010792 + 0.009791 + 0.0105 + 0.009916 + 0.010292 + 0.02025 + 0.015833 + 0.010375 + 0.010584 + 0.010584 + 0.009667 + 0.0095 + 0.037833 + 0.033625 + 0.014208 + 0.013375 + 0.01525 + 0.011166 + 0.010833 + 0.010209 + 0.009959 + 0.010417 + 0.0095 + 0.010041 + 0.01125 + 0.017625 + 0.012834 + 0.011209 + 0.010583 + 0.010375 + 0.010625 + 0.010792 + 0.010708 + 0.015292 + 0.010708 + 0.022875 + 0.014625 + 0.012583 + 0.010875 + 0.010875 + 0.01225 + 0.014834 + 0.014125 + 0.015791 + 0.013666 + 0.010458 + 0.010958 + 0.011167 + 0.012334 + 0.012875 + 0.012292 + 0.011709 + 0.010584 + 0.009541 + 0.01025 + 0.011708 + 0.011459 + 0.010334 + 0.013333 + 0.0085 + 0.008958 + 0.009 + 0.012583 + 0.010917 + 0.01025 + 0.011084 + 0.013166 + 0.012167 + 0.010833 + 0.010875 + 0.010625 + 0.012041 + 0.012667 + 0.012584 + 0.01325 + 0.012959 + 0.012875 + 0.010417 + 0.009709 + 0.009417 + 0.00925 + 0.009833 + 0.00975 + 0.010125 + 0.009917 + 0.01 + 0.010291 + 0.009625 + 0.019833 + 0.010458 + 0.009542 + 0.010291 + 0.010584 + 0.010333 + 0.011417 + 0.01175 + 0.01275 + 0.012833 + 0.013 + 0.013084 + 0.013292 + 0.01325 + 0.011708 + 0.012791 + 0.014083 + 0.014083 + 0.016 + 0.01525 + 0.0135 + 0.011875 + 0.010291 + 0.010167 + 0.010375 + 0.018416 + 0.010917 + 0.011167 + 0.012 + 0.013042 + 0.013083 + 0.013041 + 0.010958 + 0.010542 + 0.009 + 0.010291 + 0.010083 + 0.009375 + 0.009833 + 0.013291 + 0.012292 + 0.024166 + 0.010209 + 0.010709 + 0.009834 + 0.011167 + 0.0105 + 0.011459 + 0.012042 + 0.016958 + 0.013834 + 0.013166 + 0.012125 + 0.010291 + 0.01025 + 0.01025 + 0.012792 + 0.012166 + 0.011375 + 0.010083 + 0.0095 + 0.008584 + 0.008875 + 0.008417 + 0.008667 + 0.008416 + 0.008542 + 0.008167 + 0.0155 + 0.011042 + 0.014083 + 0.010042 + 0.010834 + 0.007458 + 0.007125 + 0.010125 + 0.009792 + 0.009541 + 0.009042 + 0.008916 + 0.008625 + 0.008542 + 0.009 + 0.008416 + 0.008417 + 0.008292 + 0.014291 + 0.008792 + 0.008459 + 0.008417 + 0.008 + 0.008542 + 0.0085 + 0.008667 + 0.008625 + 0.0085 + 0.008625 + 0.008583 + 0.008166 + 0.008709 + 0.008417 + 0.009 + 0.008208 + 0.008625 + 0.008583 + 0.009 + 0.008292 + 0.009 + 0.008375 + 0.0095 + 0.0085 + 0.00875 + 0.008291 + 0.009208 + 0.009 + 0.008958 + 0.02 + 0.009125 + 0.011375 + 0.01 + 0.0085 + 0.009167 + 0.0165 + 0.011209 + 0.012042 + 0.01125 + 0.0105 + 0.00975 + 0.009583 + 0.012708 + 0.0125 + 0.010791 + 0.013625 + 0.012208 + 0.011875 + 0.012 + 0.012042 + 0.010416 + 0.009083 + 0.008583 + 0.008792 + 0.011416 + 0.013292 + 0.0125 + 0.012416 + 0.014667 + 0.011584 + 0.015666 + 0.017584 + 0.022375 + 0.014583 + 0.011708 + 0.009708 + 0.009541 + 0.0145 + 0.014375 + 0.012542 + 0.01075 + 0.012 + 0.012042 + 0.015875 + 0.011792 + 0.010958 + 0.010291 + 0.009125 + 0.010708 + 0.010042 + 0.011208 + 0.011375 + 0.011959 + 0.022084 + 0.01425 + 0.010167 + 0.008875 + 0.007791 + 0.010375 + 0.009167 + 0.009166 + 0.009917 + 0.012167 + 0.012416 + 0.010292 + 0.010667 + 0.01025 + 0.009917 + 0.011166 + 0.010333 + 0.012333 + 0.012375 + 0.011958 + 0.011042 + 0.01175 + 0.012375 + 0.010708 + 0.010167 + 0.010459 + 0.01 + 0.009708 + 0.010041 + 0.009542 + 0.009334 + 0.011584 + 0.01225 + 0.011417 + 0.011709 + 0.01 + 0.009708 + 0.00975 + 0.021208 + 0.011875 + 0.010167 + 0.011667 + 0.010916 + 0.013042 + 0.012958 + 0.012792 + 0.011167 + 0.023959 + 0.013667 + 0.011542 + 0.010375 + 0.011959 + 0.012542 + 0.011375 + 0.010917 + 0.01325 + 0.021875 + 0.022666 + 0.0125 + 0.011209 + 0.01075 + 0.011 + 0.01075 + 0.012834 + 0.010917 + 0.011208 + 0.011292 + 0.0105 + 0.01125 + 0.013125 + 0.022417 + 0.014333 + 0.011583 + 0.011292 + 0.025 + 0.017375 + 0.013541 + 0.015959 + 0.014292 + 0.015791 + 0.015417 + 0.0135 + 0.014792 + 0.013917 + 0.019042 + 0.017375 + 0.017292 + 0.012458 + 0.013 + 0.012917 + 0.016792 + 0.013 + 0.011959 + 0.018417 + 0.009958 + 0.009792 + 0.008625 + 0.00925 + 0.009167 + 0.019708 + 0.011959 + 0.014166 + 0.010125 + 0.009833 + 0.008458 + 0.010458 + 0.009709 + 0.009959 + 0.0105 + 0.010083 + 0.009917 + 0.009917 + 0.010125 + 0.00925 + 0.02125 + 0.010292 + 0.011416 + 0.013458 + 0.01325 + 0.013333 + 0.010958 + 0.0125 + 0.01325 + 0.014 + 0.012542 + 0.025166 + 0.012125 + 0.009667 + 0.010166 + 0.009542 + 0.009375 + 0.00975 + 0.009875 + 0.009334 + 0.009708 + 0.01025 + 0.009834 + 0.009792 + 0.009458 + 0.009375 + 0.009875 + 0.010125 + 0.011125 + 0.009875 + 0.010458 + 0.013417 + 0.010292 + 0.010833 + 0.020625 + 0.024417 + 0.011167 + 0.01075 + 0.009959 + 0.009542 + 0.00975 + 0.009583 + 0.010292 + 0.010375 + 0.01125 + 0.010542 + 0.009875 + 0.017083 + 0.010166 + 0.010958 + 0.012875 + 0.011208 + 0.011 + 0.012 + 0.013917 + 0.020625 + 0.0185 + 0.010375 + 0.010917 + 0.009916 + 0.021333 + 0.010208 + 0.010625 + 0.010416 + 0.01025 + 0.021375 + 0.015458 + 0.014958 + 0.011708 + 0.011333 + 0.011208 + 0.011375 + 0.014542 + 0.026917 + 0.021625 + 0.013917 + 0.011292 + 0.010292 + 0.020917 + 0.00975 + 0.009875 + 0.009667 + 0.009875 + 0.010084 + 0.009334 + 0.010291 + 0.010542 + 0.01325 + 0.023 + 0.014666 + 0.010333 + 0.00975 + 0.017291 + 0.010709 + 0.011041 + 0.009792 + 0.009458 + 0.010166 + 0.010208 + 0.009792 + 0.010208 + 0.009792 + 0.009958 + 0.009292 + 0.009417 + 0.009625 + 0.00925 + 0.009875 + 0.009916 + 0.009458 + 0.008708 + 0.009625 + 0.009417 + 0.009 + 0.009542 + 0.009375 + 0.008875 + 0.010208 + 0.009458 + 0.009 + 0.009583 + 0.009708 + 0.008792 + 0.009708 + 0.00975 + 0.009958 + 0.009541 + 0.009459 + 0.01025 + 0.018459 + 0.010834 + 0.0115 + 0.009875 + 0.010125 + 0.009708 + 0.010125 + 0.015708 + 0.012583 + 0.012125 + 0.012709 + 0.012125 + 0.013 + 0.012459 + 0.012917 + 0.01125 + 0.010041 + 0.01125 + 0.013167 + 0.012 + 0.011667 + 0.0105 + 0.010166 + 0.010125 + 0.009958 + 0.009792 + 0.012458 + 0.015208 + 0.011375 + 0.010958 + 0.012417 + 0.011666 + 0.012333 + 0.014125 + 0.013375 + 0.012625 + 0.012459 + 0.01425 + 0.013792 + 0.018875 + 0.01225 + 0.009 + 0.011209 + 0.010834 + 0.011833 + 0.014958 + 0.013958 + 0.014041 + 0.013458 + 0.011584 + 0.011709 + 0.011334 + 0.011292 + 0.010791 + 0.011417 + 0.011625 + 0.011792 + 0.011791 + 0.013 + 0.013167 + 0.012667 + 0.008875 + 0.007916 + 0.009625 + 0.009125 + 0.0095 + 0.00925 + 0.009833 + 0.009583 + 0.009583 + 0.009833 + 0.009333 + 0.00975 + 0.009292 + 0.009875 + 0.009958 + 0.00975 + 0.009667 + 0.009333 + 0.009958 + 0.009541 + 0.009875 + 0.010417 + 0.009958 + 0.009833 + 0.009333 + 0.0095 + 0.009958 + 0.009708 + 0.00975 + 0.009584 + 0.0095 + 0.010042 + 0.0095 + 0.0095 + 0.009625 + 0.009292 + 0.012334 + 0.009875 + 0.01225 + 0.013291 + 0.012917 + 0.013 + 0.012083 + 0.009083 + 0.009375 + 0.009875 + 0.010292 + 0.009625 + 0.010292 + 0.009625 + 0.009584 + 0.012292 + 0.013083 + 0.015625 + 0.010041 + 0.010208 + 0.009417 + 0.009708 + 0.009541 + 0.01 + 0.009542 + 0.010167 + 0.009792 + 0.011209 + 0.018875 + 0.010208 + 0.009875 + 0.009542 + 0.012292 + 0.012625 + 0.021084 + 0.018459 + 0.011459 + 0.0105 + 0.01025 + 0.011875 + 0.013833 + 0.014167 + 0.011666 + 0.011375 + 0.011667 + 0.013584 + 0.012166 + 0.013792 + 0.013834 + 0.013625 + 0.013 + 0.0135 + 0.009667 + 0.009625 + 0.010042 + 0.018125 + 0.011208 + 0.011459 + 0.011 + 0.018584 + 0.011333 + 0.011083 + 0.013208 + 0.009667 + 0.008 + 0.007209 + 0.009208 + 0.008666 + 0.00875 + 0.009375 + 0.009375 + 0.009291 + 0.008791 + 0.008833 + 0.008958 + 0.009125 + 0.008958 + 0.008708 + 0.008708 + 0.009666 + 0.010834 + 0.009125 + 0.00875 + 0.008416 + 0.0085 + 0.009 + 0.0115 + 0.011042 + 0.010834 + 0.015125 + 0.011125 + 0.011666 + 0.010166 + 0.009416 + 0.009208 + 0.012458 + 0.012875 + 0.009709 + 0.00925 + 0.01 + 0.01125 + 0.011333 + 0.012167 + 0.01175 + 0.011292 + 0.009292 + 0.008166 + 0.007875 + 0.008458 + 0.008583 + 0.011 + 0.010458 + 0.010208 + 0.008208 + 0.013042 + 0.008458 + 0.008458 + 0.00825 + 0.008125 + 0.008375 + 0.008375 + 0.007875 + 0.008 + 0.007792 + 0.0085 + 0.008084 + 0.0085 + 0.014833 + 0.007917 + 0.008084 + 0.011959 + 0.009584 + 0.00925 + 0.011292 + 0.011042 + 0.013416 + 0.012 + 0.011291 + 0.012083 + 0.012833 + 0.015375 + 0.010708 + 0.009958 + 0.010375 + 0.0095 + 0.010458 + 0.010458 + 0.013375 + 0.011709 + 0.010666 + 0.0105 + 0.010291 + 0.01025 + 0.008458 + 0.008375 + 0.008667 + 0.007958 + 0.008042 + 0.008375 + 0.008041 + 0.008167 + 0.008416 + 0.009125 + 0.008458 + 0.008333 + 0.007083 + 0.009708 + 0.00875 + 0.00875 + 0.01075 + 0.010209 + 0.009292 + 0.009125 + 0.00875 + 0.008875 + 0.008 + 0.008166 + 0.008416 + 0.008542 + 0.008333 + 0.008125 + 0.009916 + 0.010708 + 0.01075 + 0.010708 + 0.010458 + 0.008125 + 0.008083 + 0.008041 + 0.008583 + 0.008167 + 0.008416 + 0.0085 + 0.008042 + 0.008 + 0.007958 + 0.008292 + 0.008375 + 0.008041 + 0.008375 + 0.008459 + 0.008125 + 0.008333 + 0.009042 + 0.008583 + 0.008541 + 0.008458 + 0.008416 + 0.008584 + 0.008417 + 0.011833 + 0.012083 + 0.011958 + 0.011458 + 0.00925 + 0.008625 + 0.008834 + 0.008583 + 0.008666 + 0.008792 + 0.00875 + 0.0085 + 0.008666 + 0.008792 + 0.008792 + 0.008875 + 0.00875 + 0.008209 + 0.0085 + 0.008458 + 0.014916 + 0.01175 + 0.011875 + 0.0115 + 0.009625 + 0.009 + 0.008208 + 0.009583 + 0.009333 + 0.009792 + 0.009375 + 0.012542 + 0.012791 + 0.012292 + 0.011375 + 0.01 + 0.0095 + 0.008958 + 0.012875 + 0.012125 + 0.012333 + 0.013208 + 0.010125 + 0.007875 + 0.008834 + 0.007792 + 0.007875 + 0.00825 + 0.0095 + 0.010125 + 0.009125 + 0.008041 + 0.008625 + 0.009291 + 0.00925 + 0.008792 + 0.007208 + 0.009416 + 0.009209 + 0.008834 + 0.01125 + 0.014084 + 0.0095 + 0.009792 + 0.009 + 0.008667 + 0.008416 + 0.008458 + 0.0085 + 0.008125 + 0.0085 + 0.00875 + 0.008541 + 0.010375 + 0.010584 + 0.011334 + 0.011084 + 0.011167 + 0.010875 + 0.008333 + 0.008792 + 0.008709 + 0.008458 + 0.008625 + 0.010125 + 0.009666 + 0.008417 + 0.007916 + 0.008375 + 0.009292 + 0.008166 + 0.007667 + 0.00825 + 0.008916 + 0.0085 + 0.008667 + 0.008667 + 0.0085 + 0.008792 + 0.008833 + 0.009083 + 0.008541 + 0.008208 + 0.008333 + 0.011667 + 0.011 + 0.010667 + 0.008583 + 0.008375 + 0.009042 + 0.008916 + 0.008667 + 0.009042 + 0.00875 + 0.008417 + 0.008541 + 0.0085 + 0.009 + 0.008833 + 0.00825 + 0.008583 + 0.00925 + 0.009166 + 0.0085 + 0.0115 + 0.011084 + 0.010083 + 0.00925 + 0.018334 + 0.010584 + 0.011167 + 0.010416 + 0.013167 + 0.012417 + 0.012167 + 0.012042 + 0.012625 + 0.013667 + 0.012833 + 0.015 + 0.013709 + 0.013167 + 0.012333 + 0.00825 + 0.008 + 0.0085 + 0.009083 + 0.008208 + 0.007792 + 0.007625 + 0.007625 + 0.007625 + 0.007291 + 0.008583 + 0.008084 + 0.00675 + 0.006375 + 0.008709 + 0.008042 + 0.0085 + 0.00875 + 0.011375 + 0.012042 + 0.009875 + 0.009833 + 0.008917 + 0.009 + 0.009459 + 0.01025 + 0.012167 + 0.026334 + 0.018833 + 0.0115 + 0.009833 + 0.010125 + 0.009667 + 0.010583 + 0.009208 + 0.010458 + 0.009541 + 0.009 + 0.009166 + 0.012417 + 0.00975 + 0.00975 + 0.009916 + 0.009083 + 0.009458 + 0.009125 + 0.009375 + 0.018083 + 0.009958 + 0.010125 + 0.010042 + 0.010584 + 0.012375 + 0.010375 + 0.010208 + 0.012292 + 0.012667 + 0.011084 + 0.010542 + 0.011834 + 0.0195 + 0.016291 + 0.010959 + 0.011917 + 0.011041 + 0.010417 + 0.011334 + 0.011958 + 0.010625 + 0.01075 + 0.01275 + 0.010709 + 0.011 + 0.011 + 0.03925 + 0.015 + 0.013 + 0.016917 + 0.013041 + 0.015959 + 0.013334 + 0.011417 + 0.012125 + 0.012584 + 0.016209 + 0.013917 + 0.013166 + 0.012167 + 0.010958 + 0.01475 + 0.011417 + 0.011042 + 0.011333 + 0.0115 + 0.011959 + 0.011584 + 0.012334 + 0.011208 + 0.011083 + 0.011041 + 0.009167 + 0.00925 + 0.009709 + 0.009541 + 0.011208 + 0.012167 + 0.010166 + 0.010208 + 0.009625 + 0.010375 + 0.013584 + 0.009417 + 0.007833 + 0.008208 + 0.012291 + 0.0105 + 0.009458 + 0.010042 + 0.009791 + 0.010333 + 0.011042 + 0.010459 + 0.010833 + 0.011792 + 0.011208 + 0.012875 + 0.011583 + 0.012583 + 0.011583 + 0.011833 + 0.012083 + 0.0125 + 0.027042 + 0.018125 + 0.012917 + 0.012958 + 0.011291 + 0.012167 + 0.012084 + 0.012125 + 0.014209 + 0.014375 + 0.013083 + 0.013167 + 0.013625 + 0.013292 + 0.011125 + 0.01075 + 0.010458 + 0.011208 + 0.012542 + 0.011083 + 0.011833 + 0.014291 + 0.01325 + 0.01325 + 0.013084 + 0.012625 + 0.01025 + 0.009791 + 0.010625 + 0.012292 + 0.0105 + 0.010709 + 0.015125 + 0.014292 + 0.013209 + 0.014917 + 0.01125 + 0.013875 + 0.011875 + 0.013334 + 0.013166 + 0.012625 + 0.01475 + 0.01175 + 0.010667 + 0.010333 + 0.009833 + 0.009792 + 0.010542 + 0.010083 + 0.0095 + 0.010583 + 0.009666 + 0.016208 + 0.014708 + 0.01425 + 0.012083 + 0.011042 + 0.010875 + 0.015083 + 0.014208 + 0.014917 + 0.013584 + 0.011041 + 0.010875 + 0.010333 + 0.014333 + 0.013875 + 0.013167 + 0.011417 + 0.009625 + 0.01125 + 0.011458 + 0.010791 + 0.013 + 0.0085 + 0.007916 + 0.007625 + 0.009042 + 0.00675 + 0.006875 + 0.006875 + 0.009125 + 0.008625 + 0.00825 + 0.008375 + 0.0085 + 0.008375 + 0.008416 + 0.008917 + 0.009041 + 0.008792 + 0.008291 + 0.008417 + 0.008375 + 0.008208 + 0.008333 + 0.008667 + 0.008167 + 0.008625 + 0.009292 + 0.008291 + 0.008084 + 0.0085 + 0.007875 + 0.008333 + 0.008417 + 0.010125 + 0.010792 + 0.011041 + 0.010833 + 0.009375 + 0.008583 + 0.008208 + 0.008333 + 0.008333 + 0.008084 + 0.008 + 0.009042 + 0.008584 + 0.008583 + 0.008541 + 0.008958 + 0.008375 + 0.008333 + 0.008167 + 0.009125 + 0.008417 + 0.008333 + 0.008791 + 0.008334 + 0.008541 + 0.008417 + 0.013875 + 0.011667 + 0.011166 + 0.011667 + 0.011208 + 0.008416 + 0.008333 + 0.008 + 0.009 + 0.008292 + 0.008541 + 0.008333 + 0.007959 + 0.008542 + 0.008416 + 0.008333 + 0.008167 + 0.0085 + 0.008458 + 0.008417 + 0.015083 + 0.012959 + 0.01175 + 0.012083 + 0.009875 + 0.009042 + 0.012083 + 0.011291 + 0.012208 + 0.012583 + 0.009416 + 0.009083 + 0.009167 + 0.011916 + 0.012333 + 0.012 + 0.010333 + 0.008541 + 0.007292 + 0.007791 + 0.007458 + 0.007708 + 0.007542 + 0.007708 + 0.007166 + 0.007583 + 0.007833 + 0.007208 + 0.006417 + 0.006834 + 0.008375 + 0.00825 + 0.008 + 0.007875 + 0.008291 + 0.008083 + 0.008459 + 0.008375 + 0.007709 + 0.008542 + 0.012417 + 0.008625 + 0.008125 + 0.0145 + 0.013 + 0.008209 + 0.008042 + 0.008542 + 0.008167 + 0.008208 + 0.010708 + 0.008542 + 0.008917 + 0.008791 + 0.008208 + 0.008708 + 0.008208 + 0.008708 + 0.009334 + 0.010709 + 0.009084 + 0.009459 + 0.009375 + 0.00925 + 0.0095 + 0.009833 + 0.012666 + 0.029708 + 0.012459 + 0.012584 + 0.01025 + 0.009542 + 0.009125 + 0.010625 + 0.009625 + 0.010917 + 0.009375 + 0.009959 + 0.008334 + 0.009166 + 0.016584 + 0.009167 + 0.01675 + 0.011459 + 0.010417 + 0.013542 + 0.0125 + 0.012625 + 0.012625 + 0.009209 + 0.009708 + 0.009083 + 0.009208 + 0.009458 + 0.009125 + 0.008875 + 0.009875 + 0.008583 + 0.009792 + 0.009875 + 0.010708 + 0.012917 + 0.012292 + 0.011541 + 0.010125 + 0.010083 + 0.009542 + 0.012084 + 0.01075 + 0.011708 + 0.0095 + 0.009958 + 0.009791 + 0.009542 + 0.012333 + 0.012083 + 0.00975 + 0.008041 + 0.008167 + 0.007834 + 0.018334 + 0.008959 + 0.009375 + 0.008625 + 0.008166 + 0.008083 + 0.009333 + 0.008625 + 0.008083 + 0.007083 + 0.010667 + 0.011875 + 0.011708 + 0.011917 + 0.010375 + 0.009042 + 0.009125 + 0.009417 + 0.008917 + 0.008708 + 0.011333 + 0.012791 + 0.011875 + 0.009666 + 0.009708 + 0.00875 + 0.008875 + 0.008166 + 0.009333 + 0.009167 + 0.008584 + 0.00875 + 0.009 + 0.009083 + 0.008792 + 0.008916 + 0.00925 + 0.009125 + 0.008875 + 0.008833 + 0.008833 + 0.008416 + 0.008708 + 0.016667 + 0.009791 + 0.008792 + 0.011292 + 0.010041 + 0.009 + 0.008583 + 0.009208 + 0.009041 + 0.009458 + 0.009584 + 0.009375 + 0.009083 + 0.008792 + 0.009084 + 0.009125 + 0.009333 + 0.009417 + 0.0095 + 0.0095 + 0.009 + 0.009292 + 0.008958 + 0.009166 + 0.011917 + 0.011625 + 0.011042 + 0.009833 + 0.008959 + 0.009416 + 0.009375 + 0.010708 + 0.009917 + 0.0095 + 0.010042 + 0.009083 + 0.009458 + 0.009792 + 0.012958 + 0.012334 + 0.012125 + 0.011208 + 0.010542 + 0.010542 + 0.012875 + 0.012 + 0.011583 + 0.009875 + 0.010416 + 0.0105 + 0.011208 + 0.012583 + 0.012583 + 0.009166 + 0.008333 + 0.0085 + 0.008459 + 0.009166 + 0.008917 + 0.007791 + 0.008375 + 0.007708 + 0.008667 + 0.010917 + 0.008791 + 0.00925 + 0.009209 + 0.012458 + 0.012166 + 0.011625 + 0.011458 + 0.011791 + 0.011375 + 0.01 + 0.009083 + 0.008792 + 0.008666 + 0.008833 + 0.008959 + 0.008458 + 0.008458 + 0.008583 + 0.008209 + 0.008833 + 0.008791 + 0.009334 + 0.008875 + 0.008709 + 0.0085 + 0.009458 + 0.011 + 0.009333 + 0.008667 + 0.00875 + 0.008583 + 0.011625 + 0.012083 + 0.012042 + 0.011917 + 0.008916 + 0.011292 + 0.011958 + 0.0115 + 0.011125 + 0.009625 + 0.009958 + 0.012666 + 0.011917 + 0.010167 + 0.00975 + 0.011959 + 0.01225 + 0.011916 + 0.0125 + 0.012375 + 0.009792 + 0.009208 + 0.009958 + 0.009458 + 0.008917 + 0.007916 + 0.009208 + 0.011125 + 0.011333 + 0.010667 + 0.008833 + 0.009083 + 0.009458 + 0.008208 + 0.009167 + 0.010041 + 0.008667 + 0.009125 + 0.009167 + 0.009542 + 0.008875 + 0.008833 + 0.008834 + 0.010167 + 0.011958 + 0.012 + 0.011791 + 0.01 + 0.009542 + 0.009625 + 0.009417 + 0.011791 + 0.011583 + 0.009583 + 0.010208 + 0.009917 + 0.009584 + 0.012666 + 0.012917 + 0.011583 + 0.011042 + 0.009292 + 0.009166 + 0.008542 + 0.007833 + 0.008333 + 0.008083 + 0.00825 + 0.012625 + 0.01 + 0.009042 + 0.009833 + 0.010792 + 0.008459 + 0.008459 + 0.009375 + 0.009333 + 0.010792 + 0.011834 + 0.012792 + 0.012125 + 0.01075 + 0.010083 + 0.009666 + 0.008584 + 0.008458 + 0.008625 + 0.008583 + 0.011208 + 0.011167 + 0.011958 + 0.011625 + 0.009417 + 0.011292 + 0.011792 + 0.01025 + 0.011208 + 0.01 + 0.010959 + 0.010167 + 0.010708 + 0.009875 + 0.009917 + 0.010625 + 0.009667 + 0.009292 + 0.032166 + 0.025792 + 0.021041 + 0.013083 + 0.012208 + 0.020042 + 0.013625 + 0.016125 + 0.016167 + 0.015 + 0.016083 + 0.012625 + 0.010667 + 0.010625 + 0.009917 + 0.010667 + 0.011041 + 0.01125 + 0.011458 + 0.01125 + 0.011708 + 0.010041 + 0.010459 + 0.009666 + 0.018125 + 0.013375 + 0.012833 + 0.012833 + 0.012417 + 0.010625 + 0.00975 + 0.011167 + 0.010041 + 0.010042 + 0.009542 + 0.010167 + 0.009791 + 0.012417 + 0.0105 + 0.014875 + 0.013583 + 0.013792 + 0.012542 + 0.010583 + 0.010084 + 0.010625 + 0.013166 + 0.014417 + 0.013417 + 0.012291 + 0.010459 + 0.010334 + 0.013167 + 0.011791 + 0.011333 + 0.0125 + 0.00925], + :filter-tx-naive + [0.0475 + 0.035333 + 0.033875 + 0.037958 + 0.037334 + 0.035917 + 0.03475 + 0.037667 + 0.038875 + 0.042 + 0.044458 + 0.036333 + 0.040875 + 0.036667 + 0.033792 + 0.039458 + 0.044833 + 0.037791 + 0.03325 + 0.035875 + 0.0395 + 0.034291 + 0.033042 + 0.045375 + 0.034125 + 0.033042 + 0.042042 + 0.037167 + 0.033375 + 0.039042 + 0.036792 + 0.035792 + 0.0425 + 0.03725 + 0.03525 + 0.036625 + 0.037417 + 0.054042 + 0.042208 + 0.035791 + 0.034625 + 0.033917 + 0.04175 + 0.043542 + 0.034417 + 0.043167 + 0.050833 + 0.039708 + 0.039709 + 0.047042 + 0.053541 + 0.05175 + 0.047458 + 0.0355 + 0.035625 + 0.037542 + 0.045625 + 0.046875 + 0.044416 + 0.048292 + 0.049375 + 0.047541 + 0.040208 + 0.045792 + 0.039208 + 0.038583 + 0.037833 + 0.038417 + 0.034584 + 0.034541 + 0.038291 + 0.037791 + 0.037208 + 0.03875 + 0.0495 + 0.0445 + 0.040833 + 0.039208 + 0.055375 + 0.035458 + 0.047916 + 0.04525 + 0.041666 + 0.056834 + 0.035 + 0.033917 + 0.0355 + 0.058041 + 0.041709 + 0.038 + 0.03625 + 0.03275 + 0.032417 + 0.03325 + 0.031708 + 0.032792 + 0.044792 + 0.036333 + 0.042167 + 0.037375 + 0.036375 + 0.03675 + 0.035333 + 0.040291 + 0.036875 + 0.038292 + 0.033833 + 0.042417 + 0.044667 + 0.044958 + 0.035625 + 0.032417 + 0.083167 + 0.042917 + 0.031083 + 0.032291 + 0.031459 + 0.030583 + 0.031916 + 0.032791 + 0.040958 + 0.035584 + 0.031667 + 0.032417 + 0.032625 + 0.03175 + 0.034334 + 0.046333 + 0.034959 + 0.032916 + 0.033125 + 0.031833 + 0.032708 + 0.032291 + 0.032083 + 0.032667 + 0.032584 + 0.032208 + 0.033083 + 0.0325 + 0.032208 + 0.076375 + 0.043167 + 0.033875 + 0.033542 + 0.032875 + 0.035125 + 0.037333 + 0.047166 + 0.034625 + 0.034583 + 0.034167 + 0.035167 + 0.035167 + 0.034 + 0.039416 + 0.035959 + 0.038875 + 0.03475 + 0.041291 + 0.041083 + 0.043916 + 0.040334 + 0.034667 + 0.034667 + 0.032167 + 0.033333 + 0.033792 + 0.0325 + 0.033542 + 0.033667 + 0.033 + 0.034708 + 0.034875 + 0.044041 + 0.034833 + 0.037334 + 0.035708 + 0.041 + 0.034208 + 0.048583 + 0.036875 + 0.037292 + 0.039208 + 0.049167 + 0.044792 + 0.039166 + 0.056959 + 0.042666 + 0.038916 + 0.0345 + 0.032375 + 0.035875 + 0.034 + 0.031417 + 0.032584 + 0.03425 + 0.037292 + 0.032583 + 0.034833 + 0.035917 + 0.035709 + 0.044333 + 0.0345 + 0.033542 + 0.032584 + 0.033917 + 0.033167 + 0.0415 + 0.039083 + 0.034458 + 0.040625 + 0.037333 + 0.0375 + 0.032791 + 0.034084 + 0.044292 + 0.037 + 0.034375 + 0.034 + 0.034458 + 0.034375 + 0.038208 + 0.074625 + 0.048333 + 0.048708 + 0.052334 + 0.037458 + 0.036916 + 0.039 + 0.036375 + 0.034083 + 0.035792 + 0.03375 + 0.032916 + 0.037416 + 0.036125 + 0.035333 + 0.049833 + 0.036125 + 0.041625 + 0.035041 + 0.034458 + 0.033667 + 0.033625 + 0.032958 + 0.0345 + 0.036083 + 0.046875 + 0.035041 + 0.037458 + 0.033625 + 0.037541 + 0.03425 + 0.033625 + 0.041667 + 0.036667 + 0.040833 + 0.034333 + 0.033833 + 0.03425 + 0.033292 + 0.032417 + 0.032959 + 0.034042 + 0.036125 + 0.037834 + 0.03425 + 0.036541 + 0.034125 + 0.04625 + 0.036584 + 0.04525 + 0.039583 + 0.038416 + 0.042417 + 0.038958 + 0.071334 + 0.052084 + 0.048166 + 0.054666 + 0.040375 + 0.040208 + 0.038542 + 0.038417 + 0.037166 + 0.0385 + 0.038625 + 0.038542 + 0.038625 + 0.048042 + 0.052333 + 0.039083 + 0.058292 + 0.035667 + 0.036833 + 0.037875 + 0.045791 + 0.045917 + 0.0415 + 0.034542 + 0.037167 + 0.03375 + 0.034333 + 0.034916 + 0.049125 + 0.041209 + 0.036458 + 0.044625 + 0.034417 + 0.034458 + 0.033167 + 0.034667 + 0.054542 + 0.051208 + 0.03875 + 0.041208 + 0.037292 + 0.043334 + 0.047458 + 0.035958 + 0.03425 + 0.03925 + 0.035292 + 0.035208 + 0.044667 + 0.040167 + 0.034292 + 0.033292 + 0.038167 + 0.044375 + 0.033083 + 0.034458 + 0.038708 + 0.043 + 0.034375 + 0.036875 + 0.03275 + 0.0365 + 0.033833 + 0.03375 + 0.034375 + 0.040375 + 0.034167 + 0.033917 + 0.033 + 0.033958 + 0.039875 + 0.045708 + 0.03525 + 0.03475 + 0.033291 + 0.034791 + 0.033875 + 0.040541 + 0.042458 + 0.035458 + 0.033791 + 0.034625 + 0.034708 + 0.034167 + 0.036083 + 0.036625 + 0.035333 + 0.036583 + 0.034834 + 0.037708 + 0.047042 + 0.032459 + 0.033375 + 0.033458 + 0.041166 + 0.035791 + 0.033458 + 0.043167 + 0.037 + 0.041167 + 0.033209 + 0.034792 + 0.033708 + 0.041875 + 0.0345 + 0.033542 + 0.034292 + 0.040917 + 0.034125 + 0.035333 + 0.040708 + 0.033 + 0.033792 + 0.033125 + 0.031709 + 0.032375 + 0.036083 + 0.031416 + 0.032209 + 0.0315 + 0.034875 + 0.040125 + 0.033333 + 0.039791 + 0.044375 + 0.044834 + 0.045833 + 0.046125 + 0.044875 + 0.035792 + 0.033042 + 0.032292 + 0.033167 + 0.034084 + 0.033458 + 0.033375 + 0.032792 + 0.03275 + 0.038334 + 0.033458 + 0.032334 + 0.033333 + 0.033416 + 0.036416 + 0.037458 + 0.031833 + 0.033208 + 0.033208 + 0.032709 + 0.032792 + 0.033666 + 0.032125 + 0.03325 + 0.036208 + 0.035291 + 0.033208 + 0.033375 + 0.032583 + 0.033084 + 0.033542 + 0.031834 + 0.034333 + 0.03325 + 0.033042 + 0.045625 + 0.039125 + 0.0335 + 0.033208 + 0.034959 + 0.034 + 0.044667 + 0.033459 + 0.034791 + 0.034833 + 0.033792 + 0.034417 + 0.040458 + 0.033292 + 0.033584 + 0.032667 + 0.0355 + 0.033792 + 0.033834 + 0.0335 + 0.033375 + 0.032666 + 0.034375 + 0.034417 + 0.032292 + 0.033667 + 0.032708 + 0.032917 + 0.03275 + 0.03375 + 0.041375 + 0.034042 + 0.033959 + 0.033708 + 0.034416 + 0.034541 + 0.033416 + 0.035084 + 0.040292 + 0.034792 + 0.03425 + 0.039625 + 0.03475 + 0.033834 + 0.032667 + 0.0435 + 0.033833 + 0.033625 + 0.037042 + 0.035542 + 0.030958 + 0.033375 + 0.042625 + 0.038833 + 0.046708 + 0.034625 + 0.032125 + 0.046834 + 0.044125 + 0.039459 + 0.036416 + 0.047166 + 0.035833 + 0.035208 + 0.044167 + 0.032667 + 0.033 + 0.0325 + 0.032917 + 0.039333 + 0.033625 + 0.033167 + 0.033542 + 0.040958 + 0.033125 + 0.034 + 0.032917 + 0.033375 + 0.034125 + 0.033041 + 0.033208 + 0.032708 + 0.034667 + 0.042333 + 0.033417 + 0.033333 + 0.033375 + 0.032333 + 0.033333 + 0.033334 + 0.032333 + 0.032958 + 0.033292 + 0.031875 + 0.033583 + 0.033292 + 0.03175 + 0.036208 + 0.035417 + 0.03375 + 0.03275 + 0.032458 + 0.033417 + 0.034417 + 0.03225 + 0.03325 + 0.035417 + 0.035584 + 0.037 + 0.034208 + 0.032709 + 0.035333 + 0.034583 + 0.033042 + 0.03525 + 0.03325 + 0.034458 + 0.036375 + 0.034083 + 0.033291 + 0.033416 + 0.032125 + 0.033625 + 0.035833 + 0.034833 + 0.036833 + 0.034792 + 0.040125 + 0.032791 + 0.032791 + 0.032583 + 0.036334 + 0.034708 + 0.049333 + 0.038292 + 0.035209 + 0.033625 + 0.034209 + 0.034333 + 0.034291 + 0.03975 + 0.034458 + 0.039958 + 0.033917 + 0.033125 + 0.033583 + 0.033542 + 0.04125 + 0.0345 + 0.032083 + 0.0315 + 0.034417 + 0.0325 + 0.031625 + 0.032334 + 0.032375 + 0.031667 + 0.035917 + 0.031792 + 0.031958 + 0.033917 + 0.032458 + 0.033042 + 0.033666 + 0.032333 + 0.032 + 0.033083 + 0.032375 + 0.034084 + 0.033958 + 0.032084 + 0.033625 + 0.034 + 0.068958 + 0.045458 + 0.034084 + 0.032417 + 0.037833 + 0.034583 + 0.033334 + 0.033125 + 0.032666 + 0.033625 + 0.03625 + 0.032541 + 0.033625 + 0.033542 + 0.033 + 0.033208 + 0.033083 + 0.032333 + 0.034 + 0.033166 + 0.032458 + 0.033542 + 0.034 + 0.032166 + 0.044292 + 0.043833 + 0.033583 + 0.033708 + 0.033167 + 0.033833 + 0.033167 + 0.035708 + 0.036416 + 0.039583 + 0.037334 + 0.042666 + 0.037625 + 0.034083 + 0.034583 + 0.034833 + 0.035667 + 0.034959 + 0.033292 + 0.039042 + 0.040625 + 0.033791 + 0.0335 + 0.034 + 0.0325 + 0.033 + 0.035833 + 0.039166 + 0.037541 + 0.039083 + 0.035458 + 0.035375 + 0.036541 + 0.039125 + 0.036583 + 0.036333 + 0.03675 + 0.039416 + 0.049041 + 0.036792 + 0.036459 + 0.034167 + 0.033709 + 0.034667 + 0.033792 + 0.042583 + 0.034125 + 0.037167 + 0.035125 + 0.033667 + 0.033167 + 0.040542 + 0.034125 + 0.037875 + 0.0325 + 0.046666 + 0.043 + 0.049167 + 0.042542 + 0.039667 + 0.046542 + 0.036917 + 0.041417 + 0.055542 + 0.038833 + 0.036042 + 0.033167 + 0.032292 + 0.032792 + 0.033166 + 0.031791 + 0.033291 + 0.032709 + 0.031625 + 0.032292 + 0.033083 + 0.03175 + 0.033 + 0.044375 + 0.039625 + 0.043292 + 0.037042 + 0.042709 + 0.042042 + 0.034833 + 0.034209 + 0.036458 + 0.03625 + 0.034541 + 0.044125 + 0.032291 + 0.033083 + 0.032625 + 0.039375 + 0.033959 + 0.033417 + 0.032875 + 0.036083 + 0.035958 + 0.040667 + 0.038292 + 0.033959 + 0.034083 + 0.033333 + 0.039709 + 0.038667 + 0.039375 + 0.032667 + 0.033916 + 0.033167 + 0.035875 + 0.044542 + 0.035042 + 0.032959 + 0.033292 + 0.034083 + 0.036459 + 0.038208 + 0.050041 + 0.034208 + 0.045292 + 0.0365 + 0.034 + 0.043875 + 0.040583 + 0.033709 + 0.033542 + 0.033792 + 0.035375 + 0.041625 + 0.033583 + 0.035125 + 0.033375 + 0.033 + 0.035875 + 0.045 + 0.032125 + 0.037541 + 0.034333 + 0.033333 + 0.034333 + 0.034417 + 0.034167 + 0.0435 + 0.032375 + 0.033583 + 0.043458 + 0.035542 + 0.033792 + 0.03425 + 0.032458 + 0.037917 + 0.034 + 0.031333 + 0.031958 + 0.031875 + 0.031208 + 0.032167 + 0.038417 + 0.032208 + 0.032791 + 0.039916 + 0.032792 + 0.035042 + 0.035708 + 0.033583 + 0.03325 + 0.033042 + 0.032792 + 0.033209 + 0.031917 + 0.043209 + 0.032333 + 0.031916 + 0.033291 + 0.033 + 0.031 + 0.0325 + 0.031958 + 0.032 + 0.032625 + 0.032834 + 0.032708 + 0.032375 + 0.031333 + 0.032458 + 0.033791 + 0.031333 + 0.032375 + 0.033 + 0.031875 + 0.032 + 0.033042 + 0.032209 + 0.033042 + 0.032625 + 0.034125 + 0.032583 + 0.032625 + 0.031542 + 0.032542 + 0.033042 + 0.032959 + 0.032667 + 0.033333 + 0.036166 + 0.033166 + 0.046375 + 0.032834 + 0.033333 + 0.031209 + 0.034292 + 0.038917 + 0.032458 + 0.033625 + 0.034042 + 0.035542 + 0.03325 + 0.032708 + 0.031542 + 0.033125 + 0.037125 + 0.032292 + 0.032833 + 0.03125 + 0.033041 + 0.032708 + 0.0325 + 0.035583 + 0.033084 + 0.031708 + 0.032833 + 0.032209 + 0.032084 + 0.032417 + 0.039333 + 0.031666 + 0.0425 + 0.044041 + 0.033666 + 0.041666 + 0.033375 + 0.041417 + 0.033583 + 0.037791 + 0.033125 + 0.037083 + 0.032708 + 0.033541 + 0.033541 + 0.032375 + 0.043042 + 0.043542 + 0.030792 + 0.031542 + 0.032125 + 0.030541 + 0.072208 + 0.048083 + 0.034375 + 0.040625 + 0.041666 + 0.036375 + 0.033375 + 0.0335 + 0.036333 + 0.033542 + 0.034375 + 0.036625 + 0.033708 + 0.039291 + 0.033625 + 0.037 + 0.039542 + 0.037625 + 0.033542 + 0.036708 + 0.033666 + 0.03375 + 0.032834 + 0.033625 + 0.03425 + 0.033583 + 0.034125 + 0.032458 + 0.038416 + 0.039375 + 0.034084 + 0.039125 + 0.043 + 0.0385 + 0.038667 + 0.043292 + 0.040833 + 0.038584 + 0.042333 + 0.040666 + 0.038625 + 0.06475 + 0.046334 + 0.034417 + 0.034792 + 0.03475 + 0.034375 + 0.032583 + 0.0335 + 0.034375 + 0.033208 + 0.03425 + 0.034167 + 0.034 + 0.03575 + 0.038958 + 0.038583 + 0.0405 + 0.0375 + 0.037208 + 0.036708 + 0.038459 + 0.039042 + 0.036959 + 0.038542 + 0.040208 + 0.033708 + 0.032334 + 0.0335 + 0.033459 + 0.032917 + 0.03325 + 0.033584 + 0.035084 + 0.036833 + 0.036541 + 0.03175 + 0.032166 + 0.03275 + 0.032542 + 0.032375 + 0.034166 + 0.035625 + 0.03475 + 0.034708 + 0.033417 + 0.034042 + 0.034375 + 0.035959 + 0.038334 + 0.034208 + 0.035125 + 0.034792 + 0.033917 + 0.035292 + 0.038667 + 0.034084 + 0.032625 + 0.032291 + 0.031 + 0.031417 + 0.032042 + 0.030708 + 0.031209 + 0.038167 + 0.04375 + 0.042417 + 0.033708 + 0.035 + 0.033042 + 0.034542 + 0.032875 + 0.03275 + 0.031791 + 0.032792 + 0.032083 + 0.031583 + 0.033041 + 0.032667 + 0.031334 + 0.034167 + 0.038334 + 0.042625 + 0.044292 + 0.0325 + 0.033125 + 0.041625 + 0.040542 + 0.033709 + 0.032959 + 0.04325 + 0.033125 + 0.033958 + 0.033167 + 0.039875 + 0.0325 + 0.031875 + 0.032792 + 0.032833 + 0.032041 + 0.032208 + 0.031917 + 0.039709 + 0.032667 + 0.033583 + 0.033125 + 0.032708 + 0.031666 + 0.033 + 0.032416 + 0.032625 + 0.032208 + 0.03625 + 0.031583 + 0.041875 + 0.045917 + 0.032875 + 0.080625 + 0.062125 + 0.049791 + 0.046458 + 0.041584 + 0.037084 + 0.038 + 0.037959 + 0.036417 + 0.043542 + 0.03525 + 0.033875 + 0.0335 + 0.034291 + 0.036708 + 0.040958 + 0.039667 + 0.038459 + 0.03525 + 0.046 + 0.056875 + 0.058125 + 0.036791 + 0.040083 + 0.042834 + 0.034334 + 0.03825 + 0.034333 + 0.044542 + 0.041084 + 0.038417 + 0.047625 + 0.049791 + 0.047875 + 0.048625 + 0.042709 + 0.060708 + 0.044416 + 0.048917 + 0.045459 + 0.04275 + 0.036042 + 0.042708 + 0.036833 + 0.0365 + 0.038208 + 0.036875 + 0.042625 + 0.037291 + 0.042667 + 0.041458 + 0.0375 + 0.034625 + 0.036291 + 0.033875 + 0.033875 + 0.032958 + 0.036292 + 0.034291 + 0.039459 + 0.03775 + 0.037667 + 0.033292 + 0.034709 + 0.036375 + 0.033875 + 0.033542 + 0.03425 + 0.038333 + 0.065958 + 0.044625 + 0.042667 + 0.038584 + 0.047375 + 0.040959 + 0.038333 + 0.036708 + 0.037291 + 0.033375 + 0.035833 + 0.039667 + 0.037125 + 0.036 + 0.033958 + 0.034833 + 0.04875 + 0.040583 + 0.041833 + 0.052041 + 0.038083 + 0.033292 + 0.034333 + 0.0375 + 0.039125 + 0.038333 + 0.06225 + 0.034708 + 0.034083 + 0.034375 + 0.032916 + 0.033708 + 0.044458 + 0.0355 + 0.038625 + 0.035 + 0.032042 + 0.045042 + 0.040333 + 0.041167 + 0.032792 + 0.031833 + 0.032583 + 0.032709 + 0.031875 + 0.033625 + 0.033083 + 0.031959 + 0.035375 + 0.032958 + 0.031792 + 0.031916 + 0.042125 + 0.0375 + 0.036666 + 0.03525 + 0.045 + 0.035875 + 0.036333 + 0.039542 + 0.037791 + 0.0435 + 0.035833 + 0.033459 + 0.034042 + 0.036708 + 0.034083 + 0.034875 + 0.048292 + 0.036541 + 0.035417 + 0.033041 + 0.039542 + 0.030417 + 0.031792 + 0.0315 + 0.030959 + 0.031709 + 0.032209 + 0.040375 + 0.039334 + 0.030792 + 0.0355 + 0.034541 + 0.031375 + 0.033208 + 0.0325 + 0.031458 + 0.032291 + 0.032667 + 0.031625 + 0.03375 + 0.032125 + 0.031875 + 0.032209 + 0.033291 + 0.031583 + 0.03275 + 0.044292 + 0.033584 + 0.053334 + 0.041 + 0.039125 + 0.033417 + 0.041167 + 0.046292 + 0.034125 + 0.032417 + 0.034083 + 0.047167 + 0.032875 + 0.035583 + 0.033 + 0.032167 + 0.0335 + 0.033 + 0.032083 + 0.033917 + 0.044458 + 0.033666 + 0.035625 + 0.032166 + 0.0325 + 0.033 + 0.032125 + 0.032292 + 0.033541 + 0.032125 + 0.03425 + 0.034084 + 0.039709 + 0.037 + 0.040292 + 0.032916 + 0.033542 + 0.033958 + 0.032209 + 0.033084 + 0.036542 + 0.033 + 0.033125 + 0.031791 + 0.032959 + 0.033333 + 0.044375 + 0.0335 + 0.037208 + 0.032792 + 0.033417 + 0.032708 + 0.031708 + 0.033042 + 0.041042 + 0.031791 + 0.033208 + 0.033833 + 0.032667 + 0.03725 + 0.034042 + 0.033375 + 0.032917 + 0.032708 + 0.033333 + 0.033417 + 0.039291 + 0.033792 + 0.033333 + 0.032667 + 0.033667 + 0.037 + 0.040625 + 0.032583 + 0.031708 + 0.034042 + 0.032209 + 0.031792 + 0.030959 + 0.03225 + 0.032708 + 0.035292 + 0.032333 + 0.030833 + 0.035083 + 0.034041 + 0.032417 + 0.032666 + 0.032708 + 0.031667 + 0.032292 + 0.032375 + 0.031583 + 0.032875 + 0.032708 + 0.03125 + 0.032791 + 0.033 + 0.035916 + 0.033 + 0.03275 + 0.032542 + 0.03275 + 0.032125 + 0.073708 + 0.050917 + 0.036334 + 0.040208 + 0.038708 + 0.037125 + 0.049167 + 0.04 + 0.038458 + 0.040583 + 0.036416 + 0.050125 + 0.041708 + 0.04675 + 0.034875 + 0.03325 + 0.032875 + 0.037375 + 0.039208 + 0.032667 + 0.036792 + 0.036333 + 0.035625 + 0.032959 + 0.033917 + 0.032875 + 0.041416 + 0.041375 + 0.049917 + 0.03475 + 0.0355 + 0.036875 + 0.037875 + 0.038833 + 0.037083 + 0.034666 + 0.04 + 0.045541 + 0.034417 + 0.035625 + 0.041833 + 0.038375 + 0.040459 + 0.042042 + 0.034666 + 0.036208 + 0.037375 + 0.036958 + 0.038625 + 0.041125 + 0.037709 + 0.033834 + 0.038541 + 0.036542 + 0.042708 + 0.05 + 0.041083 + 0.037833 + 0.038083 + 0.036542 + 0.034459 + 0.035584 + 0.048833 + 0.0375 + 0.037541 + 0.038125 + 0.037542 + 0.03775 + 0.045625 + 0.039584 + 0.046667 + 0.041125 + 0.042583 + 0.036708 + 0.040958 + 0.086208 + 0.0395 + 0.036792 + 0.036375 + 0.030125 + 0.033167 + 0.032875 + 0.0315 + 0.031959 + 0.032 + 0.03175 + 0.0325 + 0.031875 + 0.031166 + 0.03275 + 0.033 + 0.031417 + 0.031833 + 0.04675 + 0.035333 + 0.035834 + 0.034709 + 0.044042 + 0.043917 + 0.037292 + 0.034875 + 0.035209 + 0.038 + 0.032083 + 0.032042 + 0.03125 + 0.032375 + 0.032583 + 0.031333 + 0.032458 + 0.040042 + 0.033792 + 0.034417 + 0.039125 + 0.030917 + 0.0325 + 0.032709 + 0.032625 + 0.03325 + 0.0315 + 0.032834 + 0.040417 + 0.031709 + 0.03225 + 0.032709 + 0.040834 + 0.032334 + 0.034708 + 0.035166 + 0.033208 + 0.032708 + 0.031917 + 0.044333 + 0.034125 + 0.035083 + 0.039625 + 0.032583 + 0.032458 + 0.033666 + 0.041125 + 0.033667 + 0.033083 + 0.039583 + 0.033959 + 0.033459 + 0.040708 + 0.034375 + 0.044625 + 0.032333 + 0.03275 + 0.039542 + 0.039917 + 0.036167 + 0.033125 + 0.043208 + 0.033792 + 0.037042 + 0.0335 + 0.04275 + 0.031833 + 0.035583 + 0.033083 + 0.032875 + 0.033958 + 0.033333 + 0.0325 + 0.033458 + 0.041291 + 0.032834 + 0.046709 + 0.032709 + 0.030334 + 0.031667 + 0.031583 + 0.030083 + 0.031542 + 0.031416 + 0.062542 + 0.041375 + 0.046083 + 0.045875 + 0.043125 + 0.04375 + 0.042041 + 0.039125 + 0.042375 + 0.048084 + 0.046459 + 0.036625 + 0.043083 + 0.037708 + 0.033291 + 0.033542 + 0.034 + 0.032334 + 0.033375 + 0.040375 + 0.037166 + 0.033375 + 0.03525 + 0.033292 + 0.043291 + 0.033291 + 0.03625 + 0.033833 + 0.031959 + 0.033084 + 0.033125 + 0.032584 + 0.051667 + 0.041084 + 0.040542 + 0.045208 + 0.03875 + 0.035542 + 0.033083 + 0.039042 + 0.033583 + 0.03625 + 0.033292 + 0.051 + 0.044959 + 0.0375 + 0.038291 + 0.040625 + 0.044791 + 0.037833 + 0.041292 + 0.033292 + 0.042792 + 0.0365 + 0.033459 + 0.045667 + 0.03675 + 0.043541 + 0.064292 + 0.047667 + 0.046792 + 0.041959 + 0.037709 + 0.041709 + 0.038459 + 0.037541 + 0.045333 + 0.038125 + 0.038167 + 0.033125 + 0.033667 + 0.032833 + 0.033291 + 0.033459 + 0.031917 + 0.036 + 0.034125 + 0.037375 + 0.041125 + 0.035125 + 0.034084 + 0.040417 + 0.033833 + 0.040792 + 0.043833 + 0.043083 + 0.049416 + 0.040167 + 0.042125 + 0.038208 + 0.034208 + 0.044584 + 0.034709 + 0.032834 + 0.031459 + 0.032542 + 0.0345 + 0.031292 + 0.0325 + 0.032541 + 0.033334 + 0.034334 + 0.046292 + 0.033083 + 0.035125 + 0.033542 + 0.034041 + 0.03275 + 0.041542 + 0.032958 + 0.040542 + 0.042167 + 0.041709 + 0.045542 + 0.036708 + 0.036083 + 0.03625 + 0.032042 + 0.033708 + 0.036541 + 0.034042 + 0.033042 + 0.032166 + 0.033333 + 0.033125 + 0.035792 + 0.03525 + 0.041792 + 0.03675 + 0.037625 + 0.042958 + 0.040625 + 0.037666 + 0.036917 + 0.036708 + 0.038083 + 0.069208 + 0.052167 + 0.042542 + 0.039333 + 0.034041 + 0.035083 + 0.03975 + 0.043 + 0.034666 + 0.03275 + 0.034291 + 0.034084 + 0.033125 + 0.034333 + 0.046917 + 0.0345 + 0.036417 + 0.03375 + 0.045333 + 0.041042 + 0.052834 + 0.0345 + 0.049125 + 0.050417 + 0.037625 + 0.038792 + 0.034541 + 0.042042 + 0.033792 + 0.031958 + 0.033458 + 0.035291 + 0.0345 + 0.033583 + 0.035958 + 0.035333 + 0.035208 + 0.033375 + 0.033084 + 0.034208 + 0.034583 + 0.038208 + 0.03525 + 0.034917 + 0.042917 + 0.045125 + 0.033375 + 0.037667 + 0.041875 + 0.033708 + 0.037041 + 0.033875 + 0.032709 + 0.034375 + 0.036709 + 0.034667 + 0.0365 + 0.035917 + 0.032458 + 0.033958 + 0.039 + 0.032917 + 0.04175 + 0.033042 + 0.037416 + 0.037042 + 0.032792 + 0.050166 + 0.03625 + 0.034 + 0.034125 + 0.033334 + 0.031917 + 0.033625 + 0.033542 + 0.031584 + 0.032834 + 0.035208 + 0.032291 + 0.05075 + 0.033583 + 0.032417 + 0.032416 + 0.032875 + 0.033375 + 0.033583 + 0.032083 + 0.034333 + 0.033292 + 0.032417 + 0.03325 + 0.03275 + 0.032416 + 0.032959 + 0.032417 + 0.032166 + 0.035542 + 0.033084 + 0.032375 + 0.034833 + 0.033292 + 0.033541 + 0.074208 + 0.04675 + 0.038334 + 0.037917 + 0.048083 + 0.034542 + 0.052375 + 0.048125 + 0.0445 + 0.04975 + 0.042041 + 0.036625 + 0.040459 + 0.04925 + 0.035875 + 0.034667 + 0.037083 + 0.0375 + 0.06225 + 0.045666 + 0.037708 + 0.037333 + 0.040083 + 0.037708 + 0.043875 + 0.037958 + 0.044666 + 0.039083 + 0.044292 + 0.044416 + 0.047167 + 0.048792 + 0.045875 + 0.062125 + 0.0355 + 0.045625 + 0.045209 + 0.046459 + 0.04925 + 0.03825 + 0.047416 + 0.038833 + 0.038709 + 0.039334 + 0.040375 + 0.0385 + 0.047292 + 0.042125 + 0.040834 + 0.034916 + 0.038208 + 0.034791 + 0.034667 + 0.047541 + 0.051208 + 0.047208 + 0.04 + 0.036458 + 0.033834 + 0.034959 + 0.037041 + 0.033083 + 0.032833 + 0.059833 + 0.048166 + 0.043125 + 0.046167 + 0.056708 + 0.037708 + 0.037208 + 0.033417 + 0.040542 + 0.037875 + 0.03925 + 0.037084 + 0.036875 + 0.044125 + 0.03575 + 0.03725 + 0.045541 + 0.049708 + 0.050875 + 0.035333 + 0.038041 + 0.04275 + 0.050584 + 0.044166 + 0.044708 + 0.041125 + 0.041916 + 0.03825 + 0.039 + 0.034875 + 0.048625 + 0.033625 + 0.033416 + 0.036417 + 0.0415 + 0.043 + 0.041833 + 0.045833 + 0.041583 + 0.053125 + 0.072959 + 0.055708 + 0.040458 + 0.038333 + 0.03675 + 0.045 + 0.046 + 0.044542 + 0.038583 + 0.038 + 0.037666 + 0.035084 + 0.034375 + 0.040875 + 0.033958 + 0.045167 + 0.03625 + 0.035375 + 0.033792 + 0.033 + 0.033125 + 0.036375 + 0.033375 + 0.034084 + 0.036 + 0.038334 + 0.045 + 0.034917 + 0.053166 + 0.045375 + 0.036916 + 0.042875 + 0.036334 + 0.039333 + 0.039167 + 0.046166 + 0.033084 + 0.034583 + 0.034542 + 0.034417 + 0.034125 + 0.0335 + 0.048208 + 0.037333 + 0.035417 + 0.039 + 0.047333 + 0.037708 + 0.037208 + 0.03825 + 0.034042 + 0.035291 + 0.034625 + 0.033375 + 0.033916 + 0.0335 + 0.033208 + 0.033417 + 0.044333 + 0.032708 + 0.047041 + 0.031083 + 0.0415 + 0.033875 + 0.03275 + 0.034333 + 0.033292 + 0.032375 + 0.034125 + 0.033167 + 0.032333 + 0.033667 + 0.033167 + 0.032167 + 0.033167 + 0.037167 + 0.032584 + 0.033541 + 0.0335 + 0.033417 + 0.033667 + 0.031541 + 0.033083 + 0.033417 + 0.03175 + 0.033667 + 0.033834 + 0.032209 + 0.033084 + 0.033583 + 0.032333 + 0.033292 + 0.03625 + 0.03575 + 0.033417 + 0.041541 + 0.034917 + 0.036 + 0.0335 + 0.033666 + 0.033667 + 0.034166 + 0.036625 + 0.034292 + 0.043792 + 0.033833 + 0.032625 + 0.032875 + 0.043167 + 0.033042 + 0.041458 + 0.034458 + 0.033292 + 0.03325 + 0.033709 + 0.036458 + 0.0355 + 0.033458 + 0.03325 + 0.035084 + 0.033375 + 0.032 + 0.033167 + 0.033375 + 0.032583 + 0.034125 + 0.03375 + 0.032542 + 0.033333 + 0.0325 + 0.041625 + 0.034042 + 0.033208 + 0.048416 + 0.033334 + 0.033667 + 0.032541 + 0.033792 + 0.033334 + 0.033334 + 0.036 + 0.032583 + 0.033833 + 0.034166 + 0.035792 + 0.033 + 0.037333 + 0.043708 + 0.034792 + 0.033958 + 0.033041 + 0.033417 + 0.034583 + 0.031958 + 0.031792 + 0.032541 + 0.031459 + 0.086542 + 0.0645 + 0.05975 + 0.046542 + 0.0395 + 0.048125 + 0.033708 + 0.035333 + 0.037 + 0.033708 + 0.033542 + 0.034 + 0.033791 + 0.032833 + 0.033583 + 0.042709 + 0.042709 + 0.034459 + 0.032958 + 0.032666 + 0.033292 + 0.042333 + 0.044458 + 0.042125 + 0.044584 + 0.038 + 0.033709 + 0.035166 + 0.035042 + 0.03725 + 0.03975 + 0.038458 + 0.039084 + 0.036542 + 0.037833 + 0.03725 + 0.039334 + 0.039125 + 0.034958 + 0.033167 + 0.037916 + 0.041583 + 0.037667 + 0.035542 + 0.033833 + 0.035042 + 0.033583 + 0.035542 + 0.045666 + 0.044208 + 0.045375 + 0.037375 + 0.041917 + 0.034792 + 0.03725 + 0.034333 + 0.033417 + 0.036875 + 0.045459 + 0.033542 + 0.0345 + 0.034542 + 0.03425 + 0.035625 + 0.032625 + 0.033375 + 0.033958 + 0.03275 + 0.034208 + 0.034208 + 0.0345 + 0.034583 + 0.033875 + 0.03775 + 0.043542 + 0.045042 + 0.044709 + 0.045791 + 0.042584 + 0.035334 + 0.039917 + 0.034083 + 0.034375 + 0.044417 + 0.03375 + 0.036208 + 0.03475 + 0.033125 + 0.036 + 0.036459 + 0.039417 + 0.035416 + 0.034375 + 0.032958 + 0.034042 + 0.034459 + 0.032 + 0.03675 + 0.034208 + 0.032375 + 0.045167 + 0.038834 + 0.035834 + 0.106291 + 0.135 + 0.099167 + 0.15875 + 0.047042 + 0.045042 + 0.042416 + 0.041083 + 0.072458 + 0.049875 + 0.098292 + 0.044875 + 0.05775 + 0.052292 + 0.040209 + 0.045042 + 0.040416 + 0.041125 + 0.040958 + 0.043292 + 0.049292 + 0.037375 + 0.038208 + 0.039041 + 0.03875 + 0.038834 + 0.0405 + 0.043084 + 0.043 + 0.034417 + 0.03325 + 0.034167 + 0.034875 + 0.033917 + 0.034542 + 0.037375 + 0.045708 + 0.035625 + 0.034375 + 0.03475 + 0.044875 + 0.04775 + 0.048958 + 0.034458 + 0.033625 + 0.0345 + 0.034875 + 0.033083 + 0.034625 + 0.034333 + 0.036 + 0.041166 + 0.035625 + 0.05375 + 0.037 + 0.038 + 0.044083 + 0.043125 + 0.046125 + 0.04725 + 0.038375 + 0.034167 + 0.0355 + 0.036334 + 0.044041 + 0.048 + 0.039792 + 0.033209 + 0.033541 + 0.03575 + 0.035416 + 0.035125 + 0.033208 + 0.033 + 0.033916 + 0.034542 + 0.036042 + 0.0345 + 0.03875 + 0.042958 + 0.048958 + 0.042333 + 0.037208 + 0.035083 + 0.044542 + 0.040125 + 0.057875 + 0.050333 + 0.044375 + 0.046708 + 0.045709 + 0.043542 + 0.04475 + 0.040042 + 0.0355 + 0.032875 + 0.032416 + 0.034709 + 0.033834 + 0.033625 + 0.0335 + 0.038417 + 0.036583 + 0.044375 + 0.032792 + 0.03475 + 0.033834 + 0.033125 + 0.033667 + 0.033709 + 0.032458 + 0.033541 + 0.033458 + 0.036292 + 0.03425 + 0.034083 + 0.033041 + 0.041042 + 0.041542 + 0.157666 + 0.067625 + 0.034541 + 0.034584 + 0.033584 + 0.032333 + 0.03375 + 0.033875 + 0.032292 + 0.040416 + 0.034167 + 0.032083 + 0.0335 + 0.033416 + 0.032667 + 0.033125 + 0.034417 + 0.032583 + 0.033292 + 0.033334 + 0.033 + 0.032959 + 0.032584 + 0.039042 + 0.033833 + 0.032708 + 0.03325 + 0.034166 + 0.032208 + 0.038333 + 0.033833 + 0.032875 + 0.036334 + 0.042542 + 0.034458 + 0.034541 + 0.034042 + 0.033792 + 0.03425 + 0.036959 + 0.034333 + 0.034875 + 0.033375 + 0.035667 + 0.033792 + 0.056667 + 0.04525 + 0.04675 + 0.038208 + 0.048166 + 0.036292 + 0.031916 + 0.033916 + 0.033167 + 0.031917 + 0.033583 + 0.033166 + 0.033583 + 0.033458 + 0.050209 + 0.045334 + 0.043084 + 0.039542 + 0.035083 + 0.042333 + 0.046041 + 0.058416 + 0.04725 + 0.0455 + 0.039833 + 0.040625 + 0.040292 + 0.047542 + 0.046084 + 0.046042 + 0.044333 + 0.042459 + 0.04075 + 0.040375 + 0.040541 + 0.038041 + 0.037333 + 0.040125 + 0.046125 + 0.043334 + 0.036417 + 0.051916 + 0.042292 + 0.128709 + 0.043792 + 0.038042 + 0.039625 + 0.043709 + 0.039583 + 0.047833 + 0.042458 + 0.042417 + 0.041167 + 0.0385 + 0.037334 + 0.0535 + 0.041625 + 0.046083 + 0.041458 + 0.056333 + 0.040167 + 0.063875 + 0.048708 + 0.040958 + 0.037292 + 0.03825 + 0.03775 + 0.035208 + 0.03475 + 0.041333 + 0.038041 + 0.03775 + 0.033667 + 0.042 + 0.042041 + 0.035125 + 0.034417 + 0.045333 + 0.048666 + 0.037417 + 0.037875 + 0.035458 + 0.035375 + 0.047208 + 0.046541 + 0.045167 + 0.033958 + 0.061709 + 0.046917 + 0.035042 + 0.037542 + 0.0375 + 0.036709 + 0.049542 + 0.043666 + 0.04925 + 0.034708 + 0.044083 + 0.041459 + 0.04575 + 0.037083 + 0.034625 + 0.034125 + 0.033958 + 0.035416 + 0.036375 + 0.033292 + 0.033584 + 0.033834 + 0.034375 + 0.034 + 0.051791 + 0.034125 + 0.040625 + 0.045875 + 0.035125 + 0.045125 + 0.039292 + 0.04325 + 0.041417 + 0.039667 + 0.045334 + 0.075125 + 0.051917 + 0.047875 + 0.043583 + 0.041792 + 0.042167 + 0.036 + 0.035042 + 0.036458 + 0.045291 + 0.0405 + 0.042875 + 0.032916 + 0.036917 + 0.045625 + 0.036625 + 0.036459 + 0.036334 + 0.033125 + 0.039 + 0.035667 + 0.0365 + 0.03425 + 0.03925 + 0.033125 + 0.044042 + 0.046375 + 0.037792 + 0.059833 + 0.042708 + 0.037708 + 0.033916 + 0.037167 + 0.034625 + 0.034125 + 0.035792 + 0.041708 + 0.03925 + 0.045333 + 0.043 + 0.039958 + 0.042667 + 0.033416 + 0.033042 + 0.032083 + 0.034167 + 0.033084 + 0.032917 + 0.033041 + 0.036625 + 0.034875 + 0.056917 + 0.044125 + 0.037542 + 0.042125 + 0.0415 + 0.039125 + 0.047041 + 0.042167 + 0.0425 + 0.036875 + 0.032916 + 0.035708 + 0.041541 + 0.1295 + 0.057584 + 0.046792 + 0.051041 + 0.037375 + 0.034042 + 0.039084 + 0.041584 + 0.034541 + 0.034417 + 0.041166 + 0.0385 + 0.045417 + 0.04375 + 0.045334 + 0.037625 + 0.049333 + 0.036625 + 0.035208 + 0.037291 + 0.033667 + 0.033084 + 0.033792 + 0.032875 + 0.03325 + 0.043333 + 0.033542 + 0.033584 + 0.034541 + 0.036042 + 0.037667 + 0.033709 + 0.044584 + 0.0395 + 0.032917 + 0.035083 + 0.044333 + 0.040833 + 0.049291 + 0.043958 + 0.045 + 0.039416 + 0.038625 + 0.038083 + 0.042791 + 0.044708 + 0.035792 + 0.046417 + 0.052042 + 0.057791 + 0.054042 + 0.03525 + 0.03675 + 0.033875 + 0.031625 + 0.036416 + 0.044667 + 0.037042 + 0.045917 + 0.061167 + 0.057375 + 0.035375 + 0.034458 + 0.033541 + 0.047667 + 0.034417 + 0.033625 + 0.034667 + 0.034667 + 0.033333 + 0.033083 + 0.032292 + 0.033584 + 0.033709 + 0.032792 + 0.043416 + 0.035083 + 0.036542 + 0.043042 + 0.035042 + 0.043209 + 0.040292 + 0.032583 + 0.032875 + 0.034 + 0.0405 + 0.036 + 0.033667 + 0.03325 + 0.03275 + 0.033875 + 0.032667 + 0.035584 + 0.045042 + 0.033833 + 0.03325 + 0.03375 + 0.032958 + 0.033917 + 0.033625 + 0.033958 + 0.036625 + 0.048666 + 0.033584 + 0.034375 + 0.034417 + 0.040292 + 0.049167 + 0.045084 + 0.035542 + 0.043166 + 0.042958 + 0.034208 + 0.034208 + 0.033291 + 0.034083 + 0.033709 + 0.032459 + 0.033417 + 0.034333 + 0.034417 + 0.033792 + 0.033958 + 0.032291 + 0.037 + 0.03425 + 0.032917 + 0.034834 + 0.034417 + 0.046458 + 0.035208 + 0.034 + 0.034791 + 0.035042 + 0.033625 + 0.044958 + 0.042042 + 0.044333 + 0.040791 + 0.046291 + 0.045208 + 0.043459 + 0.0405 + 0.033334 + 0.033416 + 0.033083 + 0.030833 + 0.032458 + 0.03625 + 0.032166 + 0.032209 + 0.031583 + 0.036541 + 0.032583 + 0.033375 + 0.033625 + 0.034417 + 0.032792 + 0.033125 + 0.033416 + 0.032625 + 0.033416 + 0.033875 + 0.0325 + 0.033041 + 0.033792 + 0.033625 + 0.033042 + 0.033292 + 0.033958 + 0.032875 + 0.032917 + 0.033333 + 0.033083 + 0.031291 + 0.032208 + 0.033584 + 0.032 + 0.033541 + 0.033209 + 0.035 + 0.032791 + 0.033875 + 0.033125 + 0.033834 + 0.033709 + 0.032375 + 0.033167 + 0.033917 + 0.032833 + 0.033208 + 0.032833 + 0.033875 + 0.033875 + 0.032667 + 0.033958 + 0.034666 + 0.032333 + 0.033042 + 0.033291 + 0.035917 + 0.033416 + 0.043958 + 0.0335 + 0.033875 + 0.033875 + 0.033083 + 0.034833 + 0.040833 + 0.0345 + 0.033459 + 0.032625 + 0.033542 + 0.033375 + 0.032625 + 0.033291 + 0.033583 + 0.032208 + 0.034208 + 0.03375 + 0.03225 + 0.032291 + 0.033583 + 0.033042 + 0.036791 + 0.041125 + 0.035833 + 0.034458 + 0.036292 + 0.034166 + 0.03525 + 0.042917 + 0.041375 + 0.041375 + 0.038666 + 0.0385 + 0.037416 + 0.0375 + 0.039958 + 0.102333 + 0.051709 + 0.04675 + 0.045958 + 0.044708 + 0.040625 + 0.038125 + 0.036542 + 0.041375 + 0.040042 + 0.042083 + 0.045 + 0.039625 + 0.049042 + 0.044292 + 0.047334 + 0.039875 + 0.050916 + 0.048833 + 0.038208 + 0.035042 + 0.032875 + 0.033292 + 0.035084 + 0.033209 + 0.034541 + 0.03525 + 0.041708 + 0.037416 + 0.035583 + 0.039792 + 0.03475 + 0.037 + 0.041667 + 0.0345 + 0.03325 + 0.039583 + 0.038458 + 0.033209 + 0.034375 + 0.034792 + 0.037417 + 0.044541 + 0.041375 + 0.033042 + 0.033917 + 0.040875 + 0.033583 + 0.033833 + 0.034959 + 0.046208 + 0.033791 + 0.032625 + 0.033708 + 0.04525 + 0.033333 + 0.033584 + 0.042417 + 0.03375 + 0.034667 + 0.035334 + 0.033083 + 0.05125 + 0.045958 + 0.055166 + 0.035458 + 0.035959 + 0.033875 + 0.045042 + 0.038459 + 0.047792 + 0.036667 + 0.034334 + 0.036459 + 0.033208 + 0.032125 + 0.033959 + 0.03375 + 0.032583 + 0.033209 + 0.046125 + 0.037 + 0.034083 + 0.046125 + 0.034167 + 0.036666 + 0.039917 + 0.033084 + 0.033417 + 0.035375 + 0.047084 + 0.042333 + 0.033708 + 0.034417 + 0.034709 + 0.033125 + 0.044042 + 0.03425 + 0.034625 + 0.040459 + 0.035917 + 0.035708 + 0.03525 + 0.044333 + 0.048417 + 0.046792 + 0.034875 + 0.033875 + 0.032458 + 0.041458 + 0.035375 + 0.03225 + 0.031292 + 0.032959 + 0.035584 + 0.04075 + 0.034084 + 0.03525 + 0.033542 + 0.03325 + 0.033375 + 0.032416 + 0.040583 + 0.039208 + 0.0435 + 0.039041 + 0.037667 + 0.039042 + 0.041375 + 0.033334 + 0.03525 + 0.033458 + 0.033041 + 0.033292 + 0.033084 + 0.050708 + 0.044167 + 0.036542 + 0.032875 + 0.039792 + 0.042375 + 0.032792 + 0.033542 + 0.033917 + 0.033625 + 0.033916 + 0.034375 + 0.032042 + 0.041166 + 0.0385 + 0.034208 + 0.034292 + 0.033042 + 0.034875 + 0.035375 + 0.034125 + 0.033708 + 0.033958 + 0.032833 + 0.033875 + 0.037917 + 0.03325 + 0.034375 + 0.034542 + 0.047791 + 0.036209 + 0.034333 + 0.033625 + 0.043375 + 0.067208 + 0.05025 + 0.042417 + 0.03725 + 0.043292 + 0.04175 + 0.036208 + 0.034 + 0.033916 + 0.033208 + 0.033583 + 0.035 + 0.033042 + 0.034041 + 0.034417 + 0.033542 + 0.042541 + 0.041167 + 0.038583 + 0.039958 + 0.035458 + 0.05325 + 0.036584 + 0.033875 + 0.034834 + 0.034833 + 0.03375 + 0.050125 + 0.035833 + 0.034 + 0.03475 + 0.034291 + 0.033959 + 0.044209 + 0.046709 + 0.034084 + 0.034125 + 0.033792 + 0.032334 + 0.033209 + 0.033084 + 0.033 + 0.042666 + 0.0345 + 0.033208 + 0.0395 + 0.031542 + 0.035833 + 0.03675 + 0.034708 + 0.043208 + 0.04425 + 0.032625 + 0.033791 + 0.033458 + 0.036166 + 0.038666 + 0.038125 + 0.036917 + 0.037791 + 0.036875 + 0.03775 + 0.037334 + 0.036125 + 0.037334 + 0.037667 + 0.036333 + 0.038042 + 0.037208 + 0.035792 + 0.03775 + 0.036792 + 0.03625 + 0.037291 + 0.03775 + 0.04575 + 0.043042 + 0.042708 + 0.041 + 0.042459 + 0.041875 + 0.047 + 0.042125 + 0.042708 + 0.042833 + 0.038916 + 0.038375 + 0.0475 + 0.065875 + 0.057375 + 0.042417 + 0.04375 + 0.04325 + 0.052125 + 0.039791 + 0.042625 + 0.03625 + 0.035208 + 0.034667 + 0.038792 + 0.05 + 0.035875 + 0.045292 + 0.038667 + 0.040375 + 0.038167 + 0.040541 + 0.036375 + 0.0345 + 0.033458 + 0.034 + 0.046959 + 0.034875 + 0.035542 + 0.033875 + 0.0335 + 0.03775 + 0.03425 + 0.033208 + 0.03425 + 0.035459 + 0.043209 + 0.037166 + 0.034417 + 0.035333 + 0.048125 + 0.035625 + 0.036583 + 0.036459 + 0.036458 + 0.039125 + 0.037125 + 0.057875 + 0.04225 + 0.035875 + 0.03525 + 0.035458 + 0.043958 + 0.034208 + 0.032666 + 0.032667 + 0.045458 + 0.032833 + 0.032417 + 0.034 + 0.037125 + 0.032459 + 0.035167 + 0.033583 + 0.036917 + 0.037208 + 0.034666 + 0.033 + 0.034333 + 0.033542 + 0.033 + 0.034084 + 0.034209 + 0.033583 + 0.034 + 0.03425 + 0.066208 + 0.060875 + 0.041125 + 0.042958 + 0.041209 + 0.048125 + 0.034291 + 0.033167 + 0.033125 + 0.033541 + 0.033708 + 0.032667 + 0.033458 + 0.033875 + 0.032125 + 0.033834 + 0.033292 + 0.032791 + 0.037417 + 0.034334 + 0.043 + 0.035125 + 0.036459 + 0.033958 + 0.033916 + 0.03575 + 0.034083 + 0.033375 + 0.03275 + 0.034 + 0.034209 + 0.032834 + 0.033667 + 0.034 + 0.037375 + 0.037417 + 0.050417 + 0.033875 + 0.034375 + 0.034792 + 0.038333 + 0.034334 + 0.04275 + 0.041709 + 0.046875 + 0.033167 + 0.033166 + 0.033792 + 0.03675 + 0.044375 + 0.033791 + 0.034708 + 0.036667 + 0.03375 + 0.033083 + 0.033792 + 0.0335 + 0.033583 + 0.03925 + 0.041709 + 0.033792 + 0.037125 + 0.046334 + 0.037333 + 0.034459 + 0.034417 + 0.034833 + 0.034834 + 0.046459 + 0.0375 + 0.047625 + 0.034375 + 0.036542 + 0.041833 + 0.03975 + 0.048208 + 0.044666 + 0.042375 + 0.043792 + 0.040333 + 0.032625 + 0.04375 + 0.06425 + 0.042792 + 0.037542 + 0.032792 + 0.043542 + 0.035834 + 0.036125 + 0.039209 + 0.041 + 0.032375 + 0.035708 + 0.03575 + 0.032958 + 0.033667 + 0.033459 + 0.047625 + 0.033708 + 0.042167 + 0.03725 + 0.041167 + 0.039667 + 0.033667 + 0.0335 + 0.033 + 0.034417 + 0.033375 + 0.039834 + 0.033667 + 0.03275 + 0.032208 + 0.033125 + 0.033125 + 0.032375 + 0.03275 + 0.033834 + 0.032084 + 0.033334 + 0.033875 + 0.033167 + 0.033792 + 0.034375 + 0.033875 + 0.034208 + 0.03525 + 0.034792 + 0.034834 + 0.033125 + 0.048667 + 0.045875 + 0.035917 + 0.033583 + 0.033584 + 0.042292 + 0.036125 + 0.044667 + 0.0345 + 0.034458 + 0.034292 + 0.034292 + 0.034625 + 0.032667 + 0.033292 + 0.034209 + 0.042167 + 0.034167 + 0.036375 + 0.032875 + 0.037959 + 0.034459 + 0.033916 + 0.03425 + 0.033375 + 0.032209 + 0.033459 + 0.033125 + 0.032709 + 0.034167 + 0.033416 + 0.033917 + 0.03375 + 0.039625 + 0.03375 + 0.034291 + 0.033208 + 0.034 + 0.034416 + 0.039542 + 0.033875 + 0.034459 + 0.031959 + 0.036709 + 0.036541 + 0.040333 + 0.044458 + 0.042625 + 0.035833 + 0.04025 + 0.038167 + 0.036666 + 0.033458 + 0.03175 + 0.03275 + 0.033208 + 0.0355 + 0.032792 + 0.032209 + 0.034583 + 0.035042 + 0.033625 + 0.034333 + 0.0345 + 0.034 + 0.033083 + 0.034166 + 0.034 + 0.032375 + 0.034042 + 0.034 + 0.033958 + 0.034292 + 0.033125 + 0.03425 + 0.038833 + 0.044041 + 0.041791 + 0.034292 + 0.033667 + 0.042916 + 0.037208 + 0.036708 + 0.045041 + 0.039042 + 0.039834 + 0.039958 + 0.037667 + 0.044542 + 0.045333 + 0.044875 + 0.043292 + 0.048541 + 0.039625 + 0.035541 + 0.035375 + 0.040416 + 0.043959 + 0.036959 + 0.032875 + 0.034542 + 0.034 + 0.032542 + 0.034083 + 0.034 + 0.034 + 0.038292 + 0.0425 + 0.033541 + 0.037625 + 0.047959 + 0.036916 + 0.035417 + 0.034541 + 0.034 + 0.034542 + 0.042625 + 0.033625 + 0.034125 + 0.033459 + 0.033375 + 0.033334 + 0.033417 + 0.033625 + 0.03425 + 0.032375 + 0.033875 + 0.033584 + 0.032417 + 0.034167 + 0.0345 + 0.035875 + 0.032667 + 0.037791 + 0.037875 + 0.034542 + 0.033209 + 0.035084 + 0.033834 + 0.033375 + 0.035208 + 0.051375 + 0.040666 + 0.044083 + 0.034458 + 0.036125 + 0.035334 + 0.050792 + 0.034709 + 0.033459 + 0.032875 + 0.033292 + 0.032958 + 0.031708 + 0.032583 + 0.033 + 0.038209 + 0.032041 + 0.032667 + 0.037084 + 0.035042 + 0.034458 + 0.035041 + 0.042917 + 0.03375 + 0.03275 + 0.03525 + 0.033917 + 0.033209 + 0.033875 + 0.0335 + 0.03275 + 0.033292 + 0.0325 + 0.034375 + 0.034 + 0.033292 + 0.034041 + 0.03375 + 0.043208 + 0.03525 + 0.033625 + 0.033416 + 0.033792 + 0.033666 + 0.032583 + 0.034208 + 0.033292 + 0.032833 + 0.032916 + 0.033917 + 0.034042 + 0.033875 + 0.032375 + 0.034125 + 0.033291 + 0.033667 + 0.034209 + 0.033667 + 0.032833 + 0.034208 + 0.033416 + 0.033333 + 0.033541 + 0.036458 + 0.034208 + 0.045542 + 0.044583 + 0.044666 + 0.037708 + 0.03525 + 0.0355 + 0.053125 + 0.038958 + 0.036333 + 0.046917 + 0.036584 + 0.033584 + 0.033584 + 0.032791 + 0.035625 + 0.033167 + 0.033083 + 0.034542 + 0.044666 + 0.03475 + 0.049625 + 0.03475 + 0.033458 + 0.040208 + 0.038333 + 0.0345 + 0.033292 + 0.035 + 0.03425 + 0.033958 + 0.033583 + 0.04175 + 0.0345 + 0.03375 + 0.034458 + 0.035042 + 0.033459 + 0.034625 + 0.040458 + 0.034583 + 0.034375 + 0.034917 + 0.034041 + 0.032917 + 0.038416 + 0.033875 + 0.03325 + 0.032583 + 0.0335 + 0.039708 + 0.044667 + 0.042 + 0.03375 + 0.032458 + 0.045833 + 0.033834 + 0.033125 + 0.033459 + 0.03375 + 0.032375 + 0.033333 + 0.032708 + 0.031708 + 0.032542 + 0.034125 + 0.034042 + 0.033792 + 0.032375 + 0.036583 + 0.036 + 0.040375 + 0.041167 + 0.040375 + 0.037042 + 0.036791 + 0.034042 + 0.034042 + 0.034333 + 0.033792 + 0.032167 + 0.034583 + 0.034125 + 0.033459 + 0.034917 + 0.045958 + 0.038458 + 0.033583 + 0.037125 + 0.0395 + 0.037583 + 0.036167 + 0.038417 + 0.041541 + 0.036417 + 0.037583 + 0.039458 + 0.036708 + 0.038083 + 0.03825 + 0.036292 + 0.040416 + 0.038333 + 0.037208 + 0.046667 + 0.041917 + 0.041208 + 0.038666 + 0.037375 + 0.038792 + 0.0385 + 0.041292 + 0.039834 + 0.037875 + 0.043167 + 0.047167 + 0.040083 + 0.055875 + 0.078792 + 0.040958 + 0.03375 + 0.046417 + 0.039542 + 0.036084 + 0.034292 + 0.033666 + 0.035958 + 0.033833 + 0.036875 + 0.034458 + 0.035791 + 0.040417 + 0.035084 + 0.034583 + 0.033666 + 0.041625 + 0.037375 + 0.042917 + 0.051167 + 0.035166 + 0.033958 + 0.034625 + 0.034625 + 0.03325 + 0.034 + 0.032583], + :filter-tx-request-memo + [0.045625 + 0.018958 + 0.013375 + 0.015291 + 0.009916 + 0.009375 + 0.019125 + 0.018042 + 0.009625 + 0.010334 + 0.010458 + 0.009416 + 0.011375 + 0.010167 + 0.009875 + 0.010417 + 0.00975 + 0.009375 + 0.010083 + 0.010167 + 0.0105 + 0.010167 + 0.00975 + 0.010292 + 0.010167 + 0.009334 + 0.009917 + 0.009791 + 0.009375 + 0.009667 + 0.010083 + 0.009042 + 0.012041 + 0.009875 + 0.01275 + 0.010291 + 0.01 + 0.010042 + 0.010375 + 0.010417 + 0.0105 + 0.013375 + 0.012417 + 0.011875 + 0.011125 + 0.019042 + 0.013708 + 0.012458 + 0.012792 + 0.012583 + 0.025458 + 0.011375 + 0.011958 + 0.012 + 0.010667 + 0.011417 + 0.020125 + 0.010333 + 0.011834 + 0.018833 + 0.011541 + 0.013167 + 0.010292 + 0.012167 + 0.011792 + 0.0105 + 0.011792 + 0.011417 + 0.01025 + 0.011042 + 0.011084 + 0.009708 + 0.020333 + 0.01225 + 0.009958 + 0.015125 + 0.011291 + 0.010708 + 0.011125 + 0.01125 + 0.011042 + 0.010583 + 0.0135 + 0.017291 + 0.011709 + 0.010708 + 0.011458 + 0.011416 + 0.013208 + 0.009583 + 0.009625 + 0.008708 + 0.010125 + 0.017958 + 0.008625 + 0.009708 + 0.00925 + 0.008167 + 0.009625 + 0.010417 + 0.009416 + 0.011208 + 0.00975 + 0.009959 + 0.010083 + 0.021 + 0.009833 + 0.01025 + 0.009417 + 0.01025 + 0.009958 + 0.009084 + 0.009791 + 0.009708 + 0.008958 + 0.010125 + 0.010083 + 0.009 + 0.010292 + 0.010333 + 0.010208 + 0.009917 + 0.009209 + 0.010125 + 0.010125 + 0.009 + 0.009958 + 0.010208 + 0.016917 + 0.009833 + 0.010083 + 0.009084 + 0.010083 + 0.009833 + 0.009125 + 0.009917 + 0.010167 + 0.0095 + 0.010542 + 0.01075 + 0.011041 + 0.010666 + 0.010958 + 0.019292 + 0.011542 + 0.011084 + 0.01225 + 0.013209 + 0.011708 + 0.015916 + 0.013041 + 0.011708 + 0.013084 + 0.012833 + 0.011125 + 0.012292 + 0.012209 + 0.011542 + 0.011917 + 0.011542 + 0.024375 + 0.0125 + 0.011 + 0.011791 + 0.012208 + 0.014459 + 0.0125 + 0.011875 + 0.010583 + 0.012416 + 0.012208 + 0.020208 + 0.015 + 0.012792 + 0.011792 + 0.027459 + 0.012417 + 0.019084 + 0.021292 + 0.012792 + 0.014292 + 0.013083 + 0.015292 + 0.013375 + 0.013584 + 0.0135 + 0.021209 + 0.012834 + 0.016 + 0.011667 + 0.011209 + 0.011 + 0.011916 + 0.011417 + 0.010708 + 0.011083 + 0.011958 + 0.015 + 0.010958 + 0.01125 + 0.01275 + 0.012041 + 0.0105 + 0.012334 + 0.011583 + 0.011125 + 0.012209 + 0.011792 + 0.010959 + 0.01175 + 0.011458 + 0.010666 + 0.01125 + 0.011125 + 0.010834 + 0.011084 + 0.011917 + 0.01075 + 0.011916 + 0.012125 + 0.011792 + 0.011542 + 0.01075 + 0.011375 + 0.011334 + 0.01075 + 0.011958 + 0.011208 + 0.010416 + 0.012416 + 0.011917 + 0.010958 + 0.011666 + 0.011541 + 0.010458 + 0.012125 + 0.011416 + 0.010708 + 0.011834 + 0.011792 + 0.012042 + 0.011709 + 0.011 + 0.012167 + 0.012333 + 0.011625 + 0.013 + 0.016042 + 0.011959 + 0.019417 + 0.013417 + 0.01225 + 0.012625 + 0.012833 + 0.0115 + 0.01125 + 0.015208 + 0.01175 + 0.012375 + 0.012208 + 0.022083 + 0.0125 + 0.010583 + 0.011209 + 0.011917 + 0.011209 + 0.0125 + 0.011583 + 0.010792 + 0.011625 + 0.015083 + 0.01075 + 0.012084 + 0.011917 + 0.012584 + 0.012708 + 0.02275 + 0.012042 + 0.012209 + 0.01275 + 0.013083 + 0.011584 + 0.012375 + 0.016042 + 0.013375 + 0.011833 + 0.012958 + 0.01525 + 0.011041 + 0.021625 + 0.010792 + 0.009417 + 0.010875 + 0.010416 + 0.009333 + 0.012208 + 0.011083 + 0.010583 + 0.010208 + 0.010167 + 0.00925 + 0.0125 + 0.010166 + 0.011 + 0.012667 + 0.011458 + 0.011916 + 0.011375 + 0.010709 + 0.011542 + 0.011209 + 0.010666 + 0.011458 + 0.010667 + 0.010625 + 0.011042 + 0.011208 + 0.009917 + 0.0115 + 0.011291 + 0.011 + 0.011292 + 0.017042 + 0.011292 + 0.011042 + 0.009709 + 0.010875 + 0.010625 + 0.010458 + 0.010917 + 0.011542 + 0.009958 + 0.010917 + 0.014042 + 0.010334 + 0.011167 + 0.010791 + 0.010333 + 0.015208 + 0.024042 + 0.01075 + 0.0115 + 0.010458 + 0.011292 + 0.011125 + 0.013458 + 0.010708 + 0.012542 + 0.011709 + 0.021959 + 0.012625 + 0.011208 + 0.012416 + 0.012167 + 0.012709 + 0.011375 + 0.014625 + 0.016292 + 0.011333 + 0.011333 + 0.011292 + 0.014667 + 0.010209 + 0.0115 + 0.011709 + 0.010667 + 0.01125 + 0.015167 + 0.011 + 0.0115 + 0.011334 + 0.010333 + 0.01075 + 0.014208 + 0.022917 + 0.012667 + 0.012459 + 0.010792 + 0.011959 + 0.012292 + 0.016125 + 0.011584 + 0.011125 + 0.014292 + 0.012 + 0.014834 + 0.012042 + 0.01475 + 0.011041 + 0.012 + 0.010208 + 0.008584 + 0.00975 + 0.01025 + 0.008666 + 0.010125 + 0.009583 + 0.009416 + 0.009833 + 0.009708 + 0.010416 + 0.010416 + 0.01025 + 0.010792 + 0.011459 + 0.010542 + 0.010833 + 0.010291 + 0.010041 + 0.01075 + 0.010959 + 0.0245 + 0.010542 + 0.010583 + 0.009916 + 0.010916 + 0.011 + 0.009875 + 0.013916 + 0.010958 + 0.010875 + 0.01025 + 0.009834 + 0.011333 + 0.010583 + 0.00975 + 0.010958 + 0.010667 + 0.009542 + 0.010625 + 0.010625 + 0.0105 + 0.010833 + 0.011084 + 0.009709 + 0.010833 + 0.010458 + 0.009334 + 0.0115 + 0.010958 + 0.011333 + 0.011125 + 0.010458 + 0.010875 + 0.010875 + 0.01 + 0.010792 + 0.014709 + 0.011542 + 0.012125 + 0.011583 + 0.010541 + 0.012 + 0.01425 + 0.011 + 0.011667 + 0.010708 + 0.011083 + 0.010833 + 0.010709 + 0.010917 + 0.010833 + 0.010916 + 0.011041 + 0.011584 + 0.009791 + 0.011417 + 0.010708 + 0.009792 + 0.011042 + 0.010958 + 0.012333 + 0.011459 + 0.011375 + 0.010792 + 0.016292 + 0.012417 + 0.01025 + 0.012 + 0.014042 + 0.012084 + 0.011791 + 0.010416 + 0.011583 + 0.012208 + 0.01075 + 0.014334 + 0.012333 + 0.010958 + 0.012209 + 0.010167 + 0.009667 + 0.009625 + 0.009917 + 0.008917 + 0.009542 + 0.009667 + 0.008292 + 0.008958 + 0.009292 + 0.014417 + 0.011542 + 0.011833 + 0.011875 + 0.01125 + 0.010292 + 0.011334 + 0.011625 + 0.010709 + 0.011542 + 0.011917 + 0.010709 + 0.011458 + 0.01125 + 0.009667 + 0.011458 + 0.011542 + 0.010083 + 0.010834 + 0.010917 + 0.010458 + 0.010917 + 0.010333 + 0.010583 + 0.010875 + 0.009875 + 0.011292 + 0.011417 + 0.009834 + 0.011417 + 0.011 + 0.010459 + 0.010959 + 0.011083 + 0.01125 + 0.010792 + 0.010625 + 0.009709 + 0.011417 + 0.012042 + 0.011542 + 0.010833 + 0.010709 + 0.011334 + 0.011958 + 0.01025 + 0.011375 + 0.012208 + 0.011416 + 0.014791 + 0.012541 + 0.011375 + 0.011458 + 0.011833 + 0.010959 + 0.011375 + 0.011417 + 0.010625 + 0.014291 + 0.01175 + 0.011 + 0.011542 + 0.010292 + 0.011209 + 0.011583 + 0.010417 + 0.011375 + 0.011708 + 0.01075 + 0.011583 + 0.011209 + 0.009583 + 0.011916 + 0.011667 + 0.011084 + 0.015125 + 0.0125 + 0.010792 + 0.012375 + 0.012291 + 0.012667 + 0.015417 + 0.0105 + 0.012125 + 0.012334 + 0.011292 + 0.011208 + 0.0115 + 0.014667 + 0.013042 + 0.010375 + 0.009459 + 0.010333 + 0.010625 + 0.009208 + 0.01025 + 0.009625 + 0.00975 + 0.012 + 0.0095 + 0.009708 + 0.0125 + 0.012125 + 0.011583 + 0.011667 + 0.009584 + 0.011083 + 0.011208 + 0.01025 + 0.0105 + 0.011208 + 0.011541 + 0.011542 + 0.01075 + 0.009916 + 0.011958 + 0.011625 + 0.010666 + 0.012083 + 0.011667 + 0.012542 + 0.011667 + 0.010709 + 0.011958 + 0.011375 + 0.010417 + 0.011625 + 0.011708 + 0.010791 + 0.012 + 0.011667 + 0.010708 + 0.011541 + 0.01175 + 0.010583 + 0.012 + 0.011584 + 0.010875 + 0.012833 + 0.012167 + 0.011792 + 0.011667 + 0.011084 + 0.017583 + 0.011416 + 0.011208 + 0.012041 + 0.012083 + 0.012166 + 0.016417 + 0.013459 + 0.011667 + 0.013417 + 0.012709 + 0.011833 + 0.012416 + 0.012333 + 0.0115 + 0.012125 + 0.011958 + 0.011875 + 0.012083 + 0.010875 + 0.01175 + 0.014958 + 0.0115 + 0.01275 + 0.011625 + 0.011042 + 0.011625 + 0.011917 + 0.010917 + 0.012375 + 0.012 + 0.011 + 0.013125 + 0.012458 + 0.01175 + 0.015792 + 0.013708 + 0.012542 + 0.012834 + 0.010792 + 0.012208 + 0.012125 + 0.015917 + 0.012917 + 0.013083 + 0.012083 + 0.01275 + 0.012458 + 0.010125 + 0.014042 + 0.011708 + 0.010042 + 0.011334 + 0.011417 + 0.009833 + 0.015542 + 0.01025 + 0.011084 + 0.010208 + 0.010583 + 0.01075 + 0.011125 + 0.009833 + 0.011208 + 0.011375 + 0.010042 + 0.011167 + 0.010792 + 0.009792 + 0.010833 + 0.011375 + 0.01 + 0.011584 + 0.01125 + 0.010625 + 0.010417 + 0.0105 + 0.010833 + 0.01075 + 0.010125 + 0.01125 + 0.011083 + 0.01 + 0.01075 + 0.010916 + 0.010541 + 0.011333 + 0.011083 + 0.010292 + 0.011375 + 0.011041 + 0.010083 + 0.010458 + 0.011709 + 0.010083 + 0.011083 + 0.011584 + 0.014417 + 0.012333 + 0.010583 + 0.011292 + 0.011959 + 0.010458 + 0.0115 + 0.012292 + 0.011833 + 0.011667 + 0.012042 + 0.01425 + 0.012792 + 0.012167 + 0.011375 + 0.012667 + 0.011292 + 0.010125 + 0.012459 + 0.011625 + 0.010958 + 0.014791 + 0.011875 + 0.011209 + 0.011584 + 0.011 + 0.011584 + 0.011708 + 0.010375 + 0.011667 + 0.011917 + 0.010042 + 0.011292 + 0.01125 + 0.010417 + 0.012584 + 0.01175 + 0.013709 + 0.01225 + 0.01175 + 0.011708 + 0.01175 + 0.01025 + 0.010958 + 0.012667 + 0.01375 + 0.012541 + 0.011416 + 0.010916 + 0.012417 + 0.011625 + 0.008875 + 0.010291 + 0.009625 + 0.009167 + 0.010292 + 0.010125 + 0.009084 + 0.01 + 0.010667 + 0.0095 + 0.00925 + 0.008917 + 0.010583 + 0.010917 + 0.010292 + 0.010375 + 0.011416 + 0.009625 + 0.010708 + 0.010541 + 0.010042 + 0.010167 + 0.010125 + 0.009125 + 0.010875 + 0.010791 + 0.00975 + 0.010667 + 0.010709 + 0.010292 + 0.010875 + 0.0095 + 0.010917 + 0.010833 + 0.009875 + 0.010541 + 0.010416 + 0.010041 + 0.010583 + 0.010667 + 0.009708 + 0.010625 + 0.011 + 0.009333 + 0.010166 + 0.010416 + 0.009916 + 0.010458 + 0.011959 + 0.015083 + 0.011041 + 0.010333 + 0.010959 + 0.010625 + 0.010167 + 0.010875 + 0.010625 + 0.009917 + 0.012041 + 0.011458 + 0.010375 + 0.014708 + 0.012625 + 0.010541 + 0.011 + 0.011084 + 0.009833 + 0.013959 + 0.011875 + 0.010708 + 0.010875 + 0.009792 + 0.010458 + 0.011125 + 0.010167 + 0.016625 + 0.010625 + 0.009666 + 0.010666 + 0.010625 + 0.009709 + 0.01075 + 0.01075 + 0.010208 + 0.017333 + 0.045375 + 0.014292 + 0.014125 + 0.012 + 0.011959 + 0.011583 + 0.010333 + 0.011333 + 0.01175 + 0.013333 + 0.012 + 0.011625 + 0.010583 + 0.01125 + 0.011125 + 0.008625 + 0.009333 + 0.009625 + 0.009333 + 0.009625 + 0.010834 + 0.009542 + 0.011417 + 0.013708 + 0.011875 + 0.009709 + 0.01 + 0.011708 + 0.011709 + 0.010375 + 0.011 + 0.010625 + 0.010333 + 0.0115 + 0.011042 + 0.010625 + 0.011792 + 0.011042 + 0.009875 + 0.010708 + 0.011959 + 0.009791 + 0.011333 + 0.010833 + 0.011667 + 0.011166 + 0.010833 + 0.011334 + 0.011375 + 0.010542 + 0.011 + 0.011041 + 0.010458 + 0.011125 + 0.011041 + 0.010458 + 0.011 + 0.011375 + 0.010375 + 0.01125 + 0.011084 + 0.009334 + 0.010167 + 0.011083 + 0.010916 + 0.013458 + 0.009959 + 0.010959 + 0.010708 + 0.009542 + 0.010584 + 0.010542 + 0.009958 + 0.011875 + 0.011625 + 0.010709 + 0.01425 + 0.012083 + 0.010375 + 0.010875 + 0.010375 + 0.009375 + 0.011 + 0.010291 + 0.010459 + 0.010959 + 0.009667 + 0.010209 + 0.010333 + 0.009667 + 0.012834 + 0.010583 + 0.009625 + 0.010417 + 0.010417 + 0.009708 + 0.01025 + 0.010167 + 0.009792 + 0.010334 + 0.011292 + 0.01025 + 0.010917 + 0.013416 + 0.01175 + 0.011458 + 0.010292 + 0.010792 + 0.011666 + 0.010208 + 0.014166 + 0.011375 + 0.010375 + 0.011167 + 0.011667 + 0.00975 + 0.010042 + 0.009875 + 0.008791 + 0.009125 + 0.009291 + 0.008291 + 0.009166 + 0.009291 + 0.011708 + 0.009083 + 0.008875 + 0.010834 + 0.010334 + 0.009459 + 0.0105 + 0.0105 + 0.009208 + 0.009667 + 0.010291 + 0.009333 + 0.010084 + 0.009958 + 0.009458 + 0.010125 + 0.010917 + 0.009875 + 0.010709 + 0.009834 + 0.01125 + 0.010458 + 0.009583 + 0.010667 + 0.009667 + 0.009375 + 0.0095 + 0.010208 + 0.009375 + 0.010625 + 0.010083 + 0.009208 + 0.009792 + 0.010208 + 0.009 + 0.009875 + 0.0105 + 0.009167 + 0.010417 + 0.011375 + 0.01075 + 0.011417 + 0.01 + 0.010917 + 0.010959 + 0.009042 + 0.010917 + 0.010625 + 0.01 + 0.01475 + 0.01175 + 0.010667 + 0.018208 + 0.012333 + 0.012041 + 0.012 + 0.012583 + 0.010583 + 0.0115 + 0.011625 + 0.011209 + 0.011625 + 0.014 + 0.011791 + 0.01125 + 0.01075 + 0.025209 + 0.012167 + 0.010333 + 0.011834 + 0.011417 + 0.010791 + 0.011292 + 0.01175 + 0.010292 + 0.011333 + 0.012 + 0.011542 + 0.019125 + 0.012916 + 0.011667 + 0.011625 + 0.010833 + 0.011209 + 0.015542 + 0.01925 + 0.012291 + 0.011625 + 0.011291 + 0.012792 + 0.012375 + 0.017917 + 0.01025 + 0.010417 + 0.01 + 0.00975 + 0.010375 + 0.009417 + 0.010125 + 0.01275 + 0.01025 + 0.009708 + 0.009416 + 0.011834 + 0.011166 + 0.010334 + 0.011375 + 0.011125 + 0.010667 + 0.011125 + 0.011708 + 0.010125 + 0.011584 + 0.011292 + 0.010417 + 0.011541 + 0.011583 + 0.010667 + 0.01225 + 0.011042 + 0.011375 + 0.01125 + 0.010125 + 0.011125 + 0.011208 + 0.010375 + 0.01125 + 0.011417 + 0.0105 + 0.011209 + 0.011709 + 0.010791 + 0.011375 + 0.011833 + 0.010667 + 0.011833 + 0.011208 + 0.010583 + 0.011542 + 0.011541 + 0.015334 + 0.01125 + 0.011084 + 0.011875 + 0.0125 + 0.011666 + 0.012083 + 0.011709 + 0.0105 + 0.01225 + 0.011792 + 0.013958 + 0.012083 + 0.011917 + 0.010667 + 0.011625 + 0.01175 + 0.010583 + 0.010791 + 0.0165 + 0.011167 + 0.011 + 0.010125 + 0.011333 + 0.011083 + 0.010458 + 0.011083 + 0.011917 + 0.010167 + 0.011333 + 0.011083 + 0.0095 + 0.010916 + 0.011334 + 0.010375 + 0.011083 + 0.012 + 0.01575 + 0.012375 + 0.011333 + 0.012083 + 0.011458 + 0.011958 + 0.014791 + 0.010875 + 0.011208 + 0.011583 + 0.012084 + 0.010958 + 0.01175 + 0.014166 + 0.010917 + 0.010375 + 0.010584 + 0.008875 + 0.010292 + 0.00975 + 0.008708 + 0.009666 + 0.0095 + 0.009458 + 0.009292 + 0.008375 + 0.009833 + 0.011125 + 0.010042 + 0.011083 + 0.010791 + 0.010084 + 0.010542 + 0.01075 + 0.009875 + 0.01025 + 0.010917 + 0.009958 + 0.010667 + 0.010542 + 0.009583 + 0.010458 + 0.011083 + 0.01 + 0.010791 + 0.009 + 0.010375 + 0.010583 + 0.009209 + 0.010292 + 0.010416 + 0.009208 + 0.010375 + 0.009916 + 0.009625 + 0.010458 + 0.009791 + 0.009583 + 0.010125 + 0.010042 + 0.00925 + 0.010375 + 0.010167 + 0.010875 + 0.010291 + 0.009708 + 0.010459 + 0.010916 + 0.009792 + 0.010167 + 0.010125 + 0.010167 + 0.010791 + 0.013458 + 0.020583 + 0.011625 + 0.019458 + 0.0105 + 0.011416 + 0.011208 + 0.009625 + 0.01025 + 0.010917 + 0.021 + 0.01175 + 0.009292 + 0.010916 + 0.010417 + 0.009541 + 0.010542 + 0.012625 + 0.009208 + 0.010292 + 0.010208 + 0.009292 + 0.010583 + 0.010208 + 0.009917 + 0.01125 + 0.014542 + 0.013375 + 0.011542 + 0.010708 + 0.011583 + 0.010708 + 0.010459 + 0.014666 + 0.010167 + 0.016458 + 0.011333 + 0.011333 + 0.010083 + 0.011083 + 0.020208 + 0.011125 + 0.009583 + 0.015167 + 0.008375 + 0.00975 + 0.009666 + 0.0115 + 0.009416 + 0.00975 + 0.009417 + 0.009125 + 0.008125 + 0.008667 + 0.010875 + 0.021959 + 0.010584 + 0.010166 + 0.05875 + 0.035959 + 0.013542 + 0.01125 + 0.012 + 0.012083 + 0.011042 + 0.012042 + 0.012291 + 0.011583 + 0.012208 + 0.0115 + 0.011875 + 0.0125 + 0.011209 + 0.012167 + 0.012333 + 0.015333 + 0.017583 + 0.012792 + 0.012042 + 0.024083 + 0.024333 + 0.01125 + 0.012417 + 0.012291 + 0.012375 + 0.012792 + 0.012208 + 0.011333 + 0.012541 + 0.011917 + 0.014209 + 0.014792 + 0.011417 + 0.013458 + 0.01225 + 0.011333 + 0.0145 + 0.013541 + 0.011542 + 0.018792 + 0.025292 + 0.017 + 0.014958 + 0.015459 + 0.013667 + 0.015541 + 0.016959 + 0.0155 + 0.016542 + 0.01925 + 0.016917 + 0.017333 + 0.014458 + 0.014583 + 0.016625 + 0.016 + 0.017958 + 0.017083 + 0.015417 + 0.013959 + 0.016833 + 0.017208 + 0.017708 + 0.019333 + 0.014417 + 0.017208 + 0.014375 + 0.017791 + 0.01625 + 0.016959 + 0.029209 + 0.034417 + 0.022458 + 0.038916 + 0.025459 + 0.022333 + 0.012875 + 0.012958 + 0.011917 + 0.012667 + 0.015917 + 0.011709 + 0.012208 + 0.010833 + 0.01025 + 0.010458 + 0.011583 + 0.010875 + 0.010667 + 0.010042 + 0.0105 + 0.009584 + 0.0075 + 0.009209 + 0.010833 + 0.009875 + 0.010459 + 0.010584 + 0.009583 + 0.010458 + 0.010583 + 0.01 + 0.010708 + 0.01075 + 0.009583 + 0.010083 + 0.01025 + 0.009375 + 0.009917 + 0.01025 + 0.010375 + 0.010292 + 0.009666 + 0.010417 + 0.010709 + 0.00975 + 0.010708 + 0.010333 + 0.009291 + 0.010292 + 0.010416 + 0.009208 + 0.010209 + 0.010833 + 0.009583 + 0.010333 + 0.009917 + 0.009 + 0.011167 + 0.011083 + 0.02125 + 0.012916 + 0.011542 + 0.012417 + 0.022208 + 0.012 + 0.012417 + 0.011417 + 0.011416 + 0.020958 + 0.017292 + 0.012125 + 0.012 + 0.012084 + 0.011125 + 0.012 + 0.015125 + 0.012125 + 0.0125 + 0.012292 + 0.012 + 0.013167 + 0.011208 + 0.011541 + 0.010833 + 0.009959 + 0.010959 + 0.012417 + 0.010708 + 0.011 + 0.010958 + 0.009916 + 0.010875 + 0.013041 + 0.010208 + 0.011042 + 0.010625 + 0.011083 + 0.011125 + 0.01125 + 0.010792 + 0.011125 + 0.015792 + 0.0115 + 0.010375 + 0.010334 + 0.011667 + 0.010791 + 0.011333 + 0.014625 + 0.012 + 0.011833 + 0.012708 + 0.010833 + 0.009958 + 0.01 + 0.009292 + 0.009125 + 0.009208 + 0.009083 + 0.0255 + 0.014084 + 0.015333 + 0.013 + 0.019083 + 0.01525 + 0.01425 + 0.013958 + 0.012917 + 0.016333 + 0.016625 + 0.028459 + 0.0185 + 0.012667 + 0.010667 + 0.012666 + 0.011208 + 0.010583 + 0.011125 + 0.0115 + 0.011666 + 0.011292 + 0.009875 + 0.011375 + 0.0115 + 0.010916 + 0.011792 + 0.011833 + 0.009584 + 0.011208 + 0.011084 + 0.010125 + 0.011208 + 0.010458 + 0.010333 + 0.010875 + 0.011125 + 0.009541 + 0.010792 + 0.011125 + 0.011375 + 0.011583 + 0.0145 + 0.012375 + 0.011916 + 0.010042 + 0.011209 + 0.011084 + 0.011125 + 0.011792 + 0.015541 + 0.011417 + 0.012334 + 0.02175 + 0.012 + 0.01225 + 0.012958 + 0.010875 + 0.011791 + 0.011792 + 0.011792 + 0.01125 + 0.010958 + 0.021041 + 0.011625 + 0.011 + 0.0115 + 0.015334 + 0.01525 + 0.011 + 0.012042 + 0.010542 + 0.010917 + 0.011666 + 0.010833 + 0.012042 + 0.011333 + 0.012125 + 0.021375 + 0.01625 + 0.012792 + 0.01225 + 0.011791 + 0.012875 + 0.01175 + 0.010625 + 0.015041 + 0.012917 + 0.01125 + 0.01275 + 0.012125 + 0.01175 + 0.014542 + 0.011041 + 0.010209 + 0.010417 + 0.011625 + 0.009292 + 0.010875 + 0.010833 + 0.010791 + 0.013167 + 0.012 + 0.010292 + 0.013667 + 0.011583 + 0.011 + 0.01175 + 0.010542 + 0.011959 + 0.011375 + 0.011709 + 0.012125 + 0.011958 + 0.010584 + 0.011667 + 0.011625 + 0.010541 + 0.011458 + 0.01175 + 0.012 + 0.011042 + 0.010875 + 0.011375 + 0.011667 + 0.011166 + 0.011542 + 0.012292 + 0.011083 + 0.011333 + 0.012 + 0.011 + 0.011666 + 0.011792 + 0.010792 + 0.012041 + 0.011958 + 0.012583 + 0.013833 + 0.01275 + 0.011333 + 0.012916 + 0.011833 + 0.012042 + 0.011666 + 0.011417 + 0.012625 + 0.015084 + 0.010709 + 0.012167 + 0.012541 + 0.013125 + 0.011875 + 0.016291 + 0.01425 + 0.013416 + 0.012375 + 0.011958 + 0.013167 + 0.011333 + 0.012083 + 0.011 + 0.01025 + 0.011584 + 0.014167 + 0.011417 + 0.012291 + 0.011084 + 0.01025 + 0.01125 + 0.010875 + 0.009875 + 0.010959 + 0.011791 + 0.01025 + 0.011416 + 0.0115 + 0.0115 + 0.013875 + 0.023459 + 0.022083 + 0.017125 + 0.013625 + 0.016458 + 0.015958 + 0.014208 + 0.016834 + 0.012417 + 0.011791 + 0.013583 + 0.012833 + 0.011417 + 0.017209 + 0.012167 + 0.011458 + 0.0135 + 0.013208 + 0.011 + 0.0125 + 0.012917 + 0.012583 + 0.018375 + 0.009292 + 0.010833 + 0.013042 + 0.011209 + 0.0125 + 0.012792 + 0.021333 + 0.012833 + 0.020792 + 0.012167 + 0.012875 + 0.014625 + 0.01225 + 0.019584 + 0.013125 + 0.011375 + 0.011834 + 0.018375 + 0.015042 + 0.012 + 0.010542 + 0.011666 + 0.0115 + 0.010667 + 0.011666 + 0.011792 + 0.010542 + 0.011583 + 0.01075 + 0.01125 + 0.012417 + 0.012083 + 0.010541 + 0.011833 + 0.010625 + 0.013875 + 0.0115 + 0.011833 + 0.012916 + 0.016792 + 0.014042 + 0.014875 + 0.01475 + 0.013208 + 0.014167 + 0.013375 + 0.010917 + 0.020458 + 0.016 + 0.012291 + 0.012792 + 0.01575 + 0.012292 + 0.013416 + 0.012708 + 0.011625 + 0.013167 + 0.012333 + 0.012291 + 0.012167 + 0.01125 + 0.012292 + 0.0145 + 0.011625 + 0.012541 + 0.013 + 0.017916 + 0.01375 + 0.014958 + 0.01175 + 0.011833 + 0.012541 + 0.011125 + 0.012375 + 0.012083 + 0.010958 + 0.013917 + 0.012625 + 0.02225 + 0.013125 + 0.012125 + 0.013041 + 0.01275 + 0.011 + 0.012708 + 0.015833 + 0.012083 + 0.012917 + 0.012791 + 0.011625 + 0.012833 + 0.015166 + 0.010125 + 0.010917 + 0.01125 + 0.009667 + 0.0115 + 0.011917 + 0.011041 + 0.013583 + 0.008583 + 0.010167 + 0.010459 + 0.011334 + 0.011667 + 0.01125 + 0.0105 + 0.01125 + 0.0115 + 0.010708 + 0.011667 + 0.0115 + 0.0105 + 0.012375 + 0.015583 + 0.011083 + 0.011584 + 0.011833 + 0.0115 + 0.011792 + 0.010541 + 0.012167 + 0.01175 + 0.010583 + 0.011834 + 0.011542 + 0.010458 + 0.011542 + 0.0115 + 0.011292 + 0.012042 + 0.011875 + 0.010542 + 0.011375 + 0.011666 + 0.010625 + 0.011375 + 0.011625 + 0.01125 + 0.012 + 0.011416 + 0.011833 + 0.011208 + 0.010667 + 0.011459 + 0.011334 + 0.010792 + 0.011208 + 0.011292 + 0.011708 + 0.011875 + 0.015041 + 0.011417 + 0.011625 + 0.011042 + 0.010625 + 0.011916 + 0.011167 + 0.011 + 0.01425 + 0.010625 + 0.011417 + 0.011 + 0.01 + 0.010542 + 0.010833 + 0.009958 + 0.011167 + 0.014417 + 0.009916 + 0.010542 + 0.010666 + 0.00975 + 0.011083 + 0.011208 + 0.010042 + 0.02675 + 0.015 + 0.011834 + 0.012125 + 0.011 + 0.018167 + 0.0115 + 0.010209 + 0.014625 + 0.012417 + 0.01075 + 0.013334 + 0.011709 + 0.011041 + 0.014417 + 0.011792 + 0.010083 + 0.0105 + 0.010583 + 0.009875 + 0.010416 + 0.010167 + 0.009458 + 0.015125 + 0.008208 + 0.009708 + 0.009916 + 0.010167 + 0.011417 + 0.010708 + 0.010083 + 0.011333 + 0.010834 + 0.010125 + 0.010666 + 0.010167 + 0.010125 + 0.025083 + 0.017292 + 0.009791 + 0.010583 + 0.010708 + 0.010375 + 0.010542 + 0.0095 + 0.010583 + 0.010083 + 0.008667 + 0.010291 + 0.01075 + 0.009958 + 0.0105 + 0.01025 + 0.009667 + 0.010333 + 0.010333 + 0.009042 + 0.010792 + 0.010333 + 0.008958 + 0.012875 + 0.010708 + 0.010333 + 0.011208 + 0.013208 + 0.010542 + 0.011292 + 0.010625 + 0.010625 + 0.010875 + 0.009333 + 0.01075 + 0.011125 + 0.02075 + 0.015 + 0.012083 + 0.011084 + 0.010875 + 0.011125 + 0.01025 + 0.015208 + 0.0105 + 0.010458 + 0.010791 + 0.009458 + 0.010917 + 0.01025 + 0.009625 + 0.009708 + 0.010458 + 0.009666 + 0.01025 + 0.0105 + 0.009875 + 0.010792 + 0.010709 + 0.009709 + 0.010125 + 0.010208 + 0.009667 + 0.011291 + 0.014208 + 0.011542 + 0.011375 + 0.01025 + 0.011666 + 0.012083 + 0.013084 + 0.011 + 0.013792 + 0.010041 + 0.010917 + 0.011209 + 0.014291 + 0.014417 + 0.01225 + 0.008833 + 0.009375 + 0.009458 + 0.008458 + 0.009958 + 0.011 + 0.027958 + 0.016625 + 0.013458 + 0.014375 + 0.011209 + 0.012458 + 0.015542 + 0.018 + 0.0145 + 0.0175 + 0.016875 + 0.01575 + 0.016041 + 0.01475 + 0.018375 + 0.017042 + 0.016166 + 0.014792 + 0.014709 + 0.015542 + 0.017791 + 0.020375 + 0.014541 + 0.015917 + 0.013292 + 0.015125 + 0.017334 + 0.014167 + 0.015416 + 0.0175 + 0.018459 + 0.013125 + 0.013084 + 0.015125 + 0.014291 + 0.016125 + 0.018042 + 0.016166 + 0.016167 + 0.0175 + 0.017708 + 0.014167 + 0.016042 + 0.021125 + 0.018167 + 0.0155 + 0.015042 + 0.01775 + 0.016542 + 0.016542 + 0.015666 + 0.0135 + 0.016958 + 0.0135 + 0.01275 + 0.01425 + 0.017292 + 0.014875 + 0.013916 + 0.014292 + 0.013 + 0.014459 + 0.011875 + 0.012292 + 0.012959 + 0.011583 + 0.021167 + 0.013083 + 0.013833 + 0.014292 + 0.015208 + 0.015792 + 0.037584 + 0.025 + 0.014083 + 0.014791 + 0.01475 + 0.016083 + 0.021125 + 0.0175 + 0.017583 + 0.020875 + 0.013917 + 0.015 + 0.014458 + 0.013667 + 0.016 + 0.017917 + 0.031333 + 0.019833 + 0.015333 + 0.012625 + 0.013917 + 0.01425 + 0.015375 + 0.016959 + 0.018125 + 0.014167 + 0.013125 + 0.012917 + 0.011917 + 0.012833 + 0.014083 + 0.01275 + 0.014375 + 0.015708 + 0.014667 + 0.013708 + 0.011833 + 0.01375 + 0.013667 + 0.011833 + 0.011916 + 0.011667 + 0.010583 + 0.01225 + 0.012333 + 0.011125 + 0.0115 + 0.011208 + 0.011791 + 0.012333 + 0.011583 + 0.012042 + 0.012167 + 0.012166 + 0.011458 + 0.011833 + 0.011041 + 0.012166 + 0.012083 + 0.011125 + 0.012083 + 0.016542 + 0.010667 + 0.012042 + 0.011708 + 0.012083 + 0.012125 + 0.012875 + 0.01375 + 0.015875 + 0.014458 + 0.013792 + 0.016917 + 0.014625 + 0.013625 + 0.013958 + 0.013916 + 0.016209 + 0.016875 + 0.015334 + 0.0175 + 0.015125 + 0.015208 + 0.014458 + 0.013083 + 0.024542 + 0.014667 + 0.013 + 0.012375 + 0.012708 + 0.01175 + 0.012458 + 0.012959 + 0.017166 + 0.015042 + 0.012625 + 0.010709 + 0.0115 + 0.013 + 0.01175 + 0.019333 + 0.011959 + 0.01125 + 0.012458 + 0.013042 + 0.013708 + 0.011875 + 0.014042 + 0.013041 + 0.016625 + 0.014833 + 0.013791 + 0.013167 + 0.012292 + 0.012625 + 0.01425 + 0.016 + 0.015958 + 0.014666 + 0.012458 + 0.013542 + 0.012709 + 0.012167 + 0.010666 + 0.011042 + 0.0105 + 0.010917 + 0.01125 + 0.01125 + 0.011834 + 0.013334 + 0.009959 + 0.010416 + 0.009083 + 0.0125 + 0.016417 + 0.011083 + 0.012042 + 0.011584 + 0.010916 + 0.01175 + 0.01275 + 0.014167 + 0.01525 + 0.014833 + 0.012041 + 0.012 + 0.015166 + 0.015875 + 0.016083 + 0.014708 + 0.0155 + 0.016042 + 0.012375 + 0.012583 + 0.012875 + 0.011334 + 0.01425 + 0.011875 + 0.01075 + 0.011333 + 0.011792 + 0.010792 + 0.012333 + 0.011875 + 0.010666 + 0.012209 + 0.011792 + 0.01225 + 0.0115 + 0.011417 + 0.011916 + 0.020708 + 0.014125 + 0.012708 + 0.011958 + 0.011584 + 0.013458 + 0.0125 + 0.011125 + 0.016083 + 0.012791 + 0.015375 + 0.01425 + 0.013 + 0.012459 + 0.0135 + 0.014375 + 0.015583 + 0.016708 + 0.014083 + 0.013917 + 0.014375 + 0.013291 + 0.016625 + 0.015333 + 0.015625 + 0.017208 + 0.017625 + 0.013375 + 0.014875 + 0.015 + 0.012833 + 0.014958 + 0.014916 + 0.014417 + 0.013667 + 0.015916 + 0.018958 + 0.016458 + 0.015083 + 0.017042 + 0.063791 + 0.044584 + 0.019042 + 0.022291 + 0.015583 + 0.014167 + 0.013875 + 0.012959 + 0.013583 + 0.016791 + 0.012625 + 0.012625 + 0.011583 + 0.010666 + 0.011833 + 0.011958 + 0.01175 + 0.011334 + 0.011625 + 0.013417 + 0.010417 + 0.009625 + 0.012333 + 0.0135 + 0.011458 + 0.012167 + 0.011792 + 0.011209 + 0.012584 + 0.012834 + 0.0115 + 0.011667 + 0.012583 + 0.010875 + 0.012208 + 0.012334 + 0.012208 + 0.012416 + 0.011167 + 0.013709 + 0.015709 + 0.01375 + 0.0145 + 0.01375 + 0.013875 + 0.014125 + 0.013583 + 0.01425 + 0.014166 + 0.013917 + 0.013791 + 0.013917 + 0.014208 + 0.013375 + 0.014458 + 0.015042 + 0.014125 + 0.014708 + 0.0135 + 0.01875 + 0.015792 + 0.014083 + 0.015083 + 0.012542 + 0.01225 + 0.012959 + 0.013291 + 0.012417 + 0.015875 + 0.016417 + 0.013375 + 0.013667 + 0.013625 + 0.013083 + 0.0145 + 0.017125 + 0.015542 + 0.013291 + 0.013709 + 0.012709 + 0.012666 + 0.013125 + 0.013083 + 0.014083 + 0.0145 + 0.018542 + 0.017875 + 0.013958 + 0.015708 + 0.014542 + 0.011917 + 0.013 + 0.012333 + 0.011917 + 0.01425 + 0.016334 + 0.013917 + 0.013541 + 0.012167 + 0.013417 + 0.013375 + 0.015667 + 0.014291 + 0.012958 + 0.013209 + 0.013167 + 0.01325 + 0.013125 + 0.016416 + 0.01525 + 0.012333 + 0.012583 + 0.01175 + 0.010625 + 0.011416 + 0.011042 + 0.011791 + 0.011958 + 0.009292 + 0.013708 + 0.015875 + 0.009917 + 0.012042 + 0.01275 + 0.011167 + 0.011959 + 0.011667 + 0.011542 + 0.012417 + 0.012584 + 0.011125 + 0.012125 + 0.012291 + 0.011333 + 0.011916 + 0.012375 + 0.012083 + 0.012958 + 0.010042 + 0.011917 + 0.011458 + 0.010667 + 0.012541 + 0.012458 + 0.01125 + 0.015458 + 0.012208 + 0.010667 + 0.012458 + 0.011958 + 0.011916 + 0.012917 + 0.012583 + 0.012292 + 0.012166 + 0.012458 + 0.012792 + 0.0125 + 0.013167 + 0.014916 + 0.014542 + 0.013709 + 0.012334 + 0.01325 + 0.012459 + 0.01325 + 0.015167 + 0.012958 + 0.014208 + 0.018125 + 0.01425 + 0.014708 + 0.013917 + 0.013084 + 0.014417 + 0.031375 + 0.014541 + 0.0125 + 0.012291 + 0.012541 + 0.019917 + 0.027791 + 0.013708 + 0.013542 + 0.012625 + 0.013208 + 0.0125 + 0.011584 + 0.012417 + 0.013333 + 0.012292 + 0.01275 + 0.01225 + 0.011083 + 0.012292 + 0.0225 + 0.015875 + 0.013208 + 0.012125 + 0.013125 + 0.019291 + 0.012375 + 0.012666 + 0.027 + 0.011958 + 0.013958 + 0.013292 + 0.014917 + 0.013792 + 0.012875 + 0.016791 + 0.011667 + 0.011375 + 0.010958 + 0.010958 + 0.01175 + 0.0155 + 0.012167 + 0.012041 + 0.014709 + 0.011292 + 0.008833 + 0.01325 + 0.013959 + 0.011541 + 0.01225 + 0.012125 + 0.011333 + 0.012208 + 0.012209 + 0.011917 + 0.012125 + 0.012208 + 0.010958 + 0.011917 + 0.013459 + 0.011916 + 0.0115 + 0.0115 + 0.01575 + 0.014084 + 0.012209 + 0.0125 + 0.013333 + 0.01175 + 0.012834 + 0.013125 + 0.0125 + 0.01325 + 0.012792 + 0.012416 + 0.014209 + 0.014584 + 0.012625 + 0.012834 + 0.013167 + 0.012583 + 0.013166 + 0.012209 + 0.013417 + 0.01375 + 0.0155 + 0.016542 + 0.013666 + 0.012333 + 0.013375 + 0.012625 + 0.014334 + 0.015542 + 0.016625 + 0.014583 + 0.028958 + 0.03275 + 0.016875 + 0.014875 + 0.015541 + 0.014875 + 0.012875 + 0.013208 + 0.015958 + 0.01625 + 0.013584 + 0.013042 + 0.013333 + 0.012416 + 0.014792 + 0.013667 + 0.012167 + 0.013208 + 0.013208 + 0.012042 + 0.013167 + 0.01425 + 0.012125 + 0.013583 + 0.012917 + 0.014459 + 0.013875 + 0.012583 + 0.01775 + 0.014041 + 0.012375 + 0.014166 + 0.016875 + 0.015 + 0.018334 + 0.018041 + 0.018292 + 0.01625 + 0.016792 + 0.015167 + 0.015208 + 0.017416 + 0.01375 + 0.014708 + 0.014958 + 0.015375 + 0.015291 + 0.014625 + 0.020666 + 0.015625 + 0.015333 + 0.014125 + 0.020083 + 0.013708 + 0.013666 + 0.013459 + 0.012459 + 0.013584 + 0.0185 + 0.012375 + 0.012209 + 0.012041 + 0.011458 + 0.012458 + 0.01275 + 0.013 + 0.013125 + 0.012125 + 0.012708 + 0.012333 + 0.011 + 0.012375 + 0.012042 + 0.010834 + 0.013834 + 0.014167 + 0.012417 + 0.014916 + 0.015625 + 0.014958 + 0.015083 + 0.012917 + 0.011292 + 0.012 + 0.012 + 0.012125 + 0.012333 + 0.011375 + 0.013125 + 0.014291 + 0.016958 + 0.015833 + 0.015833 + 0.01275 + 0.012708 + 0.012333 + 0.011625 + 0.013292 + 0.013958 + 0.0125 + 0.013375 + 0.017709 + 0.015959 + 0.017667 + 0.013208 + 0.01325 + 0.013375 + 0.011917 + 0.012375 + 0.012041 + 0.011458 + 0.018291 + 0.016375 + 0.01475 + 0.013375 + 0.016875 + 0.015792 + 0.013916 + 0.01525 + 0.01325 + 0.014125 + 0.01375 + 0.01275 + 0.014666 + 0.013333 + 0.014625 + 0.015583 + 0.014916 + 0.014541 + 0.01425 + 0.013416 + 0.013917 + 0.014042 + 0.011708 + 0.014583 + 0.016458 + 0.013333 + 0.0145 + 0.014125 + 0.012916 + 0.014 + 0.013 + 0.011583 + 0.012541 + 0.012708 + 0.012083 + 0.012125 + 0.010584 + 0.012084 + 0.014292 + 0.010916 + 0.011625 + 0.014125 + 0.013208 + 0.013833 + 0.013292 + 0.011834 + 0.013542 + 0.013208 + 0.012083 + 0.014666 + 0.01375 + 0.0125 + 0.01325 + 0.01275 + 0.012875 + 0.013084 + 0.011791 + 0.012917 + 0.017 + 0.013959 + 0.014125 + 0.012709 + 0.013709 + 0.0135 + 0.013667 + 0.013042 + 0.014625 + 0.01325 + 0.011542 + 0.012916 + 0.014 + 0.013583 + 0.013167 + 0.01375 + 0.013709 + 0.013667 + 0.012208 + 0.012625 + 0.014083 + 0.013875 + 0.013625 + 0.015041 + 0.012708 + 0.013333 + 0.01325 + 0.011666 + 0.013083 + 0.012833 + 0.013459 + 0.014166 + 0.014708 + 0.01475 + 0.015708 + 0.014041 + 0.014417 + 0.01425 + 0.011667 + 0.014459 + 0.01375 + 0.011958 + 0.013417 + 0.013291 + 0.012542 + 0.013459 + 0.013792 + 0.013125 + 0.013416 + 0.013417 + 0.012333 + 0.013 + 0.013834 + 0.012375 + 0.01425 + 0.014208 + 0.017042 + 0.015209 + 0.013041 + 0.016375 + 0.017541 + 0.015625 + 0.017542 + 0.017666 + 0.012208 + 0.013708 + 0.020042 + 0.015875 + 0.017333 + 0.016541 + 0.017375 + 0.017125 + 0.011709 + 0.010916 + 0.014416 + 0.015083 + 0.013042 + 0.014041 + 0.013417 + 0.014917 + 0.034625 + 0.020625 + 0.013375 + 0.014292 + 0.015084 + 0.01325 + 0.013333 + 0.012375 + 0.013833 + 0.016583 + 0.014875 + 0.016041 + 0.016792 + 0.016584 + 0.0155 + 0.0145 + 0.016958 + 0.023 + 0.028666 + 0.017834 + 0.017709 + 0.0165 + 0.015875 + 0.014667 + 0.014 + 0.119666 + 0.018333 + 0.01425 + 0.0165 + 0.020083 + 0.01375 + 0.014375 + 0.014625 + 0.015041 + 0.016458 + 0.054959 + 0.018417 + 0.019542 + 0.014917 + 0.014667 + 0.016375 + 0.016959 + 0.0195 + 0.01575 + 0.014708 + 0.015833 + 0.016333 + 0.014541 + 0.015125 + 0.017291 + 0.017917 + 0.014542 + 0.018166 + 0.018917 + 0.018084 + 0.019459 + 0.019042 + 0.020417 + 0.014792 + 0.019458 + 0.016042 + 0.015584 + 0.016917 + 0.021083 + 0.016625 + 0.015667 + 0.018125 + 0.024 + 0.019291 + 0.017292 + 0.014833 + 0.012958 + 0.012875 + 0.016167 + 0.016875 + 0.018625 + 0.019792 + 0.013625 + 0.013042 + 0.018333 + 0.02 + 0.01625 + 0.015792 + 0.014542 + 0.014083 + 0.019083 + 0.018416 + 0.016417 + 0.022459 + 0.015209 + 0.014333 + 0.024416 + 0.021625 + 0.016458 + 0.030916 + 0.014084 + 0.012291 + 0.011875 + 0.010917 + 0.011792 + 0.01725 + 0.01025 + 0.01325 + 0.013041 + 0.013459 + 0.013584 + 0.012709 + 0.011542 + 0.012916 + 0.015583 + 0.014875 + 0.015792 + 0.019209 + 0.014208 + 0.015084 + 0.016166 + 0.014583 + 0.0135 + 0.012958 + 0.015875 + 0.015208 + 0.012833 + 0.013541 + 0.013542 + 0.012417 + 0.01325 + 0.013417 + 0.012458 + 0.013916 + 0.0135 + 0.012959 + 0.01425 + 0.012583 + 0.012417 + 0.013917 + 0.013166 + 0.013583 + 0.013042 + 0.012375 + 0.013041 + 0.015375 + 0.0135 + 0.014 + 0.013625 + 0.012459 + 0.014584 + 0.013584 + 0.012334 + 0.013584 + 0.014042 + 0.013417 + 0.014625 + 0.020959 + 0.014042 + 0.014083 + 0.016291 + 0.014 + 0.01875 + 0.014125 + 0.014667 + 0.014041 + 0.012959 + 0.013958 + 0.024125 + 0.013 + 0.02225 + 0.024375 + 0.014125 + 0.015959 + 0.013625 + 0.022084 + 0.014709 + 0.015334 + 0.021125 + 0.01425 + 0.047875 + 0.021416 + 0.02475 + 0.017959 + 0.018542 + 0.01375 + 0.013041 + 0.014542 + 0.017291 + 0.015708 + 0.023458 + 0.022625 + 0.013625 + 0.013667 + 0.014333 + 0.018166 + 0.018541 + 0.017542 + 0.013375 + 0.010875 + 0.010875 + 0.01425 + 0.011291 + 0.011083 + 0.011333 + 0.014583 + 0.009792 + 0.011458 + 0.010541 + 0.013292 + 0.012375 + 0.013 + 0.0105 + 0.011416 + 0.011917 + 0.010667 + 0.012334 + 0.01175 + 0.011417 + 0.013291 + 0.013291 + 0.012625 + 0.012417 + 0.011709 + 0.011917 + 0.011708 + 0.010916 + 0.011916 + 0.013375 + 0.013375 + 0.012459 + 0.012416 + 0.011 + 0.011833 + 0.012084 + 0.01075 + 0.011666 + 0.011625 + 0.010708 + 0.012083 + 0.012125 + 0.012208 + 0.012583 + 0.011833 + 0.012167 + 0.011667 + 0.012833 + 0.016083 + 0.014625 + 0.011541 + 0.024042 + 0.013667 + 0.012334 + 0.014792 + 0.017416 + 0.016042 + 0.0155 + 0.012417 + 0.015333 + 0.016708 + 0.014708 + 0.013416 + 0.012625 + 0.010792 + 0.011666 + 0.013292 + 0.012417 + 0.011375 + 0.015584 + 0.014084 + 0.015375 + 0.0135 + 0.010708 + 0.013083 + 0.012167 + 0.010792 + 0.011458 + 0.011792 + 0.011166 + 0.011959 + 0.011583 + 0.012333 + 0.012625 + 0.011958 + 0.020084 + 0.016875 + 0.016833 + 0.012208 + 0.011875 + 0.010916 + 0.011875 + 0.016625 + 0.014917 + 0.016541 + 0.014958 + 0.011334 + 0.014959 + 0.016459 + 0.011167 + 0.011041 + 0.011625 + 0.011625 + 0.010959 + 0.010417 + 0.011375 + 0.011542 + 0.01 + 0.011042 + 0.010958 + 0.01175 + 0.013 + 0.012625 + 0.010833 + 0.014416 + 0.0165 + 0.014416 + 0.013583 + 0.011958 + 0.011708 + 0.0125 + 0.011875 + 0.011959 + 0.011709 + 0.011042 + 0.012666 + 0.012333 + 0.011208 + 0.012042 + 0.011584 + 0.01075 + 0.012833 + 0.016542 + 0.014417 + 0.015791 + 0.012417 + 0.010541 + 0.012042 + 0.012042 + 0.011583 + 0.011834 + 0.012708 + 0.018416 + 0.012125 + 0.010792 + 0.012 + 0.011917 + 0.011708 + 0.012667 + 0.012959 + 0.012 + 0.013167 + 0.012416 + 0.011625 + 0.019666 + 0.019958 + 0.012542 + 0.021709 + 0.017 + 0.016875 + 0.017708 + 0.025834 + 0.017125 + 0.02075 + 0.013708 + 0.013167 + 0.014042 + 0.013125 + 0.013667 + 0.01325 + 0.0155 + 0.022667 + 0.01525 + 0.01325 + 0.013583 + 0.013167 + 0.012042 + 0.01275 + 0.013584 + 0.012292 + 0.013 + 0.013084 + 0.019833 + 0.013459 + 0.012417 + 0.017667 + 0.016 + 0.012875 + 0.0135 + 0.014041 + 0.011792 + 0.012375 + 0.015791 + 0.014166 + 0.012917 + 0.013083 + 0.013083 + 0.012875 + 0.023 + 0.012708 + 0.012916 + 0.011958 + 0.011667 + 0.012208 + 0.011833 + 0.016333 + 0.015583 + 0.016583 + 0.010833 + 0.010333 + 0.013875 + 0.017042 + 0.015584 + 0.01475 + 0.012458 + 0.011959 + 0.011166 + 0.012125 + 0.012125 + 0.010708 + 0.01175 + 0.011 + 0.012292 + 0.012 + 0.011708 + 0.012125 + 0.011625 + 0.010875 + 0.011125 + 0.012333 + 0.011833 + 0.01225 + 0.012167 + 0.011208 + 0.011167 + 0.015 + 0.010792 + 0.011583 + 0.012458 + 0.011208 + 0.011583 + 0.011875 + 0.011667 + 0.011459 + 0.01075 + 0.01225 + 0.012625 + 0.012208 + 0.011583 + 0.012167 + 0.011041 + 0.011792 + 0.011917 + 0.010916 + 0.012583 + 0.012417 + 0.011041 + 0.012709 + 0.013041 + 0.018292 + 0.015875 + 0.0135 + 0.012875 + 0.013334 + 0.011333 + 0.012 + 0.012041 + 0.0115 + 0.012291 + 0.013375 + 0.014792 + 0.014833 + 0.01275 + 0.011292 + 0.012 + 0.012542 + 0.011584 + 0.012834 + 0.012791 + 0.011458 + 0.012667 + 0.011916 + 0.012583 + 0.012208 + 0.013542 + 0.01375 + 0.020084 + 0.01425 + 0.013375 + 0.013208 + 0.011792 + 0.013584 + 0.012834 + 0.014459 + 0.023083 + 0.012834 + 0.013917 + 0.016292 + 0.021084 + 0.015208 + 0.011167 + 0.011166 + 0.011292 + 0.011667 + 0.010125 + 0.010833 + 0.010958 + 0.01325 + 0.009791 + 0.009667 + 0.020833 + 0.012416 + 0.01225 + 0.010416 + 0.01175 + 0.011375 + 0.010333 + 0.01125 + 0.01225 + 0.011708 + 0.0115 + 0.011541 + 0.01175 + 0.011583 + 0.010666 + 0.011375 + 0.011333 + 0.010875 + 0.011625 + 0.011875 + 0.010875 + 0.011834 + 0.011584 + 0.010791 + 0.011958 + 0.011042 + 0.011041 + 0.012458 + 0.011417 + 0.010709 + 0.011792 + 0.011708 + 0.011833 + 0.011792 + 0.011041 + 0.011292 + 0.011166 + 0.011292 + 0.014333 + 0.013583 + 0.013334 + 0.013292 + 0.012125 + 0.011 + 0.012125 + 0.011666 + 0.010625 + 0.01325 + 0.016166 + 0.014125 + 0.013666 + 0.012583 + 0.013333 + 0.01225 + 0.015333 + 0.015041 + 0.012459 + 0.011292 + 0.011958 + 0.01225 + 0.011167 + 0.01375 + 0.012459 + 0.011 + 0.011959 + 0.011958 + 0.010791 + 0.012 + 0.012667 + 0.010875 + 0.015166 + 0.014708 + 0.012709 + 0.012334 + 0.012125 + 0.013125 + 0.012542 + 0.011875 + 0.013 + 0.015541 + 0.0145 + 0.012625 + 0.0125 + 0.011875 + 0.012834 + 0.012625 + 0.015458 + 0.015375 + 0.012916 + 0.0115 + 0.011541 + 0.010875 + 0.010958 + 0.010958 + 0.010042 + 0.010833 + 0.010833 + 0.011542 + 0.009791 + 0.009792 + 0.008792 + 0.011333 + 0.010958 + 0.010166 + 0.0115 + 0.011542 + 0.010791 + 0.013292 + 0.012666 + 0.010625 + 0.011791 + 0.01525 + 0.017041 + 0.014167 + 0.012875 + 0.014125 + 0.013292 + 0.011917 + 0.013708 + 0.014292 + 0.070708 + 0.021792 + 0.013667 + 0.011625 + 0.012458 + 0.012166 + 0.011125 + 0.012292 + 0.011958 + 0.011042 + 0.011875 + 0.011834 + 0.011667 + 0.012 + 0.010666 + 0.012125 + 0.011833 + 0.011167 + 0.013625 + 0.01225 + 0.011667 + 0.012166 + 0.012209 + 0.011916 + 0.011917 + 0.017334 + 0.014834 + 0.018084 + 0.013292 + 0.011458 + 0.012916 + 0.013333 + 0.018042 + 0.017459 + 0.015208 + 0.012083 + 0.011917 + 0.01125 + 0.011958 + 0.012167 + 0.011583 + 0.011625 + 0.012291 + 0.01125 + 0.01275 + 0.01175 + 0.010958 + 0.01975 + 0.01625 + 0.0145 + 0.013042 + 0.011875 + 0.012459 + 0.012 + 0.012083 + 0.013083 + 0.012792 + 0.011958 + 0.017667 + 0.017167 + 0.015666 + 0.012708 + 0.011458 + 0.011959 + 0.012291 + 0.01975 + 0.017333 + 0.016708 + 0.015083 + 0.015958 + 0.015458 + 0.011667], + :filter-tx-shared-memo + [0.009125 + 0.007959 + 0.007458 + 0.007708 + 0.007416 + 0.007125 + 0.0075 + 0.007667 + 0.007375 + 0.007417 + 0.007458 + 0.007167 + 0.007625 + 0.007833 + 0.007458 + 0.007458 + 0.007792 + 0.007542 + 0.008125 + 0.00825 + 0.015083 + 0.007667 + 0.00725 + 0.007416 + 0.00775 + 0.007625 + 0.007791 + 0.007291 + 0.007625 + 0.007583 + 0.007667 + 0.007292 + 0.008959 + 0.007375 + 0.007209 + 0.015209 + 0.007917 + 0.008 + 0.008041 + 0.007875 + 0.008 + 0.011292 + 0.007708 + 0.008 + 0.008291 + 0.007792 + 0.013125 + 0.0085 + 0.008292 + 0.008959 + 0.013042 + 0.009042 + 0.0085 + 0.009 + 0.007958 + 0.008583 + 0.008167 + 0.008 + 0.00775 + 0.007708 + 0.007833 + 0.007791 + 0.016958 + 0.008083 + 0.00875 + 0.007625 + 0.007875 + 0.007917 + 0.007334 + 0.007708 + 0.0075 + 0.007583 + 0.008 + 0.011875 + 0.008458 + 0.011792 + 0.009458 + 0.008667 + 0.008791 + 0.008583 + 0.008709 + 0.008333 + 0.01425 + 0.009375 + 0.00875 + 0.008625 + 0.008667 + 0.008542 + 0.015208 + 0.007208 + 0.006958 + 0.007042 + 0.006708 + 0.006834 + 0.006583 + 0.006791 + 0.005917 + 0.005875 + 0.015792 + 0.00625 + 0.006167 + 0.007958 + 0.007458 + 0.00775 + 0.0075 + 0.007167 + 0.007625 + 0.007792 + 0.007667 + 0.007792 + 0.007708 + 0.007542 + 0.007458 + 0.007458 + 0.007459 + 0.007708 + 0.007333 + 0.0075 + 0.007542 + 0.007917 + 0.007584 + 0.007792 + 0.007708 + 0.007792 + 0.0075 + 0.007375 + 0.007792 + 0.007458 + 0.0075 + 0.008667 + 0.008292 + 0.00825 + 0.008167 + 0.007792 + 0.007709 + 0.007333 + 0.007667 + 0.008333 + 0.011 + 0.008459 + 0.0085 + 0.008083 + 0.008417 + 0.008375 + 0.007875 + 0.008125 + 0.008375 + 0.012542 + 0.008875 + 0.0185 + 0.009334 + 0.008667 + 0.009292 + 0.00875 + 0.008125 + 0.007958 + 0.011125 + 0.007959 + 0.007792 + 0.008334 + 0.014334 + 0.0085 + 0.008042 + 0.0085 + 0.008416 + 0.007791 + 0.007916 + 0.007791 + 0.007833 + 0.008167 + 0.008166 + 0.008083 + 0.00825 + 0.008666 + 0.021 + 0.012167 + 0.009708 + 0.008583 + 0.009125 + 0.008959 + 0.008667 + 0.008083 + 0.015875 + 0.009167 + 0.008792 + 0.008625 + 0.008916 + 0.008791 + 0.01525 + 0.0075 + 0.007416 + 0.007 + 0.00725 + 0.007041 + 0.006916 + 0.007083 + 0.007209 + 0.006417 + 0.005875 + 0.005875 + 0.00625 + 0.008125 + 0.007792 + 0.007917 + 0.007917 + 0.007541 + 0.007458 + 0.00775 + 0.00775 + 0.007417 + 0.007625 + 0.007542 + 0.007917 + 0.007959 + 0.007208 + 0.007708 + 0.007541 + 0.007875 + 0.007333 + 0.007583 + 0.008166 + 0.00775 + 0.007459 + 0.007583 + 0.007708 + 0.007292 + 0.008083 + 0.007917 + 0.007583 + 0.007458 + 0.007625 + 0.007375 + 0.0075 + 0.007542 + 0.007459 + 0.007583 + 0.007375 + 0.007833 + 0.008417 + 0.008209 + 0.008083 + 0.008 + 0.008292 + 0.00775 + 0.007917 + 0.007458 + 0.0085 + 0.008958 + 0.008458 + 0.010834 + 0.009375 + 0.008375 + 0.008667 + 0.008958 + 0.008166 + 0.007791 + 0.008166 + 0.007792 + 0.007625 + 0.007875 + 0.010291 + 0.00825 + 0.007375 + 0.007834 + 0.007583 + 0.008125 + 0.007875 + 0.008041 + 0.007875 + 0.008042 + 0.007708 + 0.00975 + 0.017667 + 0.009875 + 0.009459 + 0.008792 + 0.011125 + 0.008583 + 0.008666 + 0.008625 + 0.009 + 0.00825 + 0.009167 + 0.011417 + 0.00925 + 0.008458 + 0.008584 + 0.00825 + 0.008459 + 0.0115 + 0.007375 + 0.007083 + 0.00725 + 0.006875 + 0.007208 + 0.007084 + 0.006958 + 0.005791 + 0.00625 + 0.006334 + 0.006 + 0.007875 + 0.007667 + 0.007584 + 0.007583 + 0.007625 + 0.007458 + 0.007208 + 0.007333 + 0.0075 + 0.008208 + 0.007042 + 0.007708 + 0.007417 + 0.007417 + 0.008084 + 0.007583 + 0.007292 + 0.007917 + 0.007625 + 0.007667 + 0.007542 + 0.007209 + 0.007375 + 0.008 + 0.007209 + 0.007167 + 0.0075 + 0.007292 + 0.007333 + 0.007542 + 0.00725 + 0.016083 + 0.007875 + 0.00825 + 0.008667 + 0.008125 + 0.0085 + 0.008583 + 0.00875 + 0.0085 + 0.020917 + 0.008375 + 0.007916 + 0.008792 + 0.008375 + 0.008042 + 0.010375 + 0.018459 + 0.012417 + 0.01025 + 0.009792 + 0.009792 + 0.009542 + 0.009792 + 0.010042 + 0.008542 + 0.014875 + 0.009709 + 0.00975 + 0.008375 + 0.009125 + 0.008417 + 0.008084 + 0.009917 + 0.008959 + 0.008125 + 0.008708 + 0.008375 + 0.019917 + 0.008334 + 0.00775 + 0.008625 + 0.007833 + 0.011625 + 0.009042 + 0.00875 + 0.008167 + 0.008709 + 0.0085 + 0.013667 + 0.008 + 0.009042 + 0.00875 + 0.008792 + 0.008709 + 0.008833 + 0.011208 + 0.009208 + 0.008583 + 0.008 + 0.006959 + 0.007 + 0.006875 + 0.006708 + 0.008083 + 0.006875 + 0.014167 + 0.007334 + 0.009584 + 0.007459 + 0.007708 + 0.008083 + 0.008125 + 0.009166 + 0.008125 + 0.00775 + 0.007958 + 0.007417 + 0.007584 + 0.007667 + 0.0075 + 0.007625 + 0.007792 + 0.007709 + 0.007875 + 0.007125 + 0.007583 + 0.007625 + 0.00775 + 0.014833 + 0.007667 + 0.007584 + 0.007333 + 0.0075 + 0.007125 + 0.007666 + 0.007792 + 0.007458 + 0.007458 + 0.007459 + 0.007084 + 0.008209 + 0.008083 + 0.007667 + 0.007542 + 0.007125 + 0.017 + 0.009084 + 0.008125 + 0.008417 + 0.00775 + 0.014416 + 0.008083 + 0.007959 + 0.007542 + 0.016042 + 0.013042 + 0.009583 + 0.008583 + 0.009041 + 0.009167 + 0.009084 + 0.015208 + 0.0095 + 0.008459 + 0.008042 + 0.020958 + 0.008541 + 0.008167 + 0.008125 + 0.008208 + 0.063459 + 0.048542 + 0.014666 + 0.013083 + 0.0105 + 0.011208 + 0.010417 + 0.011292 + 0.010417 + 0.017791 + 0.012916 + 0.0135 + 0.018667 + 0.010958 + 0.011625 + 0.011625 + 0.01125 + 0.01375 + 0.014125 + 0.011208 + 0.019541 + 0.015583 + 0.011875 + 0.011 + 0.015625 + 0.014292 + 0.014 + 0.01275 + 0.021625 + 0.011458 + 0.011917 + 0.010959 + 0.008834 + 0.008625 + 0.009333 + 0.009458 + 0.010625 + 0.010709 + 0.008625 + 0.006416 + 0.008375 + 0.008166 + 0.008375 + 0.007542 + 0.007625 + 0.007584 + 0.0075 + 0.007833 + 0.007667 + 0.007292 + 0.008209 + 0.007375 + 0.007542 + 0.00775 + 0.007667 + 0.007375 + 0.007292 + 0.007792 + 0.007792 + 0.008 + 0.007417 + 0.00775 + 0.008 + 0.007875 + 0.007792 + 0.008166 + 0.007583 + 0.007708 + 0.007334 + 0.007209 + 0.008084 + 0.0075 + 0.007459 + 0.0075 + 0.007625 + 0.015916 + 0.012584 + 0.010833 + 0.013833 + 0.012125 + 0.012417 + 0.011375 + 0.01075 + 0.009417 + 0.00975 + 0.01 + 0.010709 + 0.010459 + 0.010083 + 0.012583 + 0.014 + 0.0125 + 0.011917 + 0.011125 + 0.010875 + 0.010833 + 0.010042 + 0.008167 + 0.007792 + 0.008375 + 0.008125 + 0.007708 + 0.0085 + 0.008459 + 0.008458 + 0.008375 + 0.010083 + 0.008125 + 0.008625 + 0.008333 + 0.008416 + 0.008416 + 0.008791 + 0.008916 + 0.008958 + 0.008625 + 0.011459 + 0.009583 + 0.009 + 0.008958 + 0.00825 + 0.009041 + 0.009292 + 0.011333 + 0.009292 + 0.009083 + 0.009042 + 0.010375 + 0.007333 + 0.007125 + 0.007375 + 0.007041 + 0.007041 + 0.006916 + 0.007208 + 0.007542 + 0.007375 + 0.006708 + 0.006666 + 0.007375 + 0.008833 + 0.009083 + 0.009917 + 0.008542 + 0.009333 + 0.008417 + 0.0085 + 0.008459 + 0.009375 + 0.009959 + 0.010417 + 0.00825 + 0.008583 + 0.009083 + 0.010125 + 0.008833 + 0.009708 + 0.01025 + 0.010625 + 0.010542 + 0.010209 + 0.009833 + 0.009666 + 0.009375 + 0.009459 + 0.009125 + 0.009792 + 0.01075 + 0.009125 + 0.010625 + 0.010541 + 0.010125 + 0.011292 + 0.013166 + 0.012583 + 0.011083 + 0.011209 + 0.009458 + 0.009208 + 0.009167 + 0.008417 + 0.012 + 0.00875 + 0.009042 + 0.009417 + 0.008709 + 0.009917 + 0.0095 + 0.010125 + 0.009417 + 0.013166 + 0.01 + 0.009875 + 0.009291 + 0.009083 + 0.009458 + 0.009791 + 0.009041 + 0.009125 + 0.009083 + 0.009167 + 0.008667 + 0.009542 + 0.009625 + 0.010166 + 0.009958 + 0.01375 + 0.011208 + 0.011417 + 0.010291 + 0.010541 + 0.011291 + 0.009791 + 0.01 + 0.009709 + 0.008875 + 0.010208 + 0.012916 + 0.010417 + 0.009875 + 0.008959 + 0.00925 + 0.009833 + 0.01025 + 0.011625 + 0.009667 + 0.010375 + 0.0105 + 0.009375 + 0.008291 + 0.009833 + 0.010292 + 0.010333 + 0.009292 + 0.008959 + 0.009 + 0.009625 + 0.007708 + 0.008208 + 0.007042 + 0.008625 + 0.009292 + 0.00875 + 0.009125 + 0.008625 + 0.008833 + 0.009292 + 0.009042 + 0.008833 + 0.008625 + 0.008792 + 0.008625 + 0.008792 + 0.008875 + 0.01025 + 0.008291 + 0.008458 + 0.008416 + 0.008708 + 0.008 + 0.009083 + 0.00925 + 0.009 + 0.008292 + 0.008834 + 0.00875 + 0.009 + 0.009 + 0.01075 + 0.0085 + 0.008792 + 0.008792 + 0.008166 + 0.008875 + 0.008333 + 0.008417 + 0.00875 + 0.008875 + 0.008792 + 0.008625 + 0.008709 + 0.011833 + 0.009708 + 0.008417 + 0.010125 + 0.008625 + 0.009292 + 0.009208 + 0.009958 + 0.009125 + 0.012875 + 0.0105 + 0.008916 + 0.010208 + 0.00875 + 0.009917 + 0.008708 + 0.00975 + 0.008917 + 0.008917 + 0.009 + 0.00825 + 0.012125 + 0.009458 + 0.009291 + 0.009333 + 0.009333 + 0.00925 + 0.009416 + 0.009666 + 0.009833 + 0.009208 + 0.009334 + 0.010292 + 0.009917 + 0.014667 + 0.01175 + 0.00975 + 0.01075 + 0.010125 + 0.009583 + 0.008666 + 0.0115 + 0.014667 + 0.011125 + 0.010167 + 0.010583 + 0.010208 + 0.009042 + 0.008417 + 0.00925 + 0.007916 + 0.008583 + 0.008208 + 0.008417 + 0.008541 + 0.009125 + 0.00925 + 0.008 + 0.007458 + 0.010166 + 0.010166 + 0.009375 + 0.009875 + 0.008083 + 0.009375 + 0.008458 + 0.008917 + 0.009209 + 0.00975 + 0.008666 + 0.008542 + 0.00875 + 0.009125 + 0.008875 + 0.008583 + 0.009167 + 0.009042 + 0.008417 + 0.009042 + 0.008709 + 0.008917 + 0.008667 + 0.008458 + 0.008958 + 0.009083 + 0.008458 + 0.008708 + 0.008333 + 0.009042 + 0.0085 + 0.008292 + 0.008167 + 0.009 + 0.009208 + 0.008583 + 0.008792 + 0.009042 + 0.012375 + 0.009334 + 0.009125 + 0.009125 + 0.0085 + 0.008625 + 0.009459 + 0.009167 + 0.008417 + 0.010292 + 0.010042 + 0.009584 + 0.012125 + 0.01025 + 0.010333 + 0.009834 + 0.008958 + 0.007834 + 0.009792 + 0.009125 + 0.008834 + 0.008834 + 0.008584 + 0.0095 + 0.008417 + 0.010166 + 0.011916 + 0.009791 + 0.008666 + 0.008958 + 0.009083 + 0.00875 + 0.009125 + 0.008583 + 0.008833 + 0.009834 + 0.009625 + 0.009667 + 0.012916 + 0.010083 + 0.009541 + 0.009541 + 0.009541 + 0.00875 + 0.010417 + 0.012292 + 0.010083 + 0.009959 + 0.00975 + 0.010167 + 0.009458 + 0.008166 + 0.010833 + 0.008125 + 0.007625 + 0.007375 + 0.007916 + 0.007667 + 0.007125 + 0.008084 + 0.007375 + 0.006542 + 0.006875 + 0.008625 + 0.00875 + 0.008708 + 0.008042 + 0.008417 + 0.00825 + 0.0085 + 0.007583 + 0.0085 + 0.008333 + 0.008333 + 0.008042 + 0.007959 + 0.008542 + 0.008208 + 0.00825 + 0.008083 + 0.008459 + 0.008334 + 0.008542 + 0.008917 + 0.00825 + 0.007958 + 0.008416 + 0.00825 + 0.008333 + 0.008875 + 0.008334 + 0.008917 + 0.0085 + 0.008625 + 0.007917 + 0.008375 + 0.008541 + 0.008042 + 0.0085 + 0.009167 + 0.012375 + 0.011666 + 0.009 + 0.009334 + 0.008875 + 0.008292 + 0.008958 + 0.008959 + 0.00875 + 0.009958 + 0.009458 + 0.00925 + 0.013083 + 0.011042 + 0.00925 + 0.01 + 0.009042 + 0.009125 + 0.0095 + 0.008917 + 0.008458 + 0.009042 + 0.009083 + 0.008292 + 0.008166 + 0.008708 + 0.015459 + 0.009167 + 0.008542 + 0.008792 + 0.009208 + 0.008708 + 0.008583 + 0.009083 + 0.008666 + 0.008125 + 0.009417 + 0.0095 + 0.01 + 0.011833 + 0.010125 + 0.009458 + 0.009375 + 0.008916 + 0.010083 + 0.009125 + 0.012458 + 0.010458 + 0.009083 + 0.009291 + 0.00925 + 0.009083 + 0.007333 + 0.008208 + 0.008 + 0.007375 + 0.007458 + 0.007333 + 0.007833 + 0.008166 + 0.008334 + 0.006792 + 0.006333 + 0.008167 + 0.007625 + 0.007458 + 0.008417 + 0.007625 + 0.007208 + 0.007958 + 0.007958 + 0.007625 + 0.007416 + 0.008375 + 0.007291 + 0.007667 + 0.007709 + 0.007625 + 0.007625 + 0.007666 + 0.008 + 0.007833 + 0.00825 + 0.008041 + 0.007625 + 0.007625 + 0.007625 + 0.008208 + 0.019042 + 0.007875 + 0.007625 + 0.007708 + 0.007625 + 0.007375 + 0.014708 + 0.008667 + 0.007833 + 0.007208 + 0.007708 + 0.017125 + 0.009292 + 0.008458 + 0.007875 + 0.007583 + 0.00775 + 0.007833 + 0.008208 + 0.027792 + 0.007959 + 0.011584 + 0.008667 + 0.00875 + 0.008208 + 0.009958 + 0.011667 + 0.009166 + 0.008417 + 0.008125 + 0.0085 + 0.00825 + 0.008 + 0.008083 + 0.008625 + 0.008375 + 0.0085 + 0.008959 + 0.008709 + 0.008459 + 0.008708 + 0.013041 + 0.008583 + 0.008208 + 0.008292 + 0.007959 + 0.00775 + 0.008458 + 0.009667 + 0.008916 + 0.008875 + 0.008791 + 0.011667 + 0.009292 + 0.008125 + 0.008167 + 0.0095 + 0.0085 + 0.008708 + 0.011541 + 0.008958 + 0.009 + 0.008958 + 0.008833 + 0.007458 + 0.007125 + 0.007167 + 0.006916 + 0.007167 + 0.006917 + 0.007333 + 0.006292 + 0.005959 + 0.00625 + 0.006042 + 0.007791 + 0.007667 + 0.007834 + 0.008084 + 0.007541 + 0.00825 + 0.008167 + 0.0075 + 0.007542 + 0.007458 + 0.007333 + 0.007917 + 0.007792 + 0.007625 + 0.007708 + 0.007834 + 0.00825 + 0.007708 + 0.00725 + 0.007625 + 0.007375 + 0.007292 + 0.007333 + 0.00775 + 0.007875 + 0.007375 + 0.007542 + 0.007625 + 0.007292 + 0.007416 + 0.0075 + 0.0075 + 0.007375 + 0.007584 + 0.00775 + 0.007542 + 0.007458 + 0.008167 + 0.007875 + 0.007667 + 0.008166 + 0.011375 + 0.007917 + 0.008334 + 0.008167 + 0.007833 + 0.008375 + 0.008417 + 0.008459 + 0.008625 + 0.011584 + 0.009375 + 0.008334 + 0.008417 + 0.008 + 0.008333 + 0.007958 + 0.007458 + 0.007708 + 0.007417 + 0.007666 + 0.008167 + 0.010459 + 0.00825 + 0.007834 + 0.007791 + 0.00775 + 0.007583 + 0.008083 + 0.008167 + 0.007958 + 0.007833 + 0.007666 + 0.009834 + 0.008583 + 0.008417 + 0.011792 + 0.008833 + 0.00875 + 0.008375 + 0.009334 + 0.008208 + 0.012208 + 0.009166 + 0.0085 + 0.008333 + 0.008542 + 0.008167 + 0.015625 + 0.007667 + 0.007459 + 0.007208 + 0.006542 + 0.007083 + 0.006708 + 0.006833 + 0.006 + 0.006042 + 0.006459 + 0.005916 + 0.006042 + 0.008416 + 0.007459 + 0.0075 + 0.007708 + 0.00725 + 0.007375 + 0.007791 + 0.007375 + 0.007542 + 0.007459 + 0.007375 + 0.007708 + 0.007625 + 0.0075 + 0.007709 + 0.007292 + 0.007542 + 0.007667 + 0.007541 + 0.007834 + 0.007625 + 0.007209 + 0.00725 + 0.007125 + 0.007583 + 0.007625 + 0.007458 + 0.007292 + 0.007583 + 0.007833 + 0.00775 + 0.007708 + 0.0075 + 0.007291 + 0.007709 + 0.007833 + 0.007833 + 0.007917 + 0.00725 + 0.00775 + 0.007833 + 0.007458 + 0.00775 + 0.007833 + 0.007542 + 0.008209 + 0.009208 + 0.008 + 0.011833 + 0.009375 + 0.008583 + 0.008958 + 0.009333 + 0.008583 + 0.007666 + 0.0085 + 0.007458 + 0.00825 + 0.010375 + 0.008292 + 0.008125 + 0.007709 + 0.007875 + 0.008084 + 0.007708 + 0.008375 + 0.008083 + 0.007708 + 0.007666 + 0.007833 + 0.007417 + 0.00775 + 0.008791 + 0.008459 + 0.011709 + 0.008708 + 0.008125 + 0.008583 + 0.008041 + 0.008417 + 0.007708 + 0.011916 + 0.008709 + 0.007958 + 0.008583 + 0.0085 + 0.008417 + 0.011958 + 0.007541 + 0.007 + 0.006583 + 0.006375 + 0.007292 + 0.00675 + 0.007208 + 0.007 + 0.005917 + 0.005833 + 0.006041 + 0.006 + 0.007709 + 0.007584 + 0.007375 + 0.007667 + 0.007292 + 0.008166 + 0.00775 + 0.007959 + 0.007792 + 0.007958 + 0.007333 + 0.00825 + 0.007958 + 0.007625 + 0.007708 + 0.008041 + 0.007792 + 0.007292 + 0.007625 + 0.008167 + 0.009625 + 0.009 + 0.009042 + 0.009041 + 0.009209 + 0.010792 + 0.008958 + 0.009 + 0.009458 + 0.008583 + 0.009417 + 0.009083 + 0.009583 + 0.008583 + 0.009334 + 0.008875 + 0.009375 + 0.009125 + 0.009292 + 0.009541 + 0.00975 + 0.008959 + 0.009459 + 0.008959 + 0.009459 + 0.009459 + 0.01025 + 0.012709 + 0.010667 + 0.01025 + 0.0095 + 0.009709 + 0.010125 + 0.009875 + 0.009791 + 0.009958 + 0.008916 + 0.009209 + 0.009166 + 0.012 + 0.009458 + 0.0095 + 0.009042 + 0.009583 + 0.009125 + 0.009375 + 0.01 + 0.009 + 0.009209 + 0.009209 + 0.009833 + 0.009833 + 0.009167 + 0.010375 + 0.009625 + 0.012625 + 0.010417 + 0.01 + 0.009292 + 0.009791 + 0.008208 + 0.008875 + 0.011791 + 0.009375 + 0.008708 + 0.009041 + 0.008958 + 0.0085 + 0.011959 + 0.007292 + 0.006833 + 0.00725 + 0.007583 + 0.006667 + 0.006958 + 0.007292 + 0.006542 + 0.00625 + 0.005416 + 0.005917 + 0.007833 + 0.007458 + 0.007459 + 0.007292 + 0.007583 + 0.007 + 0.007459 + 0.0075 + 0.007667 + 0.007167 + 0.007541 + 0.007458 + 0.007792 + 0.007209 + 0.007417 + 0.007375 + 0.007292 + 0.007583 + 0.007 + 0.007666 + 0.0075 + 0.007417 + 0.007375 + 0.007583 + 0.0075 + 0.007375 + 0.007542 + 0.00725 + 0.007875 + 0.007792 + 0.007333 + 0.007125 + 0.007458 + 0.007125 + 0.007958 + 0.007417 + 0.008208 + 0.007875 + 0.008083 + 0.007541 + 0.007708 + 0.007625 + 0.008125 + 0.007667 + 0.0075 + 0.007542 + 0.008875 + 0.008583 + 0.01175 + 0.009375 + 0.008625 + 0.008375 + 0.008458 + 0.008375 + 0.008166 + 0.007625 + 0.011083 + 0.008042 + 0.0075 + 0.007792 + 0.007666 + 0.007625 + 0.008167 + 0.008416 + 0.007667 + 0.007834 + 0.007666 + 0.007625 + 0.007708 + 0.008166 + 0.00825 + 0.007584 + 0.007834 + 0.008584 + 0.011583 + 0.008833 + 0.008875 + 0.008084 + 0.008292 + 0.0085 + 0.008208 + 0.011459 + 0.009125 + 0.008125 + 0.008458 + 0.008292 + 0.008375 + 0.010917 + 0.009166 + 0.007167 + 0.006833 + 0.006583 + 0.006584 + 0.006834 + 0.006375 + 0.007084 + 0.011875 + 0.007084 + 0.00775 + 0.00725 + 0.009042 + 0.008959 + 0.008834 + 0.008625 + 0.008166 + 0.008875 + 0.008709 + 0.008458 + 0.008792 + 0.008291 + 0.008084 + 0.008334 + 0.008584 + 0.008583 + 0.00775 + 0.00825 + 0.008792 + 0.008708 + 0.007917 + 0.008583 + 0.008042 + 0.008292 + 0.007875 + 0.008667 + 0.007791 + 0.008917 + 0.007958 + 0.008125 + 0.007625 + 0.007834 + 0.008542 + 0.008166 + 0.008875 + 0.00825 + 0.007833 + 0.008417 + 0.008166 + 0.008542 + 0.008709 + 0.008125 + 0.0095 + 0.009209 + 0.008917 + 0.008167 + 0.009125 + 0.009209 + 0.015584 + 0.016666 + 0.009959 + 0.009125 + 0.009333 + 0.008958 + 0.008542 + 0.011625 + 0.015208 + 0.009 + 0.008417 + 0.008042 + 0.007875 + 0.007917 + 0.008083 + 0.008166 + 0.008667 + 0.008958 + 0.008333 + 0.008917 + 0.008375 + 0.008416 + 0.008583 + 0.008 + 0.008416 + 0.008375 + 0.008541 + 0.023292 + 0.009166 + 0.009542 + 0.009042 + 0.00925 + 0.008959 + 0.018083 + 0.00975 + 0.00825 + 0.009292 + 0.009667 + 0.008667 + 0.009708 + 0.015333 + 0.009416 + 0.008709 + 0.0075 + 0.010416 + 0.010875 + 0.00775 + 0.007541 + 0.00775 + 0.007333 + 0.00675 + 0.006625 + 0.007375 + 0.006542 + 0.006625 + 0.008792 + 0.008 + 0.0085 + 0.008125 + 0.008041 + 0.008042 + 0.00825 + 0.008333 + 0.00825 + 0.007875 + 0.008542 + 0.008292 + 0.007834 + 0.008417 + 0.007875 + 0.008083 + 0.00775 + 0.008333 + 0.008292 + 0.00825 + 0.008041 + 0.008292 + 0.008083 + 0.00825 + 0.00775 + 0.008125 + 0.008334 + 0.0085 + 0.008292 + 0.008042 + 0.008 + 0.007958 + 0.007708 + 0.008 + 0.009167 + 0.00825 + 0.008167 + 0.008083 + 0.008375 + 0.008208 + 0.008292 + 0.008417 + 0.008667 + 0.007334 + 0.008083 + 0.007708 + 0.01775 + 0.009334 + 0.008916 + 0.008792 + 0.009209 + 0.008709 + 0.012167 + 0.008125 + 0.008083 + 0.008167 + 0.008167 + 0.008208 + 0.008083 + 0.008542 + 0.00775 + 0.007875 + 0.008 + 0.008542 + 0.0075 + 0.007834 + 0.007792 + 0.008458 + 0.007958 + 0.00825 + 0.008167 + 0.00875 + 0.011958 + 0.009375 + 0.009 + 0.009084 + 0.008875 + 0.008084 + 0.01575 + 0.00925 + 0.00775 + 0.0095 + 0.008333 + 0.008583 + 0.008666 + 0.016167 + 0.008834 + 0.008959 + 0.00725 + 0.006917 + 0.007208 + 0.007208 + 0.007209 + 0.016458 + 0.007042 + 0.007875 + 0.006833 + 0.006 + 0.006125 + 0.006375 + 0.007792 + 0.008083 + 0.007791 + 0.007834 + 0.007583 + 0.007708 + 0.007666 + 0.007875 + 0.007583 + 0.011541 + 0.007625 + 0.007541 + 0.007375 + 0.007875 + 0.007917 + 0.007375 + 0.007417 + 0.0075 + 0.007958 + 0.00775 + 0.0075 + 0.007958 + 0.007791 + 0.00725 + 0.007291 + 0.00825 + 0.00925 + 0.009375 + 0.009667 + 0.007875 + 0.007625 + 0.007625 + 0.007333 + 0.00775 + 0.007375 + 0.007583 + 0.007792 + 0.008083 + 0.007583 + 0.007791 + 0.008125 + 0.007792 + 0.007583 + 0.007625 + 0.007792 + 0.007958 + 0.011792 + 0.009125 + 0.00825 + 0.008709 + 0.008667 + 0.008667 + 0.011417 + 0.0085 + 0.007708 + 0.008084 + 0.00775 + 0.008208 + 0.008083 + 0.007583 + 0.008041 + 0.007667 + 0.007875 + 0.008083 + 0.008458 + 0.007708 + 0.00775 + 0.007917 + 0.007709 + 0.007875 + 0.007625 + 0.007959 + 0.007917 + 0.01175 + 0.008833 + 0.008583 + 0.008667 + 0.008333 + 0.008333 + 0.011416 + 0.008125 + 0.008625 + 0.008167 + 0.008583 + 0.008417 + 0.00825 + 0.010917 + 0.008917 + 0.008583 + 0.007125 + 0.006959 + 0.007125 + 0.00675 + 0.006917 + 0.0065 + 0.006791 + 0.006084 + 0.00625 + 0.006208 + 0.006292 + 0.007917 + 0.007667 + 0.007666 + 0.007417 + 0.007125 + 0.007375 + 0.007125 + 0.007625 + 0.007834 + 0.007584 + 0.007584 + 0.007708 + 0.007333 + 0.007542 + 0.007792 + 0.007291 + 0.007708 + 0.007792 + 0.007625 + 0.007541 + 0.007208 + 0.007625 + 0.007458 + 0.007458 + 0.007334 + 0.007708 + 0.007542 + 0.007542 + 0.007625 + 0.00725 + 0.007709 + 0.007459 + 0.007625 + 0.007542 + 0.011458 + 0.007625 + 0.007958 + 0.008417 + 0.008333 + 0.007791 + 0.008167 + 0.008458 + 0.0075 + 0.007708 + 0.007833 + 0.008 + 0.014375 + 0.011709 + 0.010125 + 0.008458 + 0.0085 + 0.016125 + 0.01225 + 0.009292 + 0.015625 + 0.008125 + 0.007791 + 0.007959 + 0.007833 + 0.007625 + 0.007417 + 0.008042 + 0.008291 + 0.007667 + 0.019708 + 0.007875 + 0.007708 + 0.007583 + 0.007625 + 0.010375 + 0.007833 + 0.007875 + 0.008041 + 0.01225 + 0.008542 + 0.008458 + 0.008375 + 0.008042 + 0.015375 + 0.00875 + 0.007667 + 0.008875 + 0.00875 + 0.008458 + 0.008208 + 0.015042 + 0.008833 + 0.008291 + 0.008542 + 0.006792 + 0.009958 + 0.006708 + 0.006541 + 0.007167 + 0.00675 + 0.006458 + 0.005958 + 0.005916 + 0.005625 + 0.006041 + 0.007708 + 0.007708 + 0.007542 + 0.007916 + 0.007792 + 0.007291 + 0.007834 + 0.007208 + 0.007791 + 0.00725 + 0.007292 + 0.007583 + 0.007708 + 0.007791 + 0.014042 + 0.007542 + 0.00725 + 0.007417 + 0.007375 + 0.007125 + 0.007167 + 0.007375 + 0.007291 + 0.007542 + 0.007417 + 0.007375 + 0.007292 + 0.007208 + 0.00725 + 0.007542 + 0.007333 + 0.00775 + 0.040875 + 0.03725 + 0.010209 + 0.012917 + 0.013166 + 0.020083 + 0.013292 + 0.012959 + 0.0135 + 0.0135 + 0.010625 + 0.010833 + 0.016542 + 0.015125 + 0.022667 + 0.014833 + 0.014125 + 0.011167 + 0.010334 + 0.015542 + 0.014458 + 0.011708 + 0.012792 + 0.015375 + 0.013958 + 0.013708 + 0.012666 + 0.011125 + 0.01175 + 0.014417 + 0.013333 + 0.016667 + 0.01375 + 0.009417 + 0.013875 + 0.014167 + 0.014042 + 0.013833 + 0.012375 + 0.014375 + 0.014833 + 0.031042 + 0.018458 + 0.015708 + 0.016417 + 0.012625 + 0.015042 + 0.014167 + 0.0155 + 0.017584 + 0.017792 + 0.016042 + 0.016792 + 0.014583 + 0.012458 + 0.011333 + 0.011458 + 0.012125 + 0.011541 + 0.011875 + 0.0135 + 0.0115 + 0.011792 + 0.011667 + 0.011041 + 0.010083 + 0.01 + 0.007833 + 0.009583 + 0.009625 + 0.0095 + 0.008875 + 0.008792 + 0.009917 + 0.008375 + 0.009167 + 0.00925 + 0.009041 + 0.008667 + 0.009375 + 0.008542 + 0.008417 + 0.009083 + 0.008833 + 0.009208 + 0.008625 + 0.016583 + 0.009166 + 0.009459 + 0.009167 + 0.008958 + 0.008708 + 0.009 + 0.008667 + 0.009083 + 0.009125 + 0.00925 + 0.008625 + 0.008541 + 0.008917 + 0.009 + 0.008625 + 0.009625 + 0.009084 + 0.00875 + 0.009292 + 0.012584 + 0.009083 + 0.010208 + 0.010542 + 0.008833 + 0.009166 + 0.009208 + 0.015125 + 0.010083 + 0.011208 + 0.009958 + 0.009875 + 0.009666 + 0.010292 + 0.016125 + 0.010958 + 0.009167 + 0.009708 + 0.012375 + 0.009541 + 0.010083 + 0.009834 + 0.009083 + 0.009417 + 0.009041 + 0.009042 + 0.009291 + 0.008875 + 0.009167 + 0.0135 + 0.009667 + 0.009084 + 0.009083 + 0.009417 + 0.009334 + 0.010208 + 0.010416 + 0.009792 + 0.010167 + 0.017041 + 0.011584 + 0.010625 + 0.011042 + 0.01 + 0.011083 + 0.00975 + 0.01375 + 0.010166 + 0.021584 + 0.010084 + 0.010042 + 0.00825 + 0.008958 + 0.007875 + 0.007834 + 0.007542 + 0.009 + 0.03275 + 0.031416 + 0.015292 + 0.009416 + 0.010167 + 0.011583 + 0.014416 + 0.011459 + 0.012417 + 0.013167 + 0.012209 + 0.019209 + 0.013083 + 0.01075 + 0.010708 + 0.011375 + 0.011709 + 0.011958 + 0.012542 + 0.010917 + 0.009958 + 0.01 + 0.010125 + 0.009834 + 0.01 + 0.010125 + 0.009625 + 0.01 + 0.010667 + 0.010875 + 0.010458 + 0.00975 + 0.009791 + 0.01 + 0.011708 + 0.012667 + 0.013 + 0.012208 + 0.01 + 0.009916 + 0.01 + 0.012583 + 0.013833 + 0.013667 + 0.014334 + 0.0135 + 0.01275 + 0.012916 + 0.011958 + 0.012583 + 0.010292 + 0.010459 + 0.012709 + 0.010666 + 0.010167 + 0.013583 + 0.012958 + 0.011833 + 0.010458 + 0.013083 + 0.012375 + 0.013041 + 0.011542 + 0.01075 + 0.012125 + 0.012333 + 0.013042 + 0.012791 + 0.013667 + 0.012667 + 0.013458 + 0.012791 + 0.012708 + 0.02075 + 0.009875 + 0.00975 + 0.009334 + 0.00975 + 0.009542 + 0.010917 + 0.011125 + 0.01025 + 0.01325 + 0.012708 + 0.012 + 0.012583 + 0.013959 + 0.013959 + 0.013917 + 0.014542 + 0.01375 + 0.011 + 0.011208 + 0.013041 + 0.013833 + 0.010667 + 0.01125 + 0.011542 + 0.009459 + 0.010041 + 0.009792 + 0.0095 + 0.011875 + 0.008459 + 0.00825 + 0.008084 + 0.012542 + 0.012541 + 0.012917 + 0.013291 + 0.011333 + 0.012042 + 0.011416 + 0.010458 + 0.011167 + 0.011292 + 0.011833 + 0.010458 + 0.011375 + 0.011292 + 0.011875 + 0.011958 + 0.010583 + 0.01 + 0.0105 + 0.009792 + 0.010042 + 0.010416 + 0.010125 + 0.010042 + 0.010166 + 0.010084 + 0.009875 + 0.010375 + 0.012042 + 0.011208 + 0.010291 + 0.010208 + 0.010417 + 0.011375 + 0.011917 + 0.01175 + 0.012042 + 0.011834 + 0.012792 + 0.013291 + 0.012708 + 0.012458 + 0.009791 + 0.010917 + 0.009875 + 0.009459 + 0.011042 + 0.009791 + 0.013917 + 0.01325 + 0.013459 + 0.012709 + 0.0105 + 0.010334 + 0.010709 + 0.013584 + 0.012625 + 0.012875 + 0.013083 + 0.012416 + 0.010166 + 0.010083 + 0.009833 + 0.010292 + 0.012916 + 0.011959 + 0.012875 + 0.013042 + 0.012041 + 0.011667 + 0.009084 + 0.009834 + 0.0095 + 0.011208 + 0.013917 + 0.013833 + 0.013542 + 0.012209 + 0.010416 + 0.009875 + 0.009334 + 0.013 + 0.013042 + 0.012875 + 0.013041 + 0.013125 + 0.0125 + 0.012042 + 0.011291 + 0.00925 + 0.008875 + 0.008459 + 0.008792 + 0.008125 + 0.008417 + 0.00825 + 0.007417 + 0.009458 + 0.007417 + 0.007458 + 0.008459 + 0.009333 + 0.009041 + 0.009542 + 0.008625 + 0.009 + 0.009833 + 0.009416 + 0.009 + 0.015917 + 0.04875 + 0.013916 + 0.011292 + 0.008584 + 0.009708 + 0.008958 + 0.012375 + 0.009208 + 0.010708 + 0.011 + 0.008667 + 0.009042 + 0.008416 + 0.008583 + 0.008959 + 0.012459 + 0.010208 + 0.010792 + 0.008792 + 0.008916 + 0.009333 + 0.008584 + 0.008666 + 0.008667 + 0.008666 + 0.008708 + 0.009875 + 0.009417 + 0.009 + 0.008959 + 0.009125 + 0.008958 + 0.008583 + 0.008833 + 0.008917 + 0.009167 + 0.009958 + 0.017834 + 0.011875 + 0.011292 + 0.009458 + 0.009791 + 0.010917 + 0.01275 + 0.012792 + 0.011667 + 0.012 + 0.01225 + 0.012583 + 0.010583 + 0.010834 + 0.009125 + 0.009042 + 0.010125 + 0.010459 + 0.011334 + 0.012084 + 0.011459 + 0.009625 + 0.0095 + 0.009375 + 0.008833 + 0.01125 + 0.015042 + 0.011042 + 0.01075 + 0.010041 + 0.009542 + 0.009708 + 0.014459 + 0.014667 + 0.013083 + 0.012541 + 0.012375 + 0.012875 + 0.014334 + 0.014208 + 0.011041 + 0.01275 + 0.012 + 0.010875 + 0.010125 + 0.010459 + 0.010417 + 0.010666 + 0.010625 + 0.010291 + 0.011083 + 0.011417 + 0.010709 + 0.009666 + 0.010584 + 0.010709 + 0.012167 + 0.011458 + 0.00975 + 0.009459 + 0.010375 + 0.009042 + 0.00825 + 0.008666 + 0.008417 + 0.008125 + 0.008375 + 0.008417 + 0.019875 + 0.008292 + 0.0085 + 0.008375 + 0.008334 + 0.008292 + 0.008583 + 0.008292 + 0.008916 + 0.0085 + 0.008292 + 0.008292 + 0.008334 + 0.008625 + 0.008958 + 0.008792 + 0.008459 + 0.008416 + 0.009208 + 0.008375 + 0.008542 + 0.008458 + 0.010166 + 0.010833 + 0.009208 + 0.009167 + 0.008875 + 0.009042 + 0.009333 + 0.01075 + 0.010666 + 0.012125 + 0.0155 + 0.012417 + 0.023375 + 0.022042 + 0.013292 + 0.0475 + 0.052 + 0.01575 + 0.012833 + 0.013916 + 0.010292 + 0.00925 + 0.008959 + 0.009291 + 0.009417 + 0.00925 + 0.0105 + 0.011792 + 0.011375 + 0.010125 + 0.020584 + 0.009958 + 0.010292 + 0.011583 + 0.010333 + 0.009791 + 0.014792 + 0.01225 + 0.019167 + 0.009916 + 0.016792 + 0.012542 + 0.011708 + 0.010041 + 0.010458 + 0.010625 + 0.010458 + 0.010708 + 0.013708 + 0.010792 + 0.01025 + 0.00975 + 0.010125 + 0.008708 + 0.008458 + 0.008 + 0.008625 + 0.020209 + 0.011416 + 0.011 + 0.0155 + 0.012 + 0.009625 + 0.012125 + 0.012375 + 0.011833 + 0.012292 + 0.0125 + 0.011 + 0.016125 + 0.014083 + 0.013958 + 0.014292 + 0.015084 + 0.040959 + 0.016292 + 0.056709 + 0.020333 + 0.100083 + 0.016625 + 0.0145 + 0.014917 + 0.014167 + 0.013458 + 0.015041 + 0.0135 + 0.013625 + 0.0145 + 0.013875 + 0.014208 + 0.015125 + 0.015125 + 0.015083 + 0.016166 + 0.016875 + 0.015625 + 0.014084 + 0.0125 + 0.012875 + 0.044833 + 0.014417 + 0.02075 + 0.013 + 0.013458 + 0.012583 + 0.016542 + 0.013208 + 0.012666 + 0.011417 + 0.015875 + 0.02175 + 0.013583 + 0.011833 + 0.011291 + 0.01575 + 0.018375 + 0.016667 + 0.017875 + 0.012208 + 0.01575 + 0.013042 + 0.010833 + 0.010041 + 0.017375 + 0.013167 + 0.01325 + 0.011625 + 0.012208 + 0.012083 + 0.012583 + 0.017208 + 0.01725 + 0.014792 + 0.011959 + 0.011167 + 0.011958 + 0.010584 + 0.014625 + 0.012042 + 0.011666 + 0.011 + 0.025625 + 0.014667 + 0.020375 + 0.012375 + 0.013584 + 0.011417 + 0.011291 + 0.014542 + 0.015 + 0.014583 + 0.014083 + 0.019583 + 0.011333 + 0.009417 + 0.011709 + 0.011875 + 0.011459 + 0.011292 + 0.009583 + 0.013208 + 0.0085 + 0.008125 + 0.0085 + 0.011042 + 0.01 + 0.009917 + 0.011583 + 0.01225 + 0.01275 + 0.012584 + 0.009792 + 0.009708 + 0.0095 + 0.009542 + 0.009916 + 0.010334 + 0.009959 + 0.009791 + 0.009583 + 0.009458 + 0.010375 + 0.009292 + 0.00925 + 0.0095 + 0.0095 + 0.009917 + 0.009583 + 0.009541 + 0.009083 + 0.009125 + 0.010125 + 0.00975 + 0.010125 + 0.009833 + 0.009666 + 0.009667 + 0.009166 + 0.009375 + 0.009708 + 0.010334 + 0.009791 + 0.010125 + 0.009541 + 0.014625 + 0.013917 + 0.013125 + 0.012791 + 0.012458 + 0.009834 + 0.01075 + 0.010417 + 0.010541 + 0.010375 + 0.014875 + 0.014541 + 0.013709 + 0.014042 + 0.012875 + 0.011209 + 0.012584 + 0.012459 + 0.010667 + 0.0115 + 0.011459 + 0.010083 + 0.019292 + 0.021292 + 0.012416 + 0.010042 + 0.016083 + 0.014208 + 0.011833 + 0.011166 + 0.010375 + 0.010708 + 0.021708 + 0.026792 + 0.020917 + 0.012583 + 0.0155 + 0.013833 + 0.013375 + 0.010833 + 0.010667 + 0.008792 + 0.011875 + 0.011708 + 0.017083 + 0.013416 + 0.011584 + 0.02125 + 0.012584 + 0.011542 + 0.009833 + 0.009541 + 0.009708 + 0.009625 + 0.008917 + 0.008083 + 0.00925 + 0.012292 + 0.008334 + 0.008625 + 0.011125 + 0.010792 + 0.010667 + 0.010084 + 0.00975 + 0.0105 + 0.009541 + 0.009375 + 0.009833 + 0.009708 + 0.009916 + 0.0095 + 0.009542 + 0.009542 + 0.009708 + 0.008708 + 0.009334 + 0.009625 + 0.008959 + 0.010083 + 0.009041 + 0.009667 + 0.009375 + 0.009458 + 0.00975 + 0.009791 + 0.009792 + 0.009459 + 0.008917 + 0.023125 + 0.010791 + 0.010792 + 0.00975 + 0.010208 + 0.010083 + 0.009625 + 0.009667 + 0.012833 + 0.012667 + 0.012834 + 0.013459 + 0.015834 + 0.012042 + 0.010958 + 0.009917 + 0.010208 + 0.011875 + 0.014708 + 0.013417 + 0.011875 + 0.011333 + 0.01425 + 0.0135 + 0.013917 + 0.011917 + 0.009333 + 0.010458 + 0.010666 + 0.009375 + 0.009958 + 0.00975 + 0.010209 + 0.012334 + 0.013542 + 0.011333 + 0.011041 + 0.011458 + 0.012958 + 0.013625 + 0.011917 + 0.010917 + 0.011375 + 0.011708 + 0.012875 + 0.011541 + 0.010417 + 0.012542 + 0.01175 + 0.0115 + 0.010916 + 0.012458 + 0.0105 + 0.010625 + 0.009875 + 0.01675 + 0.012459 + 0.0115 + 0.010875 + 0.011041 + 0.008625 + 0.008833 + 0.008667 + 0.008958 + 0.00875 + 0.009 + 0.008959 + 0.00875 + 0.012292 + 0.010542 + 0.007958 + 0.00925 + 0.009666 + 0.008958 + 0.010625 + 0.009709 + 0.010209 + 0.009666 + 0.0095 + 0.009375 + 0.009833 + 0.0095 + 0.011208 + 0.011458 + 0.016458 + 0.01 + 0.010584 + 0.01 + 0.010167 + 0.011792 + 0.015 + 0.009333 + 0.010667 + 0.010375 + 0.009417 + 0.009541 + 0.010125 + 0.009292 + 0.009375 + 0.009833 + 0.009875 + 0.009542 + 0.009875 + 0.011042 + 0.010125 + 0.012167 + 0.012125 + 0.012583 + 0.013166 + 0.011167 + 0.012417 + 0.012458 + 0.0125 + 0.013834 + 0.010125 + 0.012208 + 0.013042 + 0.011459 + 0.012375 + 0.013875 + 0.013 + 0.013458 + 0.012583 + 0.011541 + 0.013 + 0.011958 + 0.010625 + 0.01 + 0.009625 + 0.009458 + 0.009583 + 0.009625 + 0.009458 + 0.009542 + 0.011083 + 0.011083 + 0.009833 + 0.00975 + 0.013 + 0.013208 + 0.013416 + 0.01075 + 0.010041 + 0.012125 + 0.013166 + 0.01375 + 0.012292 + 0.011708 + 0.015708 + 0.013208 + 0.011458 + 0.012709 + 0.012459 + 0.013084 + 0.013833 + 0.014208 + 0.01225 + 0.012958 + 0.0105 + 0.010334 + 0.009417 + 0.008416 + 0.009125 + 0.009125 + 0.008584 + 0.009166 + 0.010417 + 0.010916 + 0.013167 + 0.008916 + 0.011417 + 0.009708 + 0.01225 + 0.012542 + 0.01225 + 0.010833 + 0.01125 + 0.010083 + 0.013958 + 0.009292 + 0.009666 + 0.009459 + 0.009125 + 0.009125 + 0.010875 + 0.011083 + 0.012041 + 0.011875 + 0.010417 + 0.010084 + 0.009958 + 0.011708 + 0.012291 + 0.010417 + 0.010083 + 0.011333 + 0.012167 + 0.011959 + 0.009416 + 0.010208 + 0.011917 + 0.011667 + 0.011583 + 0.012708 + 0.012 + 0.012 + 0.010791 + 0.012 + 0.012541 + 0.012083 + 0.016375 + 0.012541 + 0.012334 + 0.012666 + 0.012208 + 0.012792 + 0.01225 + 0.011459 + 0.012584 + 0.01225 + 0.012542 + 0.015583 + 0.01425 + 0.024709 + 0.015125 + 0.01475 + 0.012125 + 0.011541 + 0.011583 + 0.010458 + 0.010041 + 0.010417 + 0.010542 + 0.010625 + 0.010541 + 0.010834 + 0.010709 + 0.012167 + 0.012458 + 0.010583 + 0.011167 + 0.0095 + 0.009959 + 0.009417 + 0.009583 + 0.01 + 0.014417 + 0.0135 + 0.011625 + 0.010834 + 0.010791 + 0.010292 + 0.009834 + 0.015417 + 0.013625 + 0.012834 + 0.0125 + 0.009833 + 0.0105 + 0.01375 + 0.01325 + 0.011916 + 0.01075 + 0.0125 + 0.010042 + 0.008417 + 0.008208 + 0.008333 + 0.010041 + 0.007041 + 0.008209 + 0.007916 + 0.011833 + 0.013042 + 0.010834 + 0.010459 + 0.009417 + 0.009625 + 0.010042 + 0.008916 + 0.008917 + 0.008916 + 0.00925 + 0.010792 + 0.011625 + 0.012042 + 0.012166 + 0.011042 + 0.011792 + 0.012125 + 0.010625 + 0.0115 + 0.0145 + 0.01125 + 0.012667 + 0.012084 + 0.012083 + 0.010333 + 0.00925 + 0.009917 + 0.009708 + 0.009458 + 0.009708 + 0.012041 + 0.010958 + 0.009458 + 0.008583 + 0.011083 + 0.011708 + 0.012792 + 0.012 + 0.011 + 0.011084 + 0.018417 + 0.014666 + 0.013417 + 0.013584 + 0.011416 + 0.01375 + 0.011083 + 0.010875 + 0.011708 + 0.014958 + 0.01525 + 0.0115 + 0.011417 + 0.009541 + 0.009084 + 0.009125 + 0.009125 + 0.008958 + 0.009042 + 0.009084 + 0.01 + 0.013833 + 0.012583 + 0.013541 + 0.01225 + 0.009709 + 0.009375 + 0.009458 + 0.009625 + 0.009708 + 0.009041 + 0.00975 + 0.009667 + 0.010708 + 0.009916 + 0.013375 + 0.012166 + 0.011583 + 0.009875 + 0.009166 + 0.009459 + 0.009834 + 0.011625 + 0.01225 + 0.010791 + 0.009792 + 0.009166 + 0.009333 + 0.008833 + 0.008 + 0.009125 + 0.008833 + 0.008292 + 0.008292 + 0.008416 + 0.007958 + 0.0075 + 0.008084 + 0.007834 + 0.008916 + 0.009792 + 0.009666 + 0.009208 + 0.009 + 0.008792 + 0.009209 + 0.009333 + 0.0095 + 0.009042 + 0.009375 + 0.009542 + 0.009333 + 0.009 + 0.009292 + 0.00975 + 0.009209 + 0.009375 + 0.009292 + 0.008791 + 0.009333 + 0.009334 + 0.008959 + 0.009334 + 0.00975 + 0.00975 + 0.008959 + 0.009542 + 0.008916 + 0.00925 + 0.009167 + 0.009334 + 0.009209 + 0.009125 + 0.008583 + 0.008875 + 0.009125 + 0.009458 + 0.014958 + 0.011625 + 0.011292 + 0.009459 + 0.009958 + 0.009541 + 0.009042 + 0.009208 + 0.009625 + 0.01 + 0.010208 + 0.010125 + 0.012208 + 0.012458 + 0.011583 + 0.010125 + 0.009375 + 0.009084 + 0.009666 + 0.008833 + 0.010042 + 0.009292 + 0.009542 + 0.009583 + 0.009958 + 0.009542 + 0.011667 + 0.012 + 0.011334 + 0.010166 + 0.009709 + 0.009875 + 0.009875 + 0.009792 + 0.009584 + 0.010125 + 0.010292 + 0.009875 + 0.01225 + 0.012708 + 0.011958 + 0.009792 + 0.009708 + 0.009667 + 0.01025 + 0.012292 + 0.012125 + 0.01175 + 0.010292 + 0.010166 + 0.01025 + 0.008292 + 0.008584 + 0.008792 + 0.008791 + 0.009042 + 0.008708 + 0.0085 + 0.009459 + 0.011375 + 0.00775 + 0.007625 + 0.009917 + 0.011625 + 0.01 + 0.009042 + 0.010083 + 0.009791 + 0.009417 + 0.009208 + 0.009 + 0.008875 + 0.009084 + 0.009166 + 0.009208 + 0.009667 + 0.00925 + 0.009458 + 0.00925 + 0.008667 + 0.008417 + 0.008833 + 0.009458 + 0.009417 + 0.0095 + 0.008917 + 0.008834 + 0.014333 + 0.008375 + 0.009459 + 0.009709 + 0.010958 + 0.009792 + 0.009583 + 0.012 + 0.012208 + 0.011167 + 0.011875 + 0.009458 + 0.0095 + 0.012 + 0.011042 + 0.011291 + 0.011292 + 0.011417 + 0.009666 + 0.009875 + 0.009708 + 0.01075 + 0.012208 + 0.010416 + 0.010083 + 0.017416 + 0.0135 + 0.013833 + 0.012208 + 0.009708 + 0.009042 + 0.01 + 0.009 + 0.009458 + 0.010291 + 0.009375 + 0.009667 + 0.00975 + 0.009541 + 0.012708 + 0.0125 + 0.012084 + 0.012667 + 0.010208 + 0.009625 + 0.009709 + 0.009334 + 0.009959 + 0.008708 + 0.011041 + 0.010334 + 0.0105 + 0.01675 + 0.014 + 0.012791 + 0.012 + 0.009792 + 0.0105 + 0.010708 + 0.013417 + 0.013333 + 0.013083 + 0.012583 + 0.011708 + 0.012458 + 0.011166 + 0.011333 + 0.011 + 0.009125 + 0.009042 + 0.008625 + 0.00825 + 0.009917 + 0.0075 + 0.009167 + 0.011584 + 0.012833 + 0.011583 + 0.009083 + 0.008833 + 0.008958 + 0.008875 + 0.008708 + 0.009666 + 0.009583 + 0.009084 + 0.009042 + 0.008708 + 0.008917 + 0.009084 + 0.008875 + 0.009084 + 0.008959 + 0.009 + 0.009042 + 0.009166 + 0.009 + 0.008875 + 0.008959 + 0.00925 + 0.00875 + 0.008917 + 0.009 + 0.009083 + 0.008625 + 0.008458 + 0.008916 + 0.00925 + 0.009416 + 0.00875 + 0.008917 + 0.008917 + 0.009041 + 0.009792 + 0.009375 + 0.009167 + 0.00925 + 0.009334 + 0.009334 + 0.009208 + 0.009625 + 0.009 + 0.010041 + 0.009917 + 0.01325 + 0.013292 + 0.013125 + 0.012834 + 0.012834 + 0.01275 + 0.012459 + 0.011667 + 0.011834 + 0.0125 + 0.01225 + 0.011875 + 0.011458 + 0.009667 + 0.009375 + 0.009417 + 0.009584 + 0.009208 + 0.009166 + 0.008958 + 0.008792 + 0.009333 + 0.009333 + 0.009667 + 0.009042 + 0.014208 + 0.013875 + 0.012625 + 0.012334 + 0.009834 + 0.009834 + 0.012833 + 0.012708 + 0.013292 + 0.012958 + 0.009916 + 0.010292 + 0.010459 + 0.015416 + 0.0125 + 0.013334 + 0.010625 + 0.00875 + 0.007459 + 0.007959 + 0.007709 + 0.008 + 0.007875 + 0.008875 + 0.007042 + 0.006791 + 0.006583 + 0.008958 + 0.008583 + 0.008167 + 0.008292 + 0.008666 + 0.008625 + 0.008791 + 0.008834 + 0.008209 + 0.009084 + 0.008709 + 0.008959 + 0.008292 + 0.008375 + 0.008875 + 0.008958 + 0.008584 + 0.008959 + 0.008875 + 0.008208 + 0.008333 + 0.008458 + 0.008708 + 0.0085 + 0.008417 + 0.0085 + 0.008458 + 0.008416 + 0.008875 + 0.008916 + 0.00875 + 0.020208 + 0.008542 + 0.008667 + 0.009333 + 0.009542 + 0.008791 + 0.009375 + 0.009375 + 0.009333 + 0.010834 + 0.010875 + 0.009917 + 0.00925 + 0.009125 + 0.009125 + 0.012625 + 0.010833 + 0.009541 + 0.010917 + 0.010208 + 0.0105 + 0.014458 + 0.025458 + 0.020292 + 0.011625 + 0.01025 + 0.009292 + 0.009917 + 0.009583 + 0.009209 + 0.009833 + 0.009333 + 0.009583 + 0.009625 + 0.010167 + 0.009916 + 0.00975 + 0.009083 + 0.021708 + 0.019792 + 0.011916 + 0.009875 + 0.012875 + 0.010125 + 0.01575 + 0.0105 + 0.010958 + 0.0125 + 0.012375 + 0.010791 + 0.009834 + 0.010125 + 0.009542 + 0.009917 + 0.013458 + 0.012208 + 0.010708 + 0.009833 + 0.008708 + 0.008416], + :filter-tx-sparse-bounds + [0.014416 + 0.010666 + 0.010375 + 0.010166 + 0.010083 + 0.009708 + 0.010208 + 0.010167 + 0.010042 + 0.010209 + 0.009875 + 0.0095 + 0.009583 + 0.010042 + 0.009667 + 0.009917 + 0.010375 + 0.017417 + 0.01025 + 0.009833 + 0.010541 + 0.011458 + 0.01025 + 0.010542 + 0.010166 + 0.010125 + 0.010083 + 0.010541 + 0.009917 + 0.009875 + 0.009708 + 0.009709 + 0.011625 + 0.009959 + 0.009417 + 0.009709 + 0.009458 + 0.012042 + 0.01075 + 0.010542 + 0.010834 + 0.015875 + 0.01225 + 0.011583 + 0.01025 + 0.010291 + 0.013 + 0.011375 + 0.010416 + 0.011208 + 0.013125 + 0.012917 + 0.011791 + 0.011208 + 0.011541 + 0.008958 + 0.01075 + 0.010042 + 0.009959 + 0.010333 + 0.016625 + 0.009417 + 0.011583 + 0.011708 + 0.010625 + 0.0095 + 0.009083 + 0.009083 + 0.009208 + 0.009875 + 0.010083 + 0.009834 + 0.009958 + 0.011958 + 0.011625 + 0.013959 + 0.014041 + 0.013458 + 0.015333 + 0.022291 + 0.015875 + 0.011875 + 0.014417 + 0.013083 + 0.012583 + 0.010125 + 0.010834 + 0.01 + 0.024875 + 0.010583 + 0.010333 + 0.0095 + 0.010458 + 0.011792 + 0.010625 + 0.012625 + 0.012333 + 0.010042 + 0.020834 + 0.008792 + 0.010916 + 0.012083 + 0.008917 + 0.009583 + 0.017166 + 0.009208 + 0.009875 + 0.00975 + 0.009583 + 0.009542 + 0.008959 + 0.009208 + 0.009166 + 0.010208 + 0.009625 + 0.010291 + 0.010333 + 0.009666 + 0.009416 + 0.009458 + 0.009667 + 0.009834 + 0.009708 + 0.010125 + 0.010167 + 0.009208 + 0.009458 + 0.009834 + 0.009417 + 0.010417 + 0.009917 + 0.009542 + 0.009292 + 0.009834 + 0.009958 + 0.009625 + 0.009834 + 0.01 + 0.01025 + 0.010833 + 0.012 + 0.01 + 0.009667 + 0.010583 + 0.010333 + 0.01075 + 0.012542 + 0.013458 + 0.01175 + 0.012375 + 0.013041 + 0.011333 + 0.011125 + 0.011708 + 0.009916 + 0.009166 + 0.009833 + 0.009791 + 0.010083 + 0.009667 + 0.011834 + 0.012584 + 0.011458 + 0.010583 + 0.009709 + 0.009875 + 0.010042 + 0.01 + 0.00975 + 0.01075 + 0.010542 + 0.010084 + 0.009709 + 0.010792 + 0.010791 + 0.016833 + 0.012917 + 0.012125 + 0.010167 + 0.01075 + 0.011042 + 0.010083 + 0.012958 + 0.013125 + 0.011709 + 0.010083 + 0.010583 + 0.010542 + 0.01275 + 0.011083 + 0.010417 + 0.009167 + 0.009375 + 0.008833 + 0.00875 + 0.009042 + 0.00925 + 0.011709 + 0.007417 + 0.008 + 0.010833 + 0.009708 + 0.008875 + 0.01 + 0.0095 + 0.009708 + 0.009875 + 0.009125 + 0.009208 + 0.009875 + 0.009958 + 0.009292 + 0.009875 + 0.011041 + 0.010416 + 0.009458 + 0.009917 + 0.009542 + 0.009625 + 0.011 + 0.012084 + 0.011584 + 0.009208 + 0.009292 + 0.009875 + 0.008625 + 0.011209 + 0.011792 + 0.009667 + 0.009084 + 0.009542 + 0.01675 + 0.012667 + 0.012458 + 0.011125 + 0.011667 + 0.010334 + 0.010334 + 0.010833 + 0.01 + 0.009792 + 0.012417 + 0.012417 + 0.01125 + 0.010541 + 0.01225 + 0.013334 + 0.013875 + 0.013 + 0.016625 + 0.012583 + 0.011375 + 0.010542 + 0.010708 + 0.009833 + 0.009042 + 0.00975 + 0.010333 + 0.010083 + 0.010084 + 0.011959 + 0.011625 + 0.010625 + 0.007917 + 0.009208 + 0.009666 + 0.009792 + 0.009417 + 0.009375 + 0.009542 + 0.009125 + 0.009167 + 0.011 + 0.010667 + 0.010334 + 0.010042 + 0.013 + 0.0135 + 0.013583 + 0.013458 + 0.012166 + 0.010125 + 0.010167 + 0.012875 + 0.012958 + 0.011209 + 0.01 + 0.009375 + 0.010625 + 0.012334 + 0.011 + 0.00975 + 0.008416 + 0.008542 + 0.008167 + 0.008542 + 0.0085 + 0.010041 + 0.00775 + 0.007584 + 0.007708 + 0.009625 + 0.008958 + 0.009083 + 0.008916 + 0.009083 + 0.009541 + 0.009 + 0.009667 + 0.008459 + 0.009 + 0.0085 + 0.009125 + 0.009167 + 0.009083 + 0.009084 + 0.008917 + 0.009167 + 0.009542 + 0.009458 + 0.009042 + 0.008916 + 0.008833 + 0.008875 + 0.009583 + 0.010125 + 0.009958 + 0.011 + 0.013458 + 0.009167 + 0.01075 + 0.0105 + 0.009208 + 0.008959 + 0.008583 + 0.009666 + 0.00875 + 0.009541 + 0.008833 + 0.009667 + 0.00925 + 0.0095 + 0.009 + 0.00925 + 0.008959 + 0.009042 + 0.00925 + 0.009708 + 0.00975 + 0.01275 + 0.012125 + 0.011083 + 0.010875 + 0.009709 + 0.01 + 0.009708 + 0.009041 + 0.012041 + 0.011166 + 0.010875 + 0.009 + 0.009375 + 0.009084 + 0.009209 + 0.009708 + 0.009333 + 0.00925 + 0.01125 + 0.010917 + 0.00925 + 0.009333 + 0.009583 + 0.009583 + 0.010542 + 0.013833 + 0.012916 + 0.013375 + 0.013 + 0.012042 + 0.00975 + 0.01325 + 0.011375 + 0.011333 + 0.010166 + 0.009708 + 0.010209 + 0.009959 + 0.012542 + 0.011292 + 0.011792 + 0.008875 + 0.008 + 0.008375 + 0.008167 + 0.007875 + 0.008584 + 0.007833 + 0.00775 + 0.009958 + 0.007583 + 0.007333 + 0.009459 + 0.008583 + 0.008708 + 0.008792 + 0.0085 + 0.008375 + 0.008709 + 0.008375 + 0.008708 + 0.008084 + 0.00875 + 0.008583 + 0.008417 + 0.0085 + 0.008917 + 0.008875 + 0.00825 + 0.008083 + 0.008833 + 0.008875 + 0.0085 + 0.00875 + 0.008833 + 0.008333 + 0.008292 + 0.008416 + 0.008458 + 0.008125 + 0.008583 + 0.008416 + 0.008333 + 0.008458 + 0.008208 + 0.008541 + 0.008416 + 0.008291 + 0.008333 + 0.00925 + 0.009208 + 0.008792 + 0.008917 + 0.009292 + 0.00875 + 0.009 + 0.008875 + 0.009042 + 0.012125 + 0.011334 + 0.011375 + 0.010125 + 0.009417 + 0.010083 + 0.012542 + 0.011917 + 0.011208 + 0.008958 + 0.008958 + 0.009167 + 0.008792 + 0.009166 + 0.008791 + 0.008417 + 0.008083 + 0.0085 + 0.008167 + 0.007833 + 0.00825 + 0.008042 + 0.008416 + 0.01025 + 0.010709 + 0.013083 + 0.01025 + 0.009458 + 0.009917 + 0.008583 + 0.008709 + 0.00925 + 0.0145 + 0.011292 + 0.014333 + 0.00825 + 0.008917 + 0.009333 + 0.008791 + 0.010708 + 0.011125 + 0.011125 + 0.008292 + 0.007041 + 0.006875 + 0.01275 + 0.007166 + 0.007042 + 0.006792 + 0.006709 + 0.005917 + 0.005917 + 0.006042 + 0.0095 + 0.008042 + 0.00875 + 0.009166 + 0.008417 + 0.008125 + 0.008333 + 0.008 + 0.008334 + 0.008459 + 0.00825 + 0.008417 + 0.008291 + 0.008166 + 0.008042 + 0.008209 + 0.008042 + 0.007958 + 0.008167 + 0.00825 + 0.008292 + 0.008416 + 0.008 + 0.008584 + 0.008542 + 0.008125 + 0.0085 + 0.008125 + 0.007834 + 0.008375 + 0.009125 + 0.00875 + 0.007916 + 0.008084 + 0.008125 + 0.008125 + 0.008792 + 0.007792 + 0.009167 + 0.008792 + 0.011709 + 0.009 + 0.008417 + 0.008375 + 0.00875 + 0.008875 + 0.0085 + 0.00875 + 0.009333 + 0.009458 + 0.008916 + 0.012042 + 0.009917 + 0.008917 + 0.008917 + 0.008834 + 0.008917 + 0.008334 + 0.008375 + 0.007709 + 0.00825 + 0.008125 + 0.007833 + 0.0085 + 0.008375 + 0.011583 + 0.008833 + 0.008708 + 0.008791 + 0.008291 + 0.008208 + 0.008042 + 0.008667 + 0.00825 + 0.009375 + 0.008875 + 0.00875 + 0.015791 + 0.009708 + 0.0095 + 0.008791 + 0.009583 + 0.008375 + 0.011042 + 0.012375 + 0.009375 + 0.008917 + 0.009083 + 0.009083 + 0.009209 + 0.007375 + 0.007333 + 0.007333 + 0.007416 + 0.006916 + 0.007333 + 0.006583 + 0.007084 + 0.007208 + 0.006417 + 0.006292 + 0.006584 + 0.008417 + 0.007875 + 0.007917 + 0.008292 + 0.00825 + 0.007542 + 0.008041 + 0.007875 + 0.008 + 0.007875 + 0.007542 + 0.008125 + 0.007542 + 0.008292 + 0.008125 + 0.007833 + 0.008417 + 0.00775 + 0.0085 + 0.008375 + 0.007542 + 0.008125 + 0.008292 + 0.008 + 0.008 + 0.007958 + 0.007875 + 0.0085 + 0.007958 + 0.007917 + 0.007541 + 0.008167 + 0.00775 + 0.008084 + 0.008167 + 0.007625 + 0.00875 + 0.00825 + 0.011541 + 0.008666 + 0.007958 + 0.0085 + 0.007708 + 0.008 + 0.008291 + 0.008417 + 0.009167 + 0.008917 + 0.009459 + 0.011292 + 0.009917 + 0.008958 + 0.008875 + 0.008333 + 0.008708 + 0.008458 + 0.0085 + 0.008 + 0.0085 + 0.008 + 0.008167 + 0.007833 + 0.011375 + 0.008 + 0.008416 + 0.007875 + 0.007667 + 0.008334 + 0.008042 + 0.008084 + 0.007792 + 0.008417 + 0.008083 + 0.009083 + 0.009167 + 0.008667 + 0.011583 + 0.009792 + 0.008875 + 0.008875 + 0.008083 + 0.010958 + 0.008875 + 0.01175 + 0.008958 + 0.008458 + 0.009417 + 0.008666 + 0.008583 + 0.00725 + 0.007583 + 0.007459 + 0.006667 + 0.007166 + 0.006917 + 0.007125 + 0.006791 + 0.005917 + 0.006417 + 0.005666 + 0.007917 + 0.007583 + 0.008083 + 0.007542 + 0.007792 + 0.007834 + 0.007583 + 0.007625 + 0.007709 + 0.00775 + 0.007625 + 0.00775 + 0.007375 + 0.00775 + 0.008042 + 0.007542 + 0.008084 + 0.008083 + 0.008125 + 0.007958 + 0.007834 + 0.007916 + 0.007708 + 0.007625 + 0.007416 + 0.007583 + 0.007916 + 0.007667 + 0.007292 + 0.007542 + 0.00825 + 0.007208 + 0.007584 + 0.007417 + 0.008042 + 0.007292 + 0.00825 + 0.008208 + 0.011541 + 0.00875 + 0.008042 + 0.008083 + 0.008166 + 0.008042 + 0.007667 + 0.008125 + 0.009167 + 0.008542 + 0.008959 + 0.011417 + 0.009584 + 0.008334 + 0.008917 + 0.009125 + 0.00825 + 0.011292 + 0.007875 + 0.00775 + 0.008125 + 0.014541 + 0.008417 + 0.00775 + 0.007834 + 0.008709 + 0.008458 + 0.008083 + 0.007917 + 0.008209 + 0.008125 + 0.007958 + 0.008083 + 0.007916 + 0.007833 + 0.009 + 0.011458 + 0.009083 + 0.009375 + 0.009042 + 0.00875 + 0.008208 + 0.008209 + 0.008208 + 0.012333 + 0.00875 + 0.008916 + 0.008792 + 0.008375 + 0.0085 + 0.011583 + 0.007209 + 0.007417 + 0.006708 + 0.006917 + 0.007125 + 0.007083 + 0.006958 + 0.006208 + 0.006083 + 0.006666 + 0.00575 + 0.005958 + 0.008166 + 0.007792 + 0.007834 + 0.007792 + 0.008041 + 0.007542 + 0.007333 + 0.00775 + 0.007459 + 0.007625 + 0.007709 + 0.007708 + 0.008042 + 0.007667 + 0.007584 + 0.00775 + 0.007833 + 0.007667 + 0.008125 + 0.007375 + 0.007792 + 0.007583 + 0.007625 + 0.007791 + 0.007791 + 0.007666 + 0.007334 + 0.008 + 0.00825 + 0.007792 + 0.007666 + 0.007583 + 0.008084 + 0.007375 + 0.01225 + 0.00825 + 0.008417 + 0.00875 + 0.00825 + 0.007833 + 0.007708 + 0.007916 + 0.007917 + 0.008042 + 0.008 + 0.007625 + 0.008916 + 0.009042 + 0.01175 + 0.009583 + 0.00875 + 0.008542 + 0.008459 + 0.008042 + 0.0075 + 0.011166 + 0.00825 + 0.008083 + 0.00775 + 0.014791 + 0.00825 + 0.007459 + 0.00775 + 0.011167 + 0.007917 + 0.007417 + 0.008084 + 0.007834 + 0.007666 + 0.007833 + 0.007875 + 0.007791 + 0.008917 + 0.00875 + 0.013209 + 0.00925 + 0.008833 + 0.008417 + 0.008625 + 0.008417 + 0.008166 + 0.01225 + 0.008834 + 0.008583 + 0.008458 + 0.014958 + 0.009458 + 0.015708 + 0.007208 + 0.007208 + 0.006958 + 0.006875 + 0.00675 + 0.006666 + 0.006708 + 0.005875 + 0.006792 + 0.006042 + 0.006333 + 0.006125 + 0.008208 + 0.0075 + 0.006917 + 0.007542 + 0.007541 + 0.0075 + 0.00725 + 0.007208 + 0.008042 + 0.007417 + 0.007708 + 0.007 + 0.00775 + 0.007458 + 0.007125 + 0.007541 + 0.007875 + 0.00775 + 0.007792 + 0.0075 + 0.007958 + 0.007666 + 0.007042 + 0.007583 + 0.0075 + 0.007333 + 0.007625 + 0.007542 + 0.0075 + 0.007875 + 0.007125 + 0.00725 + 0.007791 + 0.00775 + 0.007333 + 0.007125 + 0.007875 + 0.008083 + 0.007708 + 0.007958 + 0.007792 + 0.008041 + 0.007833 + 0.007708 + 0.007625 + 0.00775 + 0.009 + 0.0085 + 0.011041 + 0.008875 + 0.009 + 0.008416 + 0.008667 + 0.007833 + 0.007583 + 0.008041 + 0.007667 + 0.007541 + 0.00775 + 0.010791 + 0.008333 + 0.00775 + 0.007708 + 0.008 + 0.007917 + 0.00775 + 0.007833 + 0.007792 + 0.0075 + 0.007833 + 0.007708 + 0.008667 + 0.009666 + 0.011458 + 0.01175 + 0.015625 + 0.012667 + 0.00975 + 0.009666 + 0.008959 + 0.008 + 0.008791 + 0.0105 + 0.009084 + 0.009 + 0.008417 + 0.008958 + 0.016042 + 0.008083 + 0.006958 + 0.006833 + 0.007167 + 0.00725 + 0.006834 + 0.007 + 0.006625 + 0.005958 + 0.007125 + 0.005875 + 0.006041 + 0.00775 + 0.007375 + 0.0075 + 0.007375 + 0.007666 + 0.006875 + 0.007416 + 0.00775 + 0.00725 + 0.007583 + 0.007625 + 0.007166 + 0.007375 + 0.007458 + 0.007458 + 0.007375 + 0.007458 + 0.007667 + 0.0075 + 0.007208 + 0.007375 + 0.007209 + 0.0075 + 0.007667 + 0.007458 + 0.007125 + 0.007541 + 0.007209 + 0.007209 + 0.007459 + 0.007417 + 0.007291 + 0.007584 + 0.007416 + 0.007458 + 0.00775 + 0.007667 + 0.008 + 0.007416 + 0.007667 + 0.008 + 0.007625 + 0.007292 + 0.00775 + 0.0075 + 0.007667 + 0.008792 + 0.011875 + 0.008917 + 0.008833 + 0.008541 + 0.008375 + 0.00825 + 0.018917 + 0.008333 + 0.007875 + 0.008375 + 0.008125 + 0.007625 + 0.0075 + 0.0075 + 0.007292 + 0.007584 + 0.011125 + 0.008167 + 0.0075 + 0.00775 + 0.007917 + 0.017584 + 0.007916 + 0.007792 + 0.007792 + 0.007541 + 0.011875 + 0.008875 + 0.008291 + 0.008083 + 0.008542 + 0.008209 + 0.010959 + 0.008166 + 0.008708 + 0.008208 + 0.00875 + 0.008458 + 0.008167 + 0.011667 + 0.008416 + 0.008459 + 0.0075 + 0.007084 + 0.007 + 0.00725 + 0.006625 + 0.006875 + 0.006709 + 0.005709 + 0.005625 + 0.005625 + 0.005667 + 0.007875 + 0.007542 + 0.007208 + 0.008 + 0.0075 + 0.007375 + 0.007292 + 0.0075 + 0.007417 + 0.007167 + 0.007417 + 0.007125 + 0.007292 + 0.0075 + 0.007042 + 0.007166 + 0.0075 + 0.007333 + 0.007583 + 0.007333 + 0.007667 + 0.0075 + 0.007334 + 0.007709 + 0.007166 + 0.007417 + 0.009209 + 0.007625 + 0.007417 + 0.007416 + 0.007459 + 0.007333 + 0.007541 + 0.007375 + 0.007208 + 0.0075 + 0.007291 + 0.010875 + 0.007375 + 0.007625 + 0.007625 + 0.0075 + 0.007542 + 0.007416 + 0.0075 + 0.007708 + 0.019042 + 0.008625 + 0.008333 + 0.0085 + 0.008584 + 0.008 + 0.015583 + 0.009083 + 0.007417 + 0.007875 + 0.007959 + 0.007334 + 0.007833 + 0.007417 + 0.00775 + 0.007792 + 0.007666 + 0.007709 + 0.008 + 0.007584 + 0.007416 + 0.0075 + 0.007375 + 0.015667 + 0.008083 + 0.007833 + 0.007667 + 0.011583 + 0.0085 + 0.0085 + 0.008458 + 0.008375 + 0.015333 + 0.008625 + 0.009041 + 0.008083 + 0.011917 + 0.008542 + 0.015542 + 0.008333 + 0.008791 + 0.012708 + 0.008667 + 0.007083 + 0.01025 + 0.006667 + 0.006625 + 0.0065 + 0.00675 + 0.006333 + 0.00575 + 0.005375 + 0.005542 + 0.006083 + 0.005375 + 0.007417 + 0.007375 + 0.007625 + 0.007375 + 0.007042 + 0.007167 + 0.007333 + 0.007042 + 0.006958 + 0.00725 + 0.006833 + 0.007292 + 0.007334 + 0.007041 + 0.00725 + 0.007458 + 0.007167 + 0.007084 + 0.007 + 0.007208 + 0.007417 + 0.007083 + 0.007542 + 0.007416 + 0.007167 + 0.007708 + 0.007458 + 0.007167 + 0.006959 + 0.007167 + 0.007042 + 0.007542 + 0.007125 + 0.007042 + 0.006916 + 0.00725 + 0.010792 + 0.007875 + 0.007625 + 0.007917 + 0.007625 + 0.007333 + 0.007583 + 0.017791 + 0.008125 + 0.007625 + 0.008875 + 0.0085 + 0.008334 + 0.008458 + 0.008292 + 0.014458 + 0.00875 + 0.007459 + 0.00775 + 0.011167 + 0.0075 + 0.007875 + 0.007625 + 0.007667 + 0.007917 + 0.007416 + 0.007584 + 0.007709 + 0.007584 + 0.007834 + 0.007292 + 0.00775 + 0.01425 + 0.00775 + 0.007625 + 0.007625 + 0.012208 + 0.008084 + 0.008584 + 0.0085 + 0.008625 + 0.01525 + 0.008167 + 0.008208 + 0.007958 + 0.00825 + 0.008334 + 0.00875 + 0.014916 + 0.008625 + 0.008333 + 0.008375 + 0.006875 + 0.009917 + 0.006666 + 0.006458 + 0.006167 + 0.006584 + 0.006375 + 0.006959 + 0.005667 + 0.005458 + 0.005667 + 0.005375 + 0.007666 + 0.00725 + 0.007333 + 0.007583 + 0.007333 + 0.00725 + 0.007667 + 0.006959 + 0.007167 + 0.007083 + 0.007417 + 0.007792 + 0.007458 + 0.007208 + 0.008209 + 0.007125 + 0.007333 + 0.00725 + 0.007041 + 0.007834 + 0.007375 + 0.00725 + 0.007333 + 0.007459 + 0.007042 + 0.008166 + 0.007042 + 0.007084 + 0.007416 + 0.007459 + 0.007167 + 0.007125 + 0.007083 + 0.007333 + 0.007042 + 0.007459 + 0.00775 + 0.007709 + 0.007417 + 0.00775 + 0.007334 + 0.010083 + 0.007458 + 0.007708 + 0.007583 + 0.007708 + 0.008292 + 0.008166 + 0.008334 + 0.0085 + 0.011125 + 0.009417 + 0.008625 + 0.008667 + 0.007667 + 0.007542 + 0.00775 + 0.007583 + 0.007583 + 0.00775 + 0.007625 + 0.007041 + 0.00775 + 0.00775 + 0.007583 + 0.007833 + 0.0115 + 0.00975 + 0.009583 + 0.008583 + 0.00725 + 0.00775 + 0.007209 + 0.008875 + 0.008292 + 0.008208 + 0.008291 + 0.010625 + 0.009 + 0.008625 + 0.007667 + 0.00875 + 0.008666 + 0.008792 + 0.011458 + 0.009083 + 0.008458 + 0.007958 + 0.008458 + 0.006667 + 0.00675 + 0.006709 + 0.007 + 0.006917 + 0.006583 + 0.006042 + 0.005625 + 0.005792 + 0.065208 + 0.01775 + 0.02 + 0.012083 + 0.011541 + 0.011625 + 0.011125 + 0.011958 + 0.010541 + 0.0105 + 0.028917 + 0.024042 + 0.028375 + 0.026834 + 0.016042 + 0.012125 + 0.011708 + 0.011333 + 0.01125 + 0.012333 + 0.01175 + 0.011875 + 0.01325 + 0.0115 + 0.010916 + 0.011333 + 0.011334 + 0.013166 + 0.012417 + 0.011084 + 0.011708 + 0.011333 + 0.011916 + 0.013666 + 0.012542 + 0.013167 + 0.012917 + 0.010625 + 0.012375 + 0.016083 + 0.011083 + 0.011042 + 0.011208 + 0.013833 + 0.011792 + 0.0115 + 0.010458 + 0.010959 + 0.018334 + 0.011417 + 0.012541 + 0.012333 + 0.015292 + 0.013959 + 0.012917 + 0.012542 + 0.015125 + 0.014542 + 0.013084 + 0.013042 + 0.012791 + 0.013667 + 0.01325 + 0.011791 + 0.013708 + 0.013833 + 0.014167 + 0.012209 + 0.01175 + 0.011375 + 0.011541 + 0.012167 + 0.01225 + 0.013584 + 0.012334 + 0.016 + 0.013042 + 0.012375 + 0.01375 + 0.013875 + 0.012334 + 0.013417 + 0.013916 + 0.015625 + 0.014667 + 0.014584 + 0.012125 + 0.012209 + 0.012292 + 0.010875 + 0.011666 + 0.011375 + 0.008875 + 0.010333 + 0.01075 + 0.010125 + 0.01 + 0.009458 + 0.019958 + 0.009917 + 0.009458 + 0.011167 + 0.013458 + 0.010792 + 0.013542 + 0.012959 + 0.011208 + 0.012625 + 0.013208 + 0.011541 + 0.01225 + 0.013125 + 0.010416 + 0.032625 + 0.011166 + 0.009833 + 0.01075 + 0.014167 + 0.013292 + 0.013709 + 0.010958 + 0.018792 + 0.018875 + 0.013375 + 0.012792 + 0.012375 + 0.011083 + 0.011167 + 0.010833 + 0.010666 + 0.012625 + 0.012083 + 0.011458 + 0.012917 + 0.010709 + 0.01025 + 0.020542 + 0.01175 + 0.010667 + 0.011208 + 0.01375 + 0.01125 + 0.019959 + 0.011542 + 0.010833 + 0.011875 + 0.011 + 0.011959 + 0.02875 + 0.012083 + 0.012625 + 0.014625 + 0.012583 + 0.027083 + 0.013292 + 0.013 + 0.022083 + 0.012833 + 0.0115 + 0.010458 + 0.011 + 0.021583 + 0.013916 + 0.012 + 0.012209 + 0.012542 + 0.011208 + 0.019625 + 0.011417 + 0.0105 + 0.011209 + 0.010917 + 0.010333 + 0.012 + 0.010333 + 0.01325 + 0.010792 + 0.019541 + 0.013458 + 0.014792 + 0.012334 + 0.010834 + 0.010625 + 0.011167 + 0.015584 + 0.016667 + 0.022 + 0.012084 + 0.0265 + 0.013375 + 0.015875 + 0.013875 + 0.009708 + 0.009333 + 0.023833 + 0.011209 + 0.010584 + 0.010125 + 0.008584 + 0.009917 + 0.010542 + 0.008166 + 0.011292 + 0.010792 + 0.010209 + 0.01025 + 0.009333 + 0.009917 + 0.01 + 0.010333 + 0.010292 + 0.01 + 0.009167 + 0.009541 + 0.009833 + 0.009208 + 0.010459 + 0.010084 + 0.021084 + 0.017959 + 0.011834 + 0.010541 + 0.010584 + 0.012083 + 0.012459 + 0.010041 + 0.009917 + 0.010166 + 0.010375 + 0.008875 + 0.009916 + 0.017125 + 0.009375 + 0.011584 + 0.012584 + 0.01175 + 0.012458 + 0.010708 + 0.01 + 0.01025 + 0.010459 + 0.010334 + 0.013625 + 0.010291 + 0.009458 + 0.010542 + 0.009458 + 0.010291 + 0.019625 + 0.016375 + 0.010958 + 0.013875 + 0.0245 + 0.0135 + 0.019875 + 0.028 + 0.027167 + 0.013625 + 0.012667 + 0.010208 + 0.01025 + 0.010291 + 0.014958 + 0.012917 + 0.011083 + 0.010666 + 0.009958 + 0.009792 + 0.010042 + 0.009333 + 0.009542 + 0.011666 + 0.010583 + 0.01025 + 0.027125 + 0.01575 + 0.014209 + 0.015667 + 0.012166 + 0.010458 + 0.010417 + 0.010917 + 0.009958 + 0.010666 + 0.013459 + 0.012209 + 0.011375 + 0.010458 + 0.010333 + 0.009792 + 0.012334 + 0.010625 + 0.010167 + 0.008791 + 0.008667 + 0.008416 + 0.008333 + 0.008 + 0.00925 + 0.010334 + 0.006917 + 0.007666 + 0.008792 + 0.009708 + 0.00925 + 0.008875 + 0.009125 + 0.020458 + 0.010791 + 0.009 + 0.009292 + 0.009125 + 0.008833 + 0.008625 + 0.009417 + 0.009666 + 0.009208 + 0.011667 + 0.012541 + 0.010042 + 0.009625 + 0.010917 + 0.010083 + 0.009875 + 0.01 + 0.009625 + 0.009208 + 0.009083 + 0.00925 + 0.009208 + 0.009583 + 0.009417 + 0.0115 + 0.012625 + 0.0105 + 0.010791 + 0.010959 + 0.012 + 0.012292 + 0.011291 + 0.011417 + 0.012167 + 0.011958 + 0.012042 + 0.012625 + 0.012417 + 0.011041 + 0.009375 + 0.009542 + 0.010208 + 0.010292 + 0.016084 + 0.0125 + 0.011625 + 0.010833 + 0.009584 + 0.009583 + 0.00925 + 0.019542 + 0.009458 + 0.0095 + 0.010333 + 0.011708 + 0.011417 + 0.011 + 0.011292 + 0.009708 + 0.013041 + 0.012292 + 0.012542 + 0.012458 + 0.012667 + 0.012084 + 0.012125 + 0.012083 + 0.011583 + 0.012333 + 0.011333 + 0.013958 + 0.011833 + 0.00975 + 0.009917 + 0.01025 + 0.009125 + 0.009708 + 0.015833 + 0.011917 + 0.011667 + 0.009916 + 0.010167 + 0.01 + 0.011916 + 0.01 + 0.009875 + 0.008958 + 0.007875 + 0.008416 + 0.008542 + 0.008792 + 0.009583 + 0.007 + 0.00825 + 0.008334 + 0.010042 + 0.009791 + 0.009083 + 0.009167 + 0.008709 + 0.009 + 0.009083 + 0.008917 + 0.009541 + 0.009042 + 0.00925 + 0.008417 + 0.009041 + 0.008917 + 0.009125 + 0.022417 + 0.010875 + 0.014542 + 0.012 + 0.012 + 0.01175 + 0.00975 + 0.009125 + 0.0095 + 0.008916 + 0.008417 + 0.015375 + 0.00925 + 0.009166 + 0.008334 + 0.009084 + 0.010459 + 0.010625 + 0.008792 + 0.009 + 0.011667 + 0.012625 + 0.011833 + 0.01225 + 0.009791 + 0.011167 + 0.010875 + 0.012292 + 0.011709 + 0.01025 + 0.010375 + 0.010334 + 0.011917 + 0.017708 + 0.010125 + 0.009833 + 0.010416 + 0.009917 + 0.010291 + 0.009417 + 0.011209 + 0.013084 + 0.012292 + 0.010541 + 0.010208 + 0.009375 + 0.011541 + 0.012125 + 0.011083 + 0.010375 + 0.01 + 0.012417 + 0.011708 + 0.011083 + 0.013 + 0.01275 + 0.012292 + 0.011625 + 0.00975 + 0.012875 + 0.012125 + 0.012375 + 0.011417 + 0.011583 + 0.00975 + 0.00975 + 0.01575 + 0.01325 + 0.012333 + 0.011541 + 0.012834 + 0.0135 + 0.013625 + 0.011708 + 0.009792 + 0.010625 + 0.011333 + 0.009833 + 0.009167 + 0.009 + 0.0085 + 0.012666 + 0.009042 + 0.008875 + 0.010083 + 0.011083 + 0.009333 + 0.008959 + 0.009084 + 0.009084 + 0.009083 + 0.008625 + 0.0095 + 0.008667 + 0.008375 + 0.009375 + 0.00875 + 0.009208 + 0.008875 + 0.008875 + 0.008708 + 0.008792 + 0.008458 + 0.009125 + 0.008833 + 0.008833 + 0.008541 + 0.009083 + 0.0085 + 0.008833 + 0.010375 + 0.00975 + 0.00925 + 0.009166 + 0.008459 + 0.011167 + 0.011542 + 0.011541 + 0.0125 + 0.010917 + 0.011 + 0.012084 + 0.010917 + 0.010541 + 0.010042 + 0.008709 + 0.00925 + 0.009542 + 0.009 + 0.009042 + 0.009208 + 0.009708 + 0.012792 + 0.012041 + 0.011667 + 0.009958 + 0.011875 + 0.012208 + 0.014417 + 0.012791 + 0.01175 + 0.010125 + 0.011667 + 0.013083 + 0.009833 + 0.011 + 0.01275 + 0.012292 + 0.013084 + 0.01225 + 0.010458 + 0.010083 + 0.010166 + 0.009375 + 0.011209 + 0.010959 + 0.010833 + 0.010708 + 0.012708 + 0.011167 + 0.01 + 0.01 + 0.009917 + 0.010458 + 0.012625 + 0.013709 + 0.013459 + 0.012792 + 0.011292 + 0.01175 + 0.011958 + 0.01275 + 0.014041 + 0.012583 + 0.009875 + 0.010125 + 0.010125 + 0.010125 + 0.009709 + 0.011334 + 0.010333 + 0.010541 + 0.008833 + 0.009375 + 0.00725 + 0.008458 + 0.011167 + 0.009375 + 0.008958 + 0.009042 + 0.008875 + 0.008291 + 0.009292 + 0.008958 + 0.008875 + 0.008875 + 0.008709 + 0.008417 + 0.008958 + 0.009167 + 0.008875 + 0.008709 + 0.008625 + 0.008917 + 0.008917 + 0.009 + 0.008625 + 0.008625 + 0.008417 + 0.008792 + 0.00875 + 0.00875 + 0.008875 + 0.015458 + 0.008583 + 0.009208 + 0.009875 + 0.009916 + 0.010542 + 0.011042 + 0.010958 + 0.010208 + 0.011833 + 0.013833 + 0.017416 + 0.010667 + 0.011792 + 0.012 + 0.013 + 0.012875 + 0.023625 + 0.013834 + 0.0135 + 0.020917 + 0.011792 + 0.013542 + 0.01375 + 0.016791 + 0.012584 + 0.010584 + 0.010791 + 0.010459 + 0.010791 + 0.0095 + 0.010167 + 0.008834 + 0.015459 + 0.008916 + 0.008667 + 0.017084 + 0.009542 + 0.009041 + 0.013292 + 0.008875 + 0.008416 + 0.00875 + 0.009166 + 0.009 + 0.009917 + 0.010333 + 0.010458 + 0.010208 + 0.012333 + 0.0115 + 0.009375 + 0.009958 + 0.009375 + 0.011792 + 0.009208 + 0.013292 + 0.011959 + 0.01175 + 0.011292 + 0.012458 + 0.010084 + 0.011875 + 0.009375 + 0.008042 + 0.007875 + 0.009 + 0.0085 + 0.009333 + 0.023833 + 0.007042 + 0.007042 + 0.008667 + 0.009541 + 0.009917 + 0.009125 + 0.009584 + 0.009125 + 0.009166 + 0.009334 + 0.00925 + 0.009417 + 0.009583 + 0.009 + 0.009584 + 0.009625 + 0.008708 + 0.013583 + 0.0095 + 0.010041 + 0.013959 + 0.010625 + 0.010917 + 0.009667 + 0.009834 + 0.009083 + 0.009375 + 0.01075 + 0.009542 + 0.009625 + 0.00975 + 0.009 + 0.009875 + 0.009041 + 0.009375 + 0.010208 + 0.010042 + 0.01025 + 0.009667 + 0.009625 + 0.023459 + 0.013291 + 0.009584 + 0.00975 + 0.017125 + 0.010167 + 0.018 + 0.010667 + 0.010791 + 0.011209 + 0.011458 + 0.019958 + 0.014583 + 0.011834 + 0.010709 + 0.010416 + 0.011 + 0.00925 + 0.0125 + 0.010625 + 0.0105 + 0.009959 + 0.009625 + 0.009875 + 0.017166 + 0.012417 + 0.009167 + 0.010917 + 0.018333 + 0.009875 + 0.009958 + 0.010542 + 0.010334 + 0.009709 + 0.0095 + 0.0095 + 0.010667 + 0.010625 + 0.009542 + 0.020042 + 0.010291 + 0.010083 + 0.010375 + 0.009875 + 0.014167 + 0.010334 + 0.0175 + 0.010625 + 0.009792 + 0.0105 + 0.010416 + 0.01025 + 0.008708 + 0.010666 + 0.008417 + 0.008334 + 0.008583 + 0.008541 + 0.008375 + 0.011416 + 0.020167 + 0.007583 + 0.007333 + 0.01 + 0.009042 + 0.009292 + 0.009125 + 0.009084 + 0.009 + 0.009459 + 0.013709 + 0.008709 + 0.009166 + 0.009708 + 0.009334 + 0.008708 + 0.009542 + 0.009459 + 0.009416 + 0.008917 + 0.008459 + 0.00875 + 0.00875 + 0.008667 + 0.009166 + 0.009292 + 0.009583 + 0.008875 + 0.008625 + 0.009333 + 0.008959 + 0.008875 + 0.009375 + 0.0095 + 0.009416 + 0.008917 + 0.008834 + 0.009125 + 0.009083 + 0.009167 + 0.008833 + 0.012541 + 0.01025 + 0.009375 + 0.00925 + 0.00925 + 0.010333 + 0.009375 + 0.009542 + 0.010291 + 0.010334 + 0.012916 + 0.010167 + 0.009833 + 0.01025 + 0.010083 + 0.01075 + 0.01 + 0.01225 + 0.014917 + 0.009833 + 0.00975 + 0.008959 + 0.012458 + 0.009625 + 0.009834 + 0.012417 + 0.009666 + 0.00925 + 0.012 + 0.0095 + 0.00975 + 0.012959 + 0.008959 + 0.009292 + 0.00925 + 0.013375 + 0.012333 + 0.011125 + 0.0105 + 0.011125 + 0.01075 + 0.009375 + 0.009875 + 0.017125 + 0.010625 + 0.011042 + 0.011542 + 0.010458 + 0.010875 + 0.014917 + 0.011 + 0.009291 + 0.015417 + 0.0095 + 0.00975 + 0.008708 + 0.008625 + 0.00825 + 0.007583 + 0.011459 + 0.007459 + 0.007834 + 0.010292 + 0.00975 + 0.009208 + 0.009375 + 0.019083 + 0.018791 + 0.009417 + 0.009792 + 0.0085 + 0.008792 + 0.008666 + 0.008917 + 0.008792 + 0.009167 + 0.009375 + 0.008792 + 0.008708 + 0.00875 + 0.008625 + 0.009209 + 0.009166 + 0.009333 + 0.009417 + 0.008667 + 0.009 + 0.009042 + 0.009291 + 0.009084 + 0.008708 + 0.009208 + 0.009333 + 0.008708 + 0.009041 + 0.008709 + 0.008875 + 0.008917 + 0.008666 + 0.009084 + 0.009167 + 0.009416 + 0.009334 + 0.009041 + 0.009 + 0.008583 + 0.009709 + 0.009459 + 0.009292 + 0.015916 + 0.010792 + 0.010209 + 0.010125 + 0.009792 + 0.010333 + 0.013083 + 0.009541 + 0.008917 + 0.009333 + 0.009208 + 0.009417 + 0.00925 + 0.010958 + 0.011667 + 0.011125 + 0.0105 + 0.031375 + 0.009875 + 0.009833 + 0.009791 + 0.010042 + 0.010625 + 0.009666 + 0.009792 + 0.010333 + 0.016083 + 0.012833 + 0.011291 + 0.010459 + 0.010458 + 0.010125 + 0.012333 + 0.013083 + 0.0115 + 0.010791 + 0.010792 + 0.010292 + 0.010375 + 0.013042 + 0.012875 + 0.011625 + 0.009875 + 0.008833 + 0.008334 + 0.00925 + 0.00875 + 0.009375 + 0.008209 + 0.007542 + 0.010041 + 0.009667 + 0.007584 + 0.008959 + 0.022417 + 0.008708 + 0.009416 + 0.008959 + 0.009 + 0.009042 + 0.008791 + 0.008834 + 0.010792 + 0.010333 + 0.011416 + 0.0115 + 0.011708 + 0.011083 + 0.009625 + 0.011416 + 0.012125 + 0.009666 + 0.009667 + 0.010209 + 0.010375 + 0.011584 + 0.035 + 0.013708 + 0.012583 + 0.012083 + 0.012083 + 0.012542 + 0.012292 + 0.010791 + 0.011958 + 0.012 + 0.01 + 0.010125 + 0.009417 + 0.010084 + 0.014792 + 0.013583 + 0.012041 + 0.010375 + 0.009041 + 0.0095 + 0.008916 + 0.009792 + 0.009291 + 0.010959 + 0.014958 + 0.012792 + 0.011084 + 0.010125 + 0.010083 + 0.009708 + 0.011667 + 0.012 + 0.010167 + 0.009584 + 0.00875 + 0.008833 + 0.0085 + 0.009 + 0.008541 + 0.008084 + 0.00975 + 0.008834 + 0.008541 + 0.013833 + 0.009833 + 0.010458 + 0.011958 + 0.011958 + 0.00975 + 0.010875 + 0.011791 + 0.012458 + 0.010375 + 0.010416 + 0.010708 + 0.012375 + 0.011291 + 0.010417 + 0.010417 + 0.01125 + 0.012292 + 0.011292 + 0.011542 + 0.010209 + 0.011708 + 0.011125 + 0.009167 + 0.008375 + 0.008291 + 0.00875 + 0.008542 + 0.011375 + 0.009167 + 0.009042 + 0.010625 + 0.0075 + 0.008417 + 0.011791 + 0.012292 + 0.012542 + 0.011959 + 0.012 + 0.011375 + 0.009667 + 0.009208 + 0.009583 + 0.009 + 0.008958 + 0.008666 + 0.00925 + 0.008708 + 0.009125 + 0.008791 + 0.008833 + 0.012167 + 0.011542 + 0.009041 + 0.009583 + 0.009458 + 0.008917 + 0.009709 + 0.008834 + 0.008875 + 0.0085 + 0.009417 + 0.009209 + 0.010042 + 0.00875 + 0.010041 + 0.011542 + 0.011 + 0.009541 + 0.010292 + 0.009959 + 0.010167 + 0.009833 + 0.009333 + 0.009417 + 0.009042 + 0.008667 + 0.009083 + 0.00925 + 0.009209 + 0.00975 + 0.01025 + 0.013416 + 0.013 + 0.012208 + 0.011625 + 0.009916 + 0.009417 + 0.009291 + 0.010458 + 0.016792 + 0.012666 + 0.035417 + 0.018167 + 0.011292 + 0.009458 + 0.009083 + 0.008709 + 0.009167 + 0.016916 + 0.009459 + 0.010625 + 0.011167 + 0.012209 + 0.012417 + 0.011584 + 0.011584 + 0.016792 + 0.01275 + 0.014459 + 0.012459 + 0.012792 + 0.012208 + 0.011125 + 0.010083 + 0.008667 + 0.020916 + 0.012083 + 0.009542 + 0.010083 + 0.009875 + 0.009167 + 0.012125 + 0.011791 + 0.009833 + 0.009417 + 0.013833 + 0.010917 + 0.011208 + 0.010333 + 0.010333 + 0.009167 + 0.007167 + 0.007125 + 0.006708 + 0.0095 + 0.008125 + 0.008875 + 0.0085 + 0.009 + 0.009208 + 0.013167 + 0.008167 + 0.00875 + 0.008375 + 0.008292 + 0.008417 + 0.019417 + 0.009917 + 0.01025 + 0.009042 + 0.008583 + 0.008458 + 0.008417 + 0.010292 + 0.011333 + 0.011292 + 0.009709 + 0.0095 + 0.008209 + 0.008959 + 0.009584 + 0.011375 + 0.011125 + 0.009791 + 0.00875 + 0.008875 + 0.00875 + 0.008583 + 0.008208 + 0.008958 + 0.010042 + 0.009541 + 0.01075 + 0.011792 + 0.011041 + 0.010209 + 0.012625 + 0.012958 + 0.01225 + 0.012375 + 0.011209 + 0.014917 + 0.012625 + 0.012625 + 0.012792 + 0.012625 + 0.011042 + 0.013333 + 0.013625 + 0.011375 + 0.009958 + 0.01 + 0.009917 + 0.009833 + 0.010584 + 0.011416 + 0.013083 + 0.012792 + 0.011375 + 0.012125 + 0.011667 + 0.011958 + 0.0105 + 0.009334 + 0.008542 + 0.011708 + 0.011208 + 0.016291 + 0.014042 + 0.012291 + 0.011209 + 0.01125 + 0.010334 + 0.010417 + 0.01 + 0.01025 + 0.014834 + 0.011458 + 0.010416 + 0.010833 + 0.010875 + 0.0125 + 0.015583 + 0.02725 + 0.011042 + 0.031084 + 0.043458 + 0.009709 + 0.009167 + 0.011416 + 0.026375 + 0.015833 + 0.008584 + 0.009542 + 0.010208 + 0.013416 + 0.015917 + 0.01 + 0.009583 + 0.011458 + 0.012333 + 0.01075 + 0.009667 + 0.010333 + 0.00925 + 0.008791 + 0.008833 + 0.009042 + 0.009916 + 0.01125 + 0.011167 + 0.012042 + 0.010959 + 0.009208 + 0.009125 + 0.010042 + 0.011125 + 0.011084 + 0.009791 + 0.01025 + 0.00975 + 0.0095 + 0.009708 + 0.009792 + 0.009625 + 0.009709 + 0.009667 + 0.01025 + 0.009791 + 0.011292 + 0.010916 + 0.017917 + 0.011834 + 0.012333 + 0.013917 + 0.013 + 0.019125 + 0.013875 + 0.011708 + 0.012167 + 0.013459 + 0.01925 + 0.020917 + 0.0195 + 0.020292 + 0.017125 + 0.023416 + 0.014667 + 0.016166 + 0.012208 + 0.015791 + 0.011583 + 0.013 + 0.013916 + 0.014167 + 0.015417 + 0.0135 + 0.014 + 0.010583 + 0.015958 + 0.010584 + 0.010041 + 0.01225 + 0.013 + 0.012959 + 0.013042 + 0.013375 + 0.012792 + 0.011 + 0.014 + 0.015417 + 0.014042 + 0.012291 + 0.0115 + 0.015458 + 0.013584 + 0.021334 + 0.015333 + 0.014166 + 0.015 + 0.012583 + 0.012 + 0.020917 + 0.012666 + 0.012667 + 0.012667 + 0.011125 + 0.009708 + 0.009208 + 0.009125 + 0.008208 + 0.011708 + 0.010917 + 0.008458 + 0.009208 + 0.010667 + 0.00975 + 0.010375 + 0.00975 + 0.010041 + 0.010417 + 0.009625 + 0.010084 + 0.009875 + 0.009708 + 0.020917 + 0.01225 + 0.010459 + 0.010375 + 0.009875 + 0.009333 + 0.009625 + 0.009583 + 0.012125 + 0.012208 + 0.012291 + 0.012167 + 0.009 + 0.012917 + 0.017291 + 0.01025 + 0.012542 + 0.012833 + 0.012083 + 0.011917 + 0.009584 + 0.017 + 0.00975 + 0.009792 + 0.009416 + 0.010208 + 0.010958 + 0.011209 + 0.011583 + 0.010959 + 0.012 + 0.013292 + 0.011084 + 0.011709 + 0.011042 + 0.011208 + 0.01125 + 0.0105 + 0.01425 + 0.013458 + 0.014083 + 0.014833 + 0.011542 + 0.010875 + 0.010584 + 0.011083 + 0.010125 + 0.011292 + 0.01075 + 0.014583 + 0.013458 + 0.012958 + 0.024917 + 0.022208 + 0.014541 + 0.012 + 0.010458 + 0.0115 + 0.010875 + 0.009875 + 0.010625 + 0.011208 + 0.011083 + 0.011625 + 0.01825 + 0.014375 + 0.01275 + 0.02 + 0.011875 + 0.011833 + 0.018875 + 0.014416 + 0.015125 + 0.014583 + 0.013375 + 0.012584 + 0.010791 + 0.013792 + 0.012042 + 0.01 + 0.009708 + 0.008917 + 0.008584 + 0.008416 + 0.008667 + 0.008958 + 0.011417 + 0.007542 + 0.01025 + 0.009292 + 0.01025 + 0.010083 + 0.009125 + 0.008792 + 0.009875 + 0.008959 + 0.009375 + 0.00925 + 0.009875 + 0.009416 + 0.009583 + 0.009542 + 0.009334 + 0.009041 + 0.017292 + 0.016209 + 0.010667 + 0.010125 + 0.010541 + 0.009833 + 0.009333 + 0.011292 + 0.011834 + 0.011958 + 0.012875 + 0.012791 + 0.011917 + 0.01075 + 0.0095 + 0.009459 + 0.009792 + 0.012042 + 0.012584 + 0.012209 + 0.011667 + 0.009458 + 0.009708 + 0.00975 + 0.0125 + 0.012709 + 0.011209 + 0.010834 + 0.010417 + 0.009458 + 0.009542 + 0.009875 + 0.010375 + 0.014291 + 0.014333 + 0.013708 + 0.013042 + 0.014333 + 0.012791 + 0.015834 + 0.013667 + 0.011541 + 0.010917 + 0.009958 + 0.011833 + 0.009666 + 0.009583 + 0.009875 + 0.00975 + 0.009833 + 0.011875 + 0.010458 + 0.009542 + 0.009208 + 0.009917 + 0.012208 + 0.011666 + 0.011333 + 0.0095 + 0.010208 + 0.009917 + 0.010125 + 0.01 + 0.010208 + 0.013 + 0.013334 + 0.009834 + 0.010834 + 0.0105 + 0.010208 + 0.010958 + 0.013209 + 0.012875 + 0.012583 + 0.010417 + 0.009291 + 0.008542 + 0.008875 + 0.01525 + 0.009333 + 0.009125 + 0.011291 + 0.015666 + 0.01025 + 0.009959 + 0.008875 + 0.011916 + 0.013 + 0.01275 + 0.012125 + 0.012917 + 0.01175 + 0.012709 + 0.011708 + 0.01425 + 0.011375 + 0.015083 + 0.011333 + 0.011333 + 0.0105 + 0.013417 + 0.012708 + 0.010166 + 0.01075 + 0.010917 + 0.014167 + 0.01175 + 0.0115 + 0.010792 + 0.010333 + 0.009583 + 0.009416 + 0.009458 + 0.011917 + 0.012 + 0.011917 + 0.01225 + 0.010125 + 0.009542 + 0.009416 + 0.014375 + 0.009958 + 0.009958 + 0.010792 + 0.012959 + 0.013167 + 0.012666 + 0.011375 + 0.009458 + 0.009667 + 0.009666 + 0.010625 + 0.014667 + 0.014 + 0.013583 + 0.012667 + 0.01225 + 0.010667 + 0.013208 + 0.012583 + 0.011125 + 0.009375 + 0.0095 + 0.009041 + 0.009625 + 0.011541 + 0.0095 + 0.009417 + 0.009959 + 0.01125 + 0.0115 + 0.01 + 0.010083 + 0.010625 + 0.009792 + 0.012916 + 0.013042 + 0.013542 + 0.012875 + 0.01275 + 0.010625 + 0.011375 + 0.010834 + 0.011 + 0.01375 + 0.013417 + 0.010125 + 0.010292 + 0.011708 + 0.011042 + 0.011375 + 0.013334 + 0.013417 + 0.0115 + 0.010542 + 0.009666 + 0.008583 + 0.019667 + 0.008834 + 0.008583 + 0.008416 + 0.010125 + 0.010417 + 0.009709 + 0.009667 + 0.0105 + 0.011584 + 0.01275 + 0.010416 + 0.009208 + 0.009084 + 0.008792 + 0.008958 + 0.009292 + 0.009 + 0.009375 + 0.009541 + 0.009209 + 0.018208 + 0.009333 + 0.009208 + 0.010084 + 0.009375 + 0.00925 + 0.0115 + 0.010041 + 0.00875 + 0.009083 + 0.00925 + 0.010167 + 0.012959 + 0.011541 + 0.010375 + 0.0125 + 0.011375 + 0.010667 + 0.0115 + 0.011208 + 0.0105 + 0.011291 + 0.010792 + 0.012542 + 0.051667 + 0.040791 + 0.011916 + 0.010333 + 0.010209 + 0.009833 + 0.009875 + 0.01025 + 0.018208 + 0.010417 + 0.011291 + 0.015208 + 0.01175 + 0.011167 + 0.010667 + 0.010625 + 0.013833 + 0.011542 + 0.010708 + 0.009917 + 0.010625 + 0.009542 + 0.010083 + 0.0095 + 0.00975 + 0.00975 + 0.009791 + 0.01075 + 0.01075 + 0.010708 + 0.009792 + 0.010125 + 0.010125 + 0.009 + 0.013375 + 0.010667 + 0.011417 + 0.024834 + 0.011458 + 0.011833 + 0.010834 + 0.0105 + 0.02075 + 0.010708 + 0.010834 + 0.010791 + 0.01175 + 0.009958 + 0.020833 + 0.013375 + 0.011458 + 0.011166 + 0.010917 + 0.00975 + 0.009042 + 0.008958 + 0.008792 + 0.009666 + 0.008542 + 0.008583 + 0.011625 + 0.011167 + 0.007542 + 0.008041 + 0.009334 + 0.011125 + 0.009875 + 0.009959 + 0.009958 + 0.009458 + 0.009458 + 0.01 + 0.009458 + 0.008917 + 0.009792 + 0.00925 + 0.00975 + 0.01 + 0.0095 + 0.009541 + 0.008959 + 0.009292 + 0.010667 + 0.01975 + 0.013083 + 0.013917 + 0.011583 + 0.010541 + 0.02025 + 0.019708 + 0.010709 + 0.011333 + 0.011583 + 0.012334 + 0.011417 + 0.0115 + 0.011375 + 0.010959 + 0.01025 + 0.011209 + 0.010375 + 0.011917 + 0.010833 + 0.012208 + 0.011084 + 0.011208 + 0.012583 + 0.011292 + 0.014291 + 0.011667 + 0.009584 + 0.010458 + 0.010417 + 0.010542 + 0.017458 + 0.010875 + 0.013625 + 0.011083 + 0.009708 + 0.010084 + 0.009875 + 0.008917 + 0.009583 + 0.01025 + 0.009791 + 0.009708 + 0.009583 + 0.0105 + 0.009 + 0.010375 + 0.01 + 0.010041 + 0.010667 + 0.009584 + 0.012791 + 0.01 + 0.010041 + 0.010459 + 0.011792 + 0.0135 + 0.012916 + 0.011417 + 0.012834 + 0.014584 + 0.010917 + 0.01 + 0.02575 + 0.010833 + 0.011 + 0.018708 + 0.013375 + 0.010417 + 0.012042 + 0.00975 + 0.009083 + 0.0085 + 0.009292 + 0.008833 + 0.008875 + 0.00875 + 0.009 + 0.011958 + 0.0075 + 0.007542 + 0.010041 + 0.010584 + 0.009666 + 0.0085 + 0.00925 + 0.014667 + 0.009208 + 0.009417 + 0.00925 + 0.010041 + 0.0095 + 0.00925 + 0.009333 + 0.009208 + 0.008958 + 0.009834 + 0.0095 + 0.009542 + 0.010167 + 0.009083 + 0.009334 + 0.010208 + 0.009083 + 0.017875 + 0.009833 + 0.009041 + 0.009667 + 0.009583 + 0.008917 + 0.008584 + 0.008375 + 0.00925 + 0.009625 + 0.009708 + 0.00875 + 0.009125 + 0.009208 + 0.01725 + 0.009875 + 0.010042 + 0.00975 + 0.009667 + 0.012 + 0.0095 + 0.009958 + 0.009667 + 0.0095 + 0.010708 + 0.010916 + 0.009875 + 0.010125 + 0.013541 + 0.050042 + 0.013458 + 0.010833 + 0.010625 + 0.010958 + 0.009833 + 0.010167 + 0.009583 + 0.010166 + 0.016167 + 0.012375 + 0.011333 + 0.010458 + 0.009834 + 0.009292 + 0.009875 + 0.009708 + 0.009208 + 0.00975 + 0.009833 + 0.010708 + 0.009583 + 0.011208 + 0.010625 + 0.012958 + 0.012666 + 0.011792 + 0.010292 + 0.01025 + 0.009959 + 0.010166 + 0.012875 + 0.012375 + 0.011709 + 0.010959 + 0.01075 + 0.010667 + 0.013708 + 0.01075 + 0.009542 + 0.008541 + 0.008833 + 0.009084 + 0.008666 + 0.009083 + 0.013292 + 0.011375 + 0.010375 + 0.00825 + 0.011291 + 0.009458 + 0.009292 + 0.009125 + 0.0095 + 0.009042 + 0.009416 + 0.009 + 0.008625 + 0.009166 + 0.009125 + 0.009958 + 0.009333 + 0.009459 + 0.00925 + 0.008625 + 0.009084 + 0.01 + 0.009042 + 0.009666 + 0.009417 + 0.009666 + 0.009 + 0.009416 + 0.009208 + 0.008875 + 0.00975 + 0.009458 + 0.009375 + 0.009208 + 0.009541 + 0.009333 + 0.009458 + 0.008834 + 0.009709 + 0.009584 + 0.009375 + 0.0105 + 0.009417 + 0.0105 + 0.011208 + 0.009375 + 0.00925 + 0.009917 + 0.0105 + 0.011084 + 0.014042 + 0.013375 + 0.014459 + 0.013208 + 0.011792 + 0.010875 + 0.010459 + 0.00975 + 0.009125 + 0.009459 + 0.012458 + 0.011708 + 0.011125 + 0.009667 + 0.00925 + 0.009292 + 0.009708 + 0.009541 + 0.009292 + 0.009208 + 0.009458 + 0.009208 + 0.00875 + 0.009375 + 0.009208 + 0.009792 + 0.009708 + 0.010458 + 0.014083 + 0.012917 + 0.013459 + 0.0135 + 0.01325 + 0.010083 + 0.012291 + 0.011208 + 0.011833 + 0.010083 + 0.011083 + 0.012041 + 0.010417 + 0.013625 + 0.01325 + 0.011875 + 0.011208]} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-latencies.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-latencies.edn new file mode 100644 index 00000000..be7ac35f --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/page-latencies.edn @@ -0,0 +1,49 @@ +{:workload :page, + :valid-at 1000, + :warmup-per-variant 1000, + :rounds 7, + :per-round 500, + :checksum 490000, + :variants + {:raw-four + {:n 3500, + :median-ms 0.008416, + :p95-ms 0.012708, + :min-ms 0.004375, + :max-ms 0.088458}, + :inline-eight + {:n 3500, + :median-ms 0.008791, + :p95-ms 0.014167, + :min-ms 0.005667, + :max-ms 0.07725}, + :filter-inline-eight + {:n 3500, + :median-ms 0.01, + :p95-ms 0.015708, + :min-ms 0.005459, + :max-ms 0.051208}, + :filter-tx-naive + {:n 3500, + :median-ms 0.034959, + :p95-ms 0.048166, + :min-ms 0.030083, + :max-ms 0.15875}, + :filter-tx-request-memo + {:n 3500, + :median-ms 0.011917, + :p95-ms 0.018125, + :min-ms 0.0075, + :max-ms 0.119666}, + :filter-tx-shared-memo + {:n 3500, + :median-ms 0.009125, + :p95-ms 0.014625, + :min-ms 0.005416, + :max-ms 0.100083}, + :filter-tx-sparse-bounds + {:n 3500, + :median-ms 0.00975, + :p95-ms 0.014917, + :min-ms 0.005375, + :max-ms 0.065208}}} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/point-latencies-raw.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/point-latencies-raw.edn new file mode 100644 index 00000000..f4d01b1f --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/point-latencies-raw.edn @@ -0,0 +1,98007 @@ +{:raw-four + [0.021708 + 0.007666 + 0.006125 + 0.015417 + 0.004291 + 0.004375 + 0.004166 + 0.004167 + 0.00375 + 0.003417 + 0.00375 + 0.004042 + 0.003667 + 0.003917 + 0.003959 + 0.003584 + 0.004125 + 0.003584 + 0.003541 + 0.004125 + 0.003875 + 0.003625 + 0.003458 + 0.003583 + 0.0035 + 0.034292 + 0.005167 + 0.004167 + 0.003375 + 0.003458 + 0.003375 + 0.004458 + 0.004416 + 0.00375 + 0.00375 + 0.003458 + 0.003375 + 0.004708 + 0.004042 + 0.003875 + 0.003875 + 0.003708 + 0.004041 + 0.003833 + 0.003666 + 0.003958 + 0.004709 + 0.013375 + 0.006708 + 0.006417 + 0.005834 + 0.00575 + 0.005584 + 0.00575 + 0.00525 + 0.003666 + 0.004292 + 0.005208 + 0.005209 + 0.005041 + 0.0045 + 0.005625 + 0.005667 + 0.005417 + 0.008625 + 0.004417 + 0.00375 + 0.005708 + 0.005208 + 0.005083 + 0.005167 + 0.00425 + 0.004875 + 0.005291 + 0.006541 + 0.005041 + 0.005833 + 0.005667 + 0.005334 + 0.005459 + 0.005792 + 0.004667 + 0.005583 + 0.00575 + 0.005125 + 0.005041 + 0.004958 + 0.005917 + 0.004792 + 0.003875 + 0.003458 + 0.003791 + 0.004083 + 0.004458 + 0.004792 + 0.005 + 0.005333 + 0.005667 + 0.004959 + 0.004583 + 0.004458 + 0.0055 + 0.004583 + 0.00475 + 0.004416 + 0.00425 + 0.004792 + 0.004541 + 0.004917 + 0.004584 + 0.004625 + 0.004708 + 0.00425 + 0.004541 + 0.004375 + 0.004917 + 0.00475 + 0.004584 + 0.005875 + 0.004792 + 0.0045 + 0.004875 + 0.004459 + 0.004709 + 0.004459 + 0.004791 + 0.004125 + 0.004208 + 0.004083 + 0.004542 + 0.004375 + 0.004416 + 0.004542 + 0.004416 + 0.004875 + 0.00425 + 0.004458 + 0.005166 + 0.004917 + 0.004958 + 0.00475 + 0.00475 + 0.005084 + 0.00475 + 0.004792 + 0.004666 + 0.005333 + 0.005875 + 0.005708 + 0.007208 + 0.007833 + 0.007166 + 0.007625 + 0.006375 + 0.005083 + 0.004625 + 0.005333 + 0.005041 + 0.004625 + 0.00475 + 0.004958 + 0.004834 + 0.006166 + 0.005125 + 0.006083 + 0.005292 + 0.005042 + 0.004958 + 0.004916 + 0.004833 + 0.005 + 0.005209 + 0.004959 + 0.005833 + 0.005833 + 0.005667 + 0.009167 + 0.006458 + 0.005583 + 0.005541 + 0.005833 + 0.005417 + 0.00625 + 0.005959 + 0.006917 + 0.006042 + 0.005833 + 0.010125 + 0.005583 + 0.004958 + 0.004833 + 0.005291 + 0.005 + 0.004292 + 0.004833 + 0.004625 + 0.00525 + 0.005708 + 0.004667 + 0.004708 + 0.005291 + 0.005125 + 0.004375 + 0.004458 + 0.004583 + 0.00475 + 0.004458 + 0.004583 + 0.004208 + 0.0045 + 0.004958 + 0.004959 + 0.005084 + 0.005084 + 0.00525 + 0.005333 + 0.005708 + 0.015375 + 0.009458 + 0.006167 + 0.005459 + 0.004667 + 0.004709 + 0.004667 + 0.00475 + 0.004792 + 0.0045 + 0.004792 + 0.00725 + 0.00525 + 0.005458 + 0.005 + 0.004542 + 0.004458 + 0.004125 + 0.00475 + 0.004875 + 0.00475 + 0.005708 + 0.005166 + 0.004833 + 0.005083 + 0.007042 + 0.005542 + 0.004875 + 0.00525 + 0.006583 + 0.008333 + 0.007917 + 0.010083 + 0.007875 + 0.007125 + 0.0075 + 0.013375 + 0.00775 + 0.0065 + 0.006 + 0.00625 + 0.005792 + 0.007291 + 0.005625 + 0.005542 + 0.0055 + 0.004167 + 0.004916 + 0.007625 + 0.00575 + 0.00475 + 0.004542 + 0.009834 + 0.006541 + 0.005875 + 0.006625 + 0.006208 + 0.008292 + 0.007667 + 0.00625 + 0.00575 + 0.006167 + 0.008291 + 0.008167 + 0.007334 + 0.008292 + 0.007375 + 0.007917 + 0.007291 + 0.0085 + 0.007208 + 0.00675 + 0.006375 + 0.005083 + 0.005375 + 0.005625 + 0.005417 + 0.004917 + 0.005166 + 0.005209 + 0.005166 + 0.005583 + 0.004833 + 0.004708 + 0.004917 + 0.004708 + 0.005166 + 0.005042 + 0.004708 + 0.005 + 0.004667 + 0.005167 + 0.004458 + 0.004458 + 0.004625 + 0.0045 + 0.004625 + 0.00425 + 0.005 + 0.004834 + 0.00425 + 0.00425 + 0.004541 + 0.004334 + 0.00425 + 0.004542 + 0.004541 + 0.004417 + 0.00475 + 0.004708 + 0.004667 + 0.004458 + 0.004292 + 0.004334 + 0.004625 + 0.004584 + 0.0045 + 0.004417 + 0.004416 + 0.00425 + 0.005292 + 0.004625 + 0.004958 + 0.004791 + 0.004917 + 0.004666 + 0.004917 + 0.004375 + 0.009208 + 0.006375 + 0.006917 + 0.007 + 0.006917 + 0.006875 + 0.006709 + 0.007166 + 0.007417 + 0.007 + 0.006167 + 0.005917 + 0.005917 + 0.005792 + 0.005667 + 0.005541 + 0.006 + 0.006 + 0.006084 + 0.0055 + 0.005125 + 0.004958 + 0.006708 + 0.005291 + 0.004958 + 0.005416 + 0.005084 + 0.00525 + 0.004875 + 0.006416 + 0.006166 + 0.006 + 0.006084 + 0.007875 + 0.005916 + 0.005916 + 0.005125 + 0.004916 + 0.005667 + 0.006 + 0.008167 + 0.007375 + 0.006625 + 0.007083 + 0.00625 + 0.004666 + 0.004833 + 0.00475 + 0.004916 + 0.004792 + 0.00475 + 0.004416 + 0.00475 + 0.005333 + 0.004458 + 0.005 + 0.005708 + 0.004625 + 0.005083 + 0.004917 + 0.005333 + 0.005208 + 0.005709 + 0.004833 + 0.004625 + 0.005208 + 0.005083 + 0.0045 + 0.004417 + 0.004875 + 0.005375 + 0.005583 + 0.005625 + 0.005542 + 0.006167 + 0.005833 + 0.004916 + 0.005417 + 0.004916 + 0.007458 + 0.004792 + 0.004917 + 0.005291 + 0.004667 + 0.0045 + 0.004709 + 0.0055 + 0.005041 + 0.005083 + 0.005 + 0.005667 + 0.005792 + 0.005042 + 0.005708 + 0.005 + 0.004458 + 0.005 + 0.006417 + 0.005583 + 0.005459 + 0.004834 + 0.004584 + 0.005042 + 0.005167 + 0.005417 + 0.005458 + 0.009542 + 0.006833 + 0.006792 + 0.0065 + 0.005958 + 0.005125 + 0.006042 + 0.005834 + 0.004917 + 0.004583 + 0.004333 + 0.0045 + 0.004333 + 0.00525 + 0.00575 + 0.00575 + 0.005792 + 0.0055 + 0.005792 + 0.005209 + 0.005584 + 0.005667 + 0.005417 + 0.005334 + 0.005542 + 0.005375 + 0.006625 + 0.006875 + 0.006583 + 0.007125 + 0.006375 + 0.005625 + 0.006917 + 0.007 + 0.006708 + 0.007083 + 0.006625 + 0.007167 + 0.006959 + 0.005625 + 0.004792 + 0.00525 + 0.003708 + 0.0045 + 0.004292 + 0.004625 + 0.003959 + 0.004833 + 0.005208 + 0.004333 + 0.004333 + 0.0045 + 0.004375 + 0.004625 + 0.004542 + 0.004708 + 0.003875 + 0.00425 + 0.004334 + 0.004375 + 0.004292 + 0.004208 + 0.003958 + 0.004292 + 0.004125 + 0.004875 + 0.004417 + 0.004625 + 0.004791 + 0.004667 + 0.004208 + 0.004333 + 0.00425 + 0.00425 + 0.004458 + 0.004375 + 0.004333 + 0.004583 + 0.004625 + 0.004458 + 0.004333 + 0.0045 + 0.004917 + 0.004542 + 0.004834 + 0.004292 + 0.004333 + 0.005208 + 0.004708 + 0.005041 + 0.00625 + 0.005959 + 0.006042 + 0.006583 + 0.006208 + 0.00625 + 0.006625 + 0.007417 + 0.006083 + 0.006625 + 0.0065 + 0.008 + 0.006667 + 0.006375 + 0.00525 + 0.005208 + 0.006 + 0.005916 + 0.005541 + 0.005625 + 0.005209 + 0.007084 + 0.006125 + 0.005208 + 0.005375 + 0.005292 + 0.00475 + 0.005 + 0.005916 + 0.005959 + 0.005958 + 0.004833 + 0.005375 + 0.005542 + 0.005833 + 0.0055 + 0.007083 + 0.006875 + 0.007958 + 0.006583 + 0.006041 + 0.005333 + 0.006708 + 0.007875 + 0.007791 + 0.006166 + 0.00825 + 0.006917 + 0.006542 + 0.005084 + 0.005417 + 0.005375 + 0.0055 + 0.005291 + 0.00525 + 0.005208 + 0.005917 + 0.005667 + 0.004291 + 0.004084 + 0.004583 + 0.004708 + 0.004375 + 0.00425 + 0.003959 + 0.004125 + 0.0045 + 0.006083 + 0.005375 + 0.004625 + 0.005167 + 0.004708 + 0.00425 + 0.004167 + 0.004334 + 0.004542 + 0.004458 + 0.004833 + 0.004458 + 0.004583 + 0.004125 + 0.004416 + 0.004833 + 0.004334 + 0.004333 + 0.004375 + 0.0045 + 0.004334 + 0.004792 + 0.006792 + 0.00475 + 0.003959 + 0.004458 + 0.004167 + 0.004625 + 0.003875 + 0.004 + 0.004334 + 0.00425 + 0.004209 + 0.0045 + 0.004459 + 0.004416 + 0.004125 + 0.004667 + 0.004583 + 0.004292 + 0.00525 + 0.009958 + 0.011 + 0.006583 + 0.006709 + 0.007125 + 0.007042 + 0.005458 + 0.004583 + 0.005083 + 0.006292 + 0.006208 + 0.006084 + 0.005167 + 0.005 + 0.004667 + 0.004708 + 0.0045 + 0.004625 + 0.004584 + 0.004917 + 0.004375 + 0.004709 + 0.004458 + 0.005208 + 0.006083 + 0.005458 + 0.00675 + 0.006916 + 0.005625 + 0.005458 + 0.005375 + 0.005375 + 0.00525 + 0.004875 + 0.005542 + 0.006458 + 0.007125 + 0.006291 + 0.006 + 0.005583 + 0.005417 + 0.006667 + 0.005291 + 0.005375 + 0.005209 + 0.004917 + 0.005125 + 0.005208 + 0.004916 + 0.005333 + 0.00425 + 0.0045 + 0.003625 + 0.004125 + 0.004042 + 0.004417 + 0.004292 + 0.004042 + 0.004291 + 0.004042 + 0.004083 + 0.004291 + 0.003959 + 0.004208 + 0.003875 + 0.003708 + 0.003833 + 0.003792 + 0.004125 + 0.004042 + 0.004291 + 0.004334 + 0.004167 + 0.003958 + 0.004209 + 0.004416 + 0.004209 + 0.00375 + 0.00425 + 0.004292 + 0.004041 + 0.003875 + 0.004 + 0.004125 + 0.00425 + 0.004125 + 0.004167 + 0.004083 + 0.004209 + 0.004292 + 0.004792 + 0.004833 + 0.004083 + 0.004084 + 0.004 + 0.00425 + 0.004083 + 0.004417 + 0.004625 + 0.005667 + 0.005 + 0.010375 + 0.006417 + 0.0065 + 0.006666 + 0.006708 + 0.007125 + 0.005416 + 0.005416 + 0.005458 + 0.005125 + 0.004167 + 0.008333 + 0.005875 + 0.005541 + 0.005959 + 0.00525 + 0.005833 + 0.00575 + 0.005292 + 0.005208 + 0.004416 + 0.0045 + 0.00425 + 0.004458 + 0.004417 + 0.005209 + 0.004834 + 0.007083 + 0.007042 + 0.006334 + 0.006667 + 0.006625 + 0.005541 + 0.005041 + 0.00625 + 0.006625 + 0.007083 + 0.00675 + 0.006167 + 0.006542 + 0.005917 + 0.004916 + 0.004416 + 0.004125 + 0.004042 + 0.003917 + 0.003791 + 0.003792 + 0.004375 + 0.004084 + 0.004083 + 0.004458 + 0.004083 + 0.004 + 0.004125 + 0.004166 + 0.004417 + 0.004 + 0.003959 + 0.003875 + 0.004583 + 0.004375 + 0.00425 + 0.0035 + 0.003666 + 0.003917 + 0.003708 + 0.003958 + 0.004292 + 0.004042 + 0.003833 + 0.004542 + 0.004042 + 0.004208 + 0.003917 + 0.003708 + 0.004375 + 0.004125 + 0.003958 + 0.003917 + 0.00425 + 0.004292 + 0.003875 + 0.004125 + 0.004208 + 0.004208 + 0.003833 + 0.004292 + 0.004041 + 0.004334 + 0.00525 + 0.005667 + 0.005667 + 0.005375 + 0.005 + 0.005 + 0.005167 + 0.005083 + 0.004833 + 0.006458 + 0.006458 + 0.006833 + 0.007125 + 0.006542 + 0.005916 + 0.005542 + 0.0045 + 0.004583 + 0.00525 + 0.004458 + 0.005 + 0.00475 + 0.004625 + 0.004834 + 0.004834 + 0.004792 + 0.005958 + 0.004708 + 0.004958 + 0.004916 + 0.004375 + 0.004666 + 0.004542 + 0.004625 + 0.004791 + 0.005167 + 0.005375 + 0.006 + 0.006417 + 0.005959 + 0.005667 + 0.005416 + 0.005333 + 0.004875 + 0.005833 + 0.007041 + 0.00575 + 0.005541 + 0.005417 + 0.005666 + 0.005125 + 0.004791 + 0.004208 + 0.004167 + 0.004208 + 0.004375 + 0.004125 + 0.004375 + 0.00425 + 0.005333 + 0.004417 + 0.004292 + 0.004375 + 0.004375 + 0.004958 + 0.004708 + 0.005584 + 0.006 + 0.006041 + 0.006167 + 0.005083 + 0.004958 + 0.010542 + 0.0045 + 0.004334 + 0.004208 + 0.00425 + 0.004583 + 0.004708 + 0.004458 + 0.004125 + 0.004083 + 0.004416 + 0.0045 + 0.010375 + 0.005 + 0.004625 + 0.005042 + 0.005667 + 0.005708 + 0.005083 + 0.056458 + 0.018417 + 0.00725 + 0.005541 + 0.005458 + 0.005292 + 0.005333 + 0.005166 + 0.00625 + 0.008125 + 0.00625 + 0.005583 + 0.005667 + 0.008875 + 0.005709 + 0.005625 + 0.006375 + 0.006167 + 0.008625 + 0.00725 + 0.006416 + 0.006083 + 0.006167 + 0.009083 + 0.006292 + 0.005542 + 0.012166 + 0.010333 + 0.006417 + 0.005583 + 0.005833 + 0.005958 + 0.0055 + 0.005667 + 0.044167 + 0.009708 + 0.005375 + 0.005209 + 0.005542 + 0.005833 + 0.005708 + 0.004958 + 0.005125 + 0.005875 + 0.004542 + 0.01025 + 0.008042 + 0.006833 + 0.006167 + 0.006208 + 0.006333 + 0.00825 + 0.006291 + 0.007625 + 0.006542 + 0.00625 + 0.006417 + 0.005959 + 0.00975 + 0.007792 + 0.007 + 0.005875 + 0.005542 + 0.005333 + 0.004417 + 0.0045 + 0.004625 + 0.004625 + 0.004834 + 0.004291 + 0.0045 + 0.004583 + 0.00525 + 0.00475 + 0.004584 + 0.004709 + 0.004417 + 0.004291 + 0.004625 + 0.00475 + 0.004709 + 0.00475 + 0.004375 + 0.004542 + 0.00475 + 0.004875 + 0.004292 + 0.004416 + 0.004 + 0.00425 + 0.00475 + 0.004208 + 0.00475 + 0.004542 + 0.00425 + 0.004792 + 0.004667 + 0.004583 + 0.004584 + 0.004417 + 0.00725 + 0.004375 + 0.004334 + 0.004542 + 0.004917 + 0.004292 + 0.004416 + 0.004542 + 0.0045 + 0.007959 + 0.004875 + 0.004917 + 0.004958 + 0.005208 + 0.004583 + 0.004583 + 0.004667 + 0.003917 + 0.005666 + 0.011958 + 0.005667 + 0.005875 + 0.005584 + 0.00625 + 0.005792 + 0.006209 + 0.005 + 0.004875 + 0.005333 + 0.005042 + 0.004916 + 0.004708 + 0.004709 + 0.00475 + 0.004625 + 0.005083 + 0.005042 + 0.00825 + 0.005125 + 0.004625 + 0.005334 + 0.004833 + 0.004833 + 0.004833 + 0.004541 + 0.005625 + 0.007292 + 0.005916 + 0.005667 + 0.005875 + 0.006 + 0.00575 + 0.004917 + 0.006584 + 0.005875 + 0.005583 + 0.006416 + 0.006375 + 0.006542 + 0.006792 + 0.005958 + 0.004875 + 0.0045 + 0.004542 + 0.004917 + 0.004333 + 0.004333 + 0.004625 + 0.004958 + 0.004833 + 0.005333 + 0.004708 + 0.005458 + 0.005042 + 0.004875 + 0.006041 + 0.005833 + 0.007042 + 0.005875 + 0.005542 + 0.005583 + 0.005333 + 0.004791 + 0.005042 + 0.004584 + 0.00525 + 0.004625 + 0.004458 + 0.004458 + 0.004833 + 0.004417 + 0.004666 + 0.004625 + 0.004375 + 0.0045 + 0.004917 + 0.004416 + 0.0045 + 0.004333 + 0.005166 + 0.005583 + 0.004625 + 0.004292 + 0.004334 + 0.004792 + 0.004458 + 0.004625 + 0.004666 + 0.004917 + 0.005083 + 0.005541 + 0.005667 + 0.0055 + 0.005125 + 0.0055 + 0.005625 + 0.005542 + 0.005292 + 0.006333 + 0.006541 + 0.00625 + 0.00625 + 0.007042 + 0.007917 + 0.00625 + 0.00775 + 0.006666 + 0.005833 + 0.004917 + 0.004916 + 0.004917 + 0.005084 + 0.004792 + 0.005083 + 0.005916 + 0.006542 + 0.0055 + 0.006833 + 0.006417 + 0.006292 + 0.005584 + 0.005625 + 0.005875 + 0.005125 + 0.004833 + 0.007125 + 0.006416 + 0.005791 + 0.006458 + 0.005625 + 0.005958 + 0.005208 + 0.007083 + 0.005709 + 0.007375 + 0.005833 + 0.006041 + 0.005459 + 0.006542 + 0.009833 + 0.017292 + 0.009041 + 0.00675 + 0.007709 + 0.006584 + 0.005666 + 0.006333 + 0.005917 + 0.004958 + 0.005292 + 0.004458 + 0.004834 + 0.006041 + 0.006459 + 0.005541 + 0.006083 + 0.005542 + 0.005833 + 0.005667 + 0.005375 + 0.005375 + 0.005833 + 0.005917 + 0.005166 + 0.005834 + 0.004916 + 0.004667 + 0.0045 + 0.005333 + 0.004833 + 0.004791 + 0.004 + 0.012209 + 0.004541 + 0.004208 + 0.004584 + 0.00475 + 0.005 + 0.005166 + 0.0055 + 0.005959 + 0.00575 + 0.005167 + 0.005459 + 0.005667 + 0.005625 + 0.004208 + 0.0045 + 0.004375 + 0.00475 + 0.005042 + 0.005291 + 0.005209 + 0.004875 + 0.004625 + 0.004834 + 0.004708 + 0.004292 + 0.005 + 0.005667 + 0.0075 + 0.006875 + 0.006542 + 0.006625 + 0.006667 + 0.006625 + 0.005917 + 0.00525 + 0.005167 + 0.006458 + 0.006042 + 0.006125 + 0.005542 + 0.006 + 0.006 + 0.006083 + 0.006 + 0.006 + 0.005792 + 0.006334 + 0.005834 + 0.004834 + 0.00475 + 0.005125 + 0.005792 + 0.006875 + 0.008584 + 0.007625 + 0.007208 + 0.0065 + 0.005875 + 0.005875 + 0.007125 + 0.005708 + 0.006166 + 0.00575 + 0.005625 + 0.005625 + 0.005875 + 0.006917 + 0.006292 + 0.00575 + 0.005667 + 0.005459 + 0.004958 + 0.005042 + 0.005167 + 0.004708 + 0.0055 + 0.005917 + 0.004416 + 0.004459 + 0.0045 + 0.005125 + 0.005875 + 0.005833 + 0.005792 + 0.005875 + 0.00575 + 0.005709 + 0.005209 + 0.0065 + 0.006041 + 0.005333 + 0.006042 + 0.005875 + 0.006 + 0.005625 + 0.005458 + 0.005458 + 0.005375 + 0.005458 + 0.00525 + 0.004583 + 0.004334 + 0.005167 + 0.005333 + 0.005083 + 0.004917 + 0.005583 + 0.004833 + 0.005125 + 0.004333 + 0.00475 + 0.004625 + 0.004625 + 0.004042 + 0.004667 + 0.004458 + 0.004875 + 0.004875 + 0.004792 + 0.00575 + 0.005292 + 0.005375 + 0.005667 + 0.005666 + 0.0055 + 0.005958 + 0.006791 + 0.010375 + 0.006709 + 0.006417 + 0.006792 + 0.007083 + 0.006333 + 0.006084 + 0.006333 + 0.0055 + 0.004417 + 0.00575 + 0.006708 + 0.005833 + 0.005625 + 0.006292 + 0.008416 + 0.00525 + 0.005542 + 0.005875 + 0.005917 + 0.005834 + 0.005709 + 0.005083 + 0.00475 + 0.004875 + 0.004875 + 0.006958 + 0.008208 + 0.007417 + 0.007042 + 0.006666 + 0.006125 + 0.005958 + 0.005083 + 0.007917 + 0.006458 + 0.006417 + 0.005333 + 0.005417 + 0.005334 + 0.005958 + 0.006625 + 0.005542 + 0.005292 + 0.005167 + 0.005666 + 0.005583 + 0.005 + 0.004875 + 0.004667 + 0.004209 + 0.003958 + 0.004083 + 0.004542 + 0.0045 + 0.004084 + 0.004208 + 0.004208 + 0.004625 + 0.004125 + 0.005291 + 0.005333 + 0.005541 + 0.005375 + 0.004958 + 0.004708 + 0.00475 + 0.00475 + 0.004458 + 0.004958 + 0.0045 + 0.004417 + 0.005417 + 0.00525 + 0.004958 + 0.004709 + 0.005125 + 0.005458 + 0.005125 + 0.005417 + 0.005 + 0.005334 + 0.004958 + 0.005333 + 0.005291 + 0.005417 + 0.005167 + 0.005042 + 0.0055 + 0.004541 + 0.004666 + 0.004958 + 0.004917 + 0.005625 + 0.005458 + 0.0055 + 0.005709 + 0.005833 + 0.005292 + 0.005125 + 0.006666 + 0.006375 + 0.005417 + 0.008583 + 0.00775 + 0.006291 + 0.007667 + 0.006 + 0.005583 + 0.004792 + 0.004458 + 0.004708 + 0.005 + 0.004542 + 0.00475 + 0.005208 + 0.005583 + 0.00575 + 0.0055 + 0.005792 + 0.005708 + 0.005375 + 0.005292 + 0.005125 + 0.005416 + 0.00425 + 0.006792 + 0.006417 + 0.005709 + 0.005708 + 0.005708 + 0.005542 + 0.005709 + 0.004583 + 0.006375 + 0.006125 + 0.005458 + 0.0055 + 0.005458 + 0.005458 + 0.006125 + 0.005708 + 0.005083 + 0.004584 + 0.004417 + 0.004166 + 0.005875 + 0.004375 + 0.004459 + 0.004542 + 0.004541 + 0.004708 + 0.004125 + 0.005041 + 0.004375 + 0.003959 + 0.004458 + 0.004375 + 0.004709 + 0.004584 + 0.004417 + 0.004208 + 0.004375 + 0.004708 + 0.00425 + 0.00425 + 0.004375 + 0.004791 + 0.004458 + 0.004458 + 0.004375 + 0.004458 + 0.004667 + 0.004625 + 0.005041 + 0.004208 + 0.004333 + 0.003792 + 0.003875 + 0.004375 + 0.004167 + 0.004 + 0.004375 + 0.004542 + 0.004584 + 0.004583 + 0.004542 + 0.004583 + 0.004625 + 0.004291 + 0.004958 + 0.004833 + 0.005042 + 0.0045 + 0.0045 + 0.004833 + 0.004459 + 0.004542 + 0.004917 + 0.005458 + 0.006459 + 0.006 + 0.005458 + 0.005375 + 0.0055 + 0.005 + 0.005916 + 0.005208 + 0.004333 + 0.004416 + 0.004458 + 0.004958 + 0.004375 + 0.004417 + 0.004916 + 0.004875 + 0.004667 + 0.00475 + 0.004667 + 0.00475 + 0.004833 + 0.00475 + 0.004708 + 0.004709 + 0.004709 + 0.004833 + 0.007458 + 0.006083 + 0.005917 + 0.005834 + 0.00575 + 0.005333 + 0.005792 + 0.006708 + 0.005 + 0.0055 + 0.005459 + 0.005709 + 0.005542 + 0.0055 + 0.007042 + 0.005833 + 0.004708 + 0.004416 + 0.004458 + 0.0045 + 0.004583 + 0.004417 + 0.004291 + 0.004958 + 0.004167 + 0.005292 + 0.005584 + 0.005292 + 0.005041 + 0.004125 + 0.004208 + 0.004583 + 0.004167 + 0.004292 + 0.004208 + 0.004459 + 0.004584 + 0.004417 + 0.004458 + 0.004291 + 0.004458 + 0.004583 + 0.004292 + 0.004792 + 0.004708 + 0.004333 + 0.004625 + 0.004291 + 0.004542 + 0.004375 + 0.004125 + 0.004125 + 0.004292 + 0.004458 + 0.004375 + 0.004459 + 0.004125 + 0.004291 + 0.004584 + 0.004666 + 0.004292 + 0.004166 + 0.004625 + 0.00425 + 0.004417 + 0.004833 + 0.005125 + 0.00475 + 0.00475 + 0.004208 + 0.004583 + 0.004792 + 0.004333 + 0.004792 + 0.005583 + 0.005333 + 0.009291 + 0.005625 + 0.005708 + 0.005625 + 0.005625 + 0.00475 + 0.004583 + 0.004417 + 0.004834 + 0.004708 + 0.004791 + 0.004708 + 0.004708 + 0.004791 + 0.004875 + 0.005875 + 0.004625 + 0.005083 + 0.004541 + 0.004417 + 0.004542 + 0.004875 + 0.00475 + 0.00525 + 0.005625 + 0.005667 + 0.005417 + 0.006458 + 0.006083 + 0.005334 + 0.005708 + 0.005333 + 0.005042 + 0.00575 + 0.006458 + 0.006792 + 0.006541 + 0.006541 + 0.004958 + 0.005208 + 0.004625 + 0.004417 + 0.005084 + 0.005334 + 0.005334 + 0.005167 + 0.004458 + 0.006166 + 0.005208 + 0.004334 + 0.003875 + 0.004334 + 0.004666 + 0.004417 + 0.003959 + 0.003875 + 0.004542 + 0.0045 + 0.004208 + 0.004375 + 0.004417 + 0.004375 + 0.004542 + 0.004291 + 0.004375 + 0.004416 + 0.004333 + 0.0045 + 0.004458 + 0.004416 + 0.004834 + 0.00475 + 0.004792 + 0.004583 + 0.004417 + 0.0045 + 0.00425 + 0.004 + 0.00425 + 0.004084 + 0.004209 + 0.004375 + 0.004417 + 0.004459 + 0.004375 + 0.004208 + 0.00425 + 0.004333 + 0.004833 + 0.004875 + 0.004958 + 0.004708 + 0.004875 + 0.005708 + 0.004917 + 0.004459 + 0.004917 + 0.004625 + 0.005917 + 0.005708 + 0.005666 + 0.0055 + 0.006084 + 0.006417 + 0.0055 + 0.004667 + 0.0045 + 0.004792 + 0.004667 + 0.004708 + 0.004625 + 0.004625 + 0.005292 + 0.004708 + 0.004708 + 0.004167 + 0.004916 + 0.00475 + 0.004791 + 0.005 + 0.006292 + 0.005125 + 0.004916 + 0.004958 + 0.004792 + 0.005708 + 0.005666 + 0.005958 + 0.005416 + 0.006083 + 0.01125 + 0.0055 + 0.004417 + 0.005667 + 0.005458 + 0.005625 + 0.005292 + 0.00625 + 0.00575 + 0.005667 + 0.005209 + 0.004792 + 0.004791 + 0.004417 + 0.004459 + 0.004291 + 0.0045 + 0.004583 + 0.004958 + 0.00425 + 0.004458 + 0.00425 + 0.005167 + 0.004209 + 0.003709 + 0.004208 + 0.0045 + 0.004 + 0.00475 + 0.004583 + 0.004625 + 0.0045 + 0.004459 + 0.004417 + 0.0045 + 0.004167 + 0.004417 + 0.004541 + 0.012959 + 0.005417 + 0.005167 + 0.004708 + 0.004833 + 0.00475 + 0.0045 + 0.004584 + 0.004083 + 0.00425 + 0.004167 + 0.004 + 0.003667 + 0.003625 + 0.00375 + 0.00425 + 0.003917 + 0.003833 + 0.004083 + 0.003917 + 0.004584 + 0.00425 + 0.004791 + 0.004333 + 0.004625 + 0.007833 + 0.0045 + 0.004125 + 0.004292 + 0.005375 + 0.007375 + 0.005958 + 0.006583 + 0.006208 + 0.006084 + 0.008334 + 0.006833 + 0.006292 + 0.006667 + 0.013166 + 0.005292 + 0.005 + 0.005 + 0.005041 + 0.003917 + 0.005041 + 0.004083 + 0.012042 + 0.004959 + 0.004916 + 0.005958 + 0.0055 + 0.006083 + 0.00625 + 0.00525 + 0.006167 + 0.005416 + 0.00775 + 0.012791 + 0.005542 + 0.006042 + 0.007666 + 0.007917 + 0.007208 + 0.005958 + 0.007875 + 0.007125 + 0.006292 + 0.00825 + 0.007333 + 0.006417 + 0.00675 + 0.008167 + 0.006083 + 0.005708 + 0.005458 + 0.005625 + 0.005875 + 0.005167 + 0.004791 + 0.005792 + 0.005083 + 0.003875 + 0.004917 + 0.004666 + 0.005292 + 0.004625 + 0.012083 + 0.00575 + 0.005833 + 0.006542 + 0.006333 + 0.031042 + 0.005875 + 0.005083 + 0.004708 + 0.005416 + 0.004667 + 0.004125 + 0.004708 + 0.004792 + 0.004334 + 0.0045 + 0.004625 + 0.00375 + 0.004416 + 0.004167 + 0.004292 + 0.003958 + 0.004084 + 0.004375 + 0.004625 + 0.012958 + 0.0045 + 0.005 + 0.004292 + 0.004375 + 0.004583 + 0.0045 + 0.00475 + 0.004542 + 0.006416 + 0.011333 + 0.004458 + 0.013375 + 0.007 + 0.006 + 0.006041 + 0.004834 + 0.004459 + 0.006792 + 0.005833 + 0.005666 + 0.005584 + 0.007084 + 0.007584 + 0.006792 + 0.006458 + 0.005916 + 0.00525 + 0.005833 + 0.005583 + 0.00575 + 0.005291 + 0.004708 + 0.004625 + 0.004417 + 0.004833 + 0.006167 + 0.005792 + 0.011584 + 0.005292 + 0.004791 + 0.004542 + 0.004166 + 0.004458 + 0.005042 + 0.005583 + 0.005625 + 0.005 + 0.007334 + 0.00625 + 0.005959 + 0.005916 + 0.006333 + 0.004625 + 0.005125 + 0.005125 + 0.006708 + 0.006958 + 0.005916 + 0.006 + 0.006083 + 0.004667 + 0.004541 + 0.004083 + 0.005083 + 0.004667 + 0.0045 + 0.008708 + 0.005583 + 0.004625 + 0.004708 + 0.004625 + 0.004917 + 0.004625 + 0.004167 + 0.004166 + 0.00425 + 0.003584 + 0.003292 + 0.002875 + 0.003041 + 0.003125 + 0.003792 + 0.003916 + 0.003917 + 0.003125 + 0.003167 + 0.003209 + 0.00325 + 0.003167 + 0.003041 + 0.0035 + 0.003375 + 0.003167 + 0.003042 + 0.00325 + 0.003208 + 0.003167 + 0.003334 + 0.003167 + 0.002917 + 0.003125 + 0.003084 + 0.003292 + 0.003375 + 0.003375 + 0.003125 + 0.003375 + 0.003 + 0.003833 + 0.004166 + 0.005125 + 0.005458 + 0.004917 + 0.005167 + 0.004583 + 0.004375 + 0.004667 + 0.004875 + 0.009333 + 0.005083 + 0.005083 + 0.005667 + 0.005084 + 0.004959 + 0.0055 + 0.004208 + 0.004375 + 0.003708 + 0.0035 + 0.003375 + 0.003292 + 0.003334 + 0.003583 + 0.003333 + 0.004125 + 0.003375 + 0.0035 + 0.003625 + 0.005583 + 0.004958 + 0.00475 + 0.004375 + 0.00425 + 0.004208 + 0.005375 + 0.004625 + 0.003917 + 0.003833 + 0.00575 + 0.005375 + 0.004959 + 0.005084 + 0.004375 + 0.005708 + 0.004334 + 0.004416 + 0.005542 + 0.004917 + 0.005167 + 0.004708 + 0.004875 + 0.004333 + 0.004084 + 0.004042 + 0.003542 + 0.003709 + 0.003792 + 0.004583 + 0.00475 + 0.003625 + 0.0035 + 0.003917 + 0.004459 + 0.004083 + 0.003541 + 0.004459 + 0.003791 + 0.003792 + 0.003959 + 0.003666 + 0.00375 + 0.003833 + 0.004 + 0.004084 + 0.003959 + 0.003333 + 0.004208 + 0.004 + 0.004042 + 0.003583 + 0.003917 + 0.004209 + 0.004208 + 0.003833 + 0.003625 + 0.004167 + 0.003708 + 0.00375 + 0.003834 + 0.003708 + 0.003833 + 0.00375 + 0.004083 + 0.003834 + 0.003709 + 0.003625 + 0.003875 + 0.003583 + 0.004042 + 0.003916 + 0.003875 + 0.00375 + 0.003833 + 0.0045 + 0.004042 + 0.003959 + 0.003916 + 0.004125 + 0.006042 + 0.006083 + 0.005417 + 0.006084 + 0.005709 + 0.005167 + 0.005542 + 0.005292 + 0.004792 + 0.004791 + 0.004833 + 0.005083 + 0.004667 + 0.004084 + 0.004166 + 0.004542 + 0.003458 + 0.003875 + 0.004125 + 0.004125 + 0.004083 + 0.003542 + 0.004541 + 0.00425 + 0.004375 + 0.004 + 0.003917 + 0.005875 + 0.0065 + 0.00575 + 0.006125 + 0.005917 + 0.00525 + 0.005708 + 0.005083 + 0.00575 + 0.005958 + 0.006125 + 0.005834 + 0.005125 + 0.006333 + 0.005875 + 0.005708 + 0.004541 + 0.004542 + 0.004958 + 0.004833 + 0.004334 + 0.003625 + 0.003875 + 0.004792 + 0.00375 + 0.003291 + 0.003708 + 0.003584 + 0.004042 + 0.003875 + 0.003916 + 0.003584 + 0.003709 + 0.00375 + 0.003917 + 0.00375 + 0.003625 + 0.004125 + 0.003625 + 0.003416 + 0.004042 + 0.003625 + 0.0035 + 0.00425 + 0.003833 + 0.003959 + 0.0035 + 0.003917 + 0.003583 + 0.00375 + 0.003709 + 0.003667 + 0.00375 + 0.003792 + 0.003667 + 0.003959 + 0.003666 + 0.00375 + 0.0035 + 0.004208 + 0.00375 + 0.003708 + 0.0035 + 0.003666 + 0.003833 + 0.004208 + 0.003708 + 0.003833 + 0.003959 + 0.003958 + 0.003958 + 0.003958 + 0.003667 + 0.00475 + 0.006375 + 0.00575 + 0.005584 + 0.005584 + 0.007167 + 0.00525 + 0.005791 + 0.005208 + 0.005291 + 0.004833 + 0.004792 + 0.004709 + 0.004334 + 0.003833 + 0.004291 + 0.004084 + 0.004084 + 0.004167 + 0.004333 + 0.003958 + 0.003875 + 0.003792 + 0.004042 + 0.004125 + 0.004125 + 0.003917 + 0.005916 + 0.0065 + 0.006041 + 0.006042 + 0.005334 + 0.005459 + 0.00575 + 0.005 + 0.005333 + 0.005833 + 0.004833 + 0.005166 + 0.005375 + 0.006125 + 0.006625 + 0.006 + 0.004958 + 0.004666 + 0.004625 + 0.005 + 0.004708 + 0.004875 + 0.004958 + 0.00425 + 0.005167 + 0.004625 + 0.004459 + 0.004 + 0.00375 + 0.003917 + 0.004125 + 0.00375 + 0.003666 + 0.003542 + 0.004416 + 0.003667 + 0.00425 + 0.004083 + 0.003667 + 0.0035 + 0.003584 + 0.003709 + 0.003667 + 0.003667 + 0.003834 + 0.0035 + 0.003792 + 0.004083 + 0.003791 + 0.003875 + 0.003583 + 0.003625 + 0.003958 + 0.003708 + 0.003792 + 0.003792 + 0.003791 + 0.004583 + 0.004583 + 0.004375 + 0.004292 + 0.004542 + 0.004583 + 0.004333 + 0.004042 + 0.004209 + 0.003875 + 0.004542 + 0.003833 + 0.004042 + 0.003834 + 0.00375 + 0.004167 + 0.00575 + 0.005833 + 0.005708 + 0.005416 + 0.005292 + 0.0055 + 0.005542 + 0.005833 + 0.004666 + 0.004542 + 0.00475 + 0.004917 + 0.004625 + 0.004334 + 0.003917 + 0.004208 + 0.004041 + 0.003958 + 0.004167 + 0.0045 + 0.003958 + 0.004208 + 0.004 + 0.003666 + 0.0035 + 0.003708 + 0.004416 + 0.006084 + 0.006084 + 0.0055 + 0.00625 + 0.005875 + 0.005083 + 0.005584 + 0.006084 + 0.00475 + 0.005916 + 0.005708 + 0.005625 + 0.004666 + 0.005083 + 0.006375 + 0.005833 + 0.004583 + 0.004375 + 0.003625 + 0.003375 + 0.003458 + 0.003833 + 0.004541 + 0.0045 + 0.005916 + 0.005292 + 0.004542 + 0.004458 + 0.005125 + 0.004792 + 0.005083 + 0.0045 + 0.004959 + 0.003917 + 0.003792 + 0.003875 + 0.004125 + 0.003625 + 0.00425 + 0.003625 + 0.004041 + 0.003209 + 0.003584 + 0.003458 + 0.003958 + 0.003917 + 0.003625 + 0.003792 + 0.004 + 0.003833 + 0.003667 + 0.003792 + 0.00375 + 0.003875 + 0.00375 + 0.00375 + 0.003833 + 0.003792 + 0.00375 + 0.004042 + 0.003833 + 0.003667 + 0.003834 + 0.00375 + 0.004042 + 0.004125 + 0.004167 + 0.004167 + 0.004625 + 0.0045 + 0.00475 + 0.005583 + 0.004917 + 0.005417 + 0.006334 + 0.006167 + 0.007083 + 0.0065 + 0.005417 + 0.004667 + 0.004959 + 0.004125 + 0.004208 + 0.004375 + 0.004334 + 0.004125 + 0.004083 + 0.005542 + 0.005667 + 0.005042 + 0.005292 + 0.005208 + 0.005375 + 0.005166 + 0.00525 + 0.005 + 0.004959 + 0.004667 + 0.00425 + 0.004167 + 0.005083 + 0.004666 + 0.006667 + 0.006292 + 0.005917 + 0.006667 + 0.006417 + 0.005917 + 0.005375 + 0.005959 + 0.006458 + 0.006417 + 0.006041 + 0.005958 + 0.006375 + 0.005916 + 0.004667 + 0.004667 + 0.004417 + 0.003875 + 0.003875 + 0.003583 + 0.003625 + 0.004292 + 0.004292 + 0.003875 + 0.003375 + 0.003709 + 0.004208 + 0.003875 + 0.004042 + 0.003917 + 0.004084 + 0.003708 + 0.00425 + 0.003959 + 0.003459 + 0.003917 + 0.004125 + 0.0035 + 0.003625 + 0.003625 + 0.003667 + 0.003333 + 0.003541 + 0.003542 + 0.004167 + 0.00375 + 0.003708 + 0.004 + 0.00375 + 0.003583 + 0.003583 + 0.004 + 0.003958 + 0.003708 + 0.003958 + 0.003959 + 0.003916 + 0.004 + 0.003667 + 0.003834 + 0.003625 + 0.003959 + 0.004125 + 0.004292 + 0.004 + 0.003959 + 0.003959 + 0.005417 + 0.004916 + 0.005042 + 0.005041 + 0.005208 + 0.005917 + 0.006042 + 0.006 + 0.005625 + 0.006667 + 0.006584 + 0.005959 + 0.005209 + 0.005125 + 0.0055 + 0.005375 + 0.005125 + 0.004875 + 0.004084 + 0.004916 + 0.003959 + 0.004125 + 0.004125 + 0.004167 + 0.004541 + 0.005667 + 0.006208 + 0.005125 + 0.004833 + 0.004875 + 0.00525 + 0.005 + 0.005958 + 0.005792 + 0.005125 + 0.004625 + 0.006208 + 0.006333 + 0.006208 + 0.005166 + 0.006375 + 0.006583 + 0.006458 + 0.006625 + 0.006416 + 0.006417 + 0.006334 + 0.006125 + 0.005167 + 0.00475 + 0.004542 + 0.004583 + 0.004042 + 0.00375 + 0.003333 + 0.004083 + 0.003792 + 0.004083 + 0.00325 + 0.004292 + 0.003458 + 0.003959 + 0.003917 + 0.003958 + 0.003958 + 0.004333 + 0.003666 + 0.004083 + 0.003667 + 0.00425 + 0.003958 + 0.003666 + 0.003667 + 0.0035 + 0.003709 + 0.003959 + 0.003625 + 0.003708 + 0.003375 + 0.003875 + 0.003708 + 0.00375 + 0.004 + 0.003667 + 0.003584 + 0.004083 + 0.004125 + 0.004041 + 0.003583 + 0.003792 + 0.004083 + 0.003708 + 0.003792 + 0.003792 + 0.004125 + 0.004541 + 0.004375 + 0.00425 + 0.003958 + 0.003917 + 0.004291 + 0.004 + 0.003791 + 0.005375 + 0.005667 + 0.006042 + 0.005875 + 0.005959 + 0.006084 + 0.005875 + 0.006792 + 0.006208 + 0.006166 + 0.005375 + 0.005166 + 0.005042 + 0.005 + 0.004959 + 0.004875 + 0.004958 + 0.004333 + 0.004042 + 0.004041 + 0.0045 + 0.00425 + 0.004291 + 0.00525 + 0.005708 + 0.005542 + 0.005375 + 0.004917 + 0.00525 + 0.006125 + 0.006083 + 0.005958 + 0.005083 + 0.006083 + 0.00625 + 0.006 + 0.005292 + 0.004625 + 0.006083 + 0.00575 + 0.005833 + 0.007875 + 0.006375 + 0.006083 + 0.005958 + 0.005042 + 0.004709 + 0.00475 + 0.004917 + 0.004334 + 0.004083 + 0.0035 + 0.003708 + 0.004 + 0.003791 + 0.0035 + 0.003666 + 0.004208 + 0.004167 + 0.00375 + 0.003833 + 0.003708 + 0.003833 + 0.003792 + 0.003667 + 0.004125 + 0.004042 + 0.003542 + 0.00425 + 0.003833 + 0.004166 + 0.003417 + 0.003458 + 0.004042 + 0.003416 + 0.003958 + 0.003667 + 0.004125 + 0.003625 + 0.003709 + 0.003625 + 0.00425 + 0.003584 + 0.003458 + 0.004042 + 0.00375 + 0.003583 + 0.003625 + 0.003958 + 0.003791 + 0.004 + 0.004042 + 0.003542 + 0.004167 + 0.004167 + 0.003959 + 0.003875 + 0.004084 + 0.004292 + 0.004125 + 0.005416 + 0.005 + 0.004291 + 0.006333 + 0.00575 + 0.006416 + 0.005958 + 0.006208 + 0.006708 + 0.006458 + 0.005291 + 0.005042 + 0.005291 + 0.004875 + 0.004875 + 0.005333 + 0.005 + 0.004625 + 0.003875 + 0.004333 + 0.00425 + 0.004125 + 0.004083 + 0.004333 + 0.004084 + 0.00425 + 0.004084 + 0.005041 + 0.005833 + 0.006042 + 0.006167 + 0.006 + 0.005917 + 0.00575 + 0.006834 + 0.006125 + 0.006 + 0.005292 + 0.006209 + 0.005959 + 0.006042 + 0.006958 + 0.005959 + 0.006209 + 0.005834 + 0.00525 + 0.005292 + 0.004834 + 0.00475 + 0.00475 + 0.00475 + 0.004459 + 0.004834 + 0.004875 + 0.004417 + 0.004084 + 0.003417 + 0.003833 + 0.003708 + 0.004 + 0.0035 + 0.003666 + 0.003875 + 0.003875 + 0.003958 + 0.003833 + 0.004208 + 0.0045 + 0.004542 + 0.004416 + 0.004209 + 0.00375 + 0.004083 + 0.004292 + 0.003792 + 0.00375 + 0.003334 + 0.003834 + 0.003916 + 0.003708 + 0.004125 + 0.004166 + 0.004292 + 0.004333 + 0.004208 + 0.004666 + 0.004375 + 0.004167 + 0.003792 + 0.003833 + 0.004166 + 0.004125 + 0.004083 + 0.005041 + 0.004917 + 0.004959 + 0.004584 + 0.004667 + 0.004833 + 0.00475 + 0.003875 + 0.004166 + 0.00475 + 0.007916 + 0.005875 + 0.006167 + 0.005834 + 0.0055 + 0.006125 + 0.005833 + 0.00525 + 0.005 + 0.005167 + 0.006166 + 0.005458 + 0.005167 + 0.005125 + 0.005708 + 0.005583 + 0.022042 + 0.009 + 0.004875 + 0.004584 + 0.004459 + 0.004416 + 0.003958 + 0.004208 + 0.004167 + 0.003959 + 0.005083 + 0.009166 + 0.006042 + 0.00575 + 0.005583 + 0.005667 + 0.004417 + 0.005792 + 0.004709 + 0.005584 + 0.006709 + 0.005875 + 0.005708 + 0.005667 + 0.018625 + 0.006041 + 0.005375 + 0.004666 + 0.004375 + 0.00425 + 0.011458 + 0.007958 + 0.011583 + 0.004334 + 0.005125 + 0.003625 + 0.004333 + 0.003708 + 0.004084 + 0.004291 + 0.003625 + 0.004083 + 0.012709 + 0.004167 + 0.003917 + 0.003916 + 0.00425 + 0.004042 + 0.004042 + 0.003792 + 0.004125 + 0.003584 + 0.003917 + 0.004541 + 0.004542 + 0.004709 + 0.004333 + 0.004125 + 0.004041 + 0.003666 + 0.0035 + 0.003958 + 0.003917 + 0.003834 + 0.004292 + 0.0045 + 0.003959 + 0.005208 + 0.01275 + 0.005125 + 0.004708 + 0.00525 + 0.004458 + 0.004292 + 0.004333 + 0.004958 + 0.004917 + 0.004625 + 0.0045 + 0.004416 + 0.004208 + 0.004417 + 0.004625 + 0.003958 + 0.005667 + 0.009292 + 0.0065 + 0.006125 + 0.005958 + 0.006667 + 0.005334 + 0.005542 + 0.0045 + 0.015208 + 0.005417 + 0.005375 + 0.005208 + 0.005792 + 0.005084 + 0.005667 + 0.005291 + 0.005667 + 0.005792 + 0.00525 + 0.005459 + 0.004917 + 0.004542 + 0.004416 + 0.004292 + 0.011709 + 0.004584 + 0.0075 + 0.007834 + 0.006791 + 0.006833 + 0.005125 + 0.006041 + 0.005125 + 0.004333 + 0.005458 + 0.009041 + 0.006916 + 0.0065 + 0.005917 + 0.00675 + 0.006583 + 0.006125 + 0.005584 + 0.005125 + 0.004917 + 0.004959 + 0.005166 + 0.005208 + 0.005167 + 0.005625 + 0.004042 + 0.004333 + 0.00425 + 0.004209 + 0.004125 + 0.004208 + 0.003917 + 0.00375 + 0.003709 + 0.003834 + 0.003459 + 0.00375 + 0.004208 + 0.00375 + 0.003708 + 0.003958 + 0.003708 + 0.003791 + 0.003667 + 0.00375 + 0.004209 + 0.003875 + 0.004167 + 0.004125 + 0.003958 + 0.003833 + 0.003625 + 0.003834 + 0.003834 + 0.00375 + 0.003292 + 0.004292 + 0.003583 + 0.003625 + 0.003958 + 0.003833 + 0.003958 + 0.003708 + 0.003917 + 0.003709 + 0.006 + 0.005125 + 0.005167 + 0.005208 + 0.004666 + 0.004584 + 0.004084 + 0.00425 + 0.003958 + 0.004958 + 0.004625 + 0.004458 + 0.008625 + 0.005584 + 0.005792 + 0.005542 + 0.005542 + 0.00475 + 0.003792 + 0.004208 + 0.004084 + 0.004125 + 0.004084 + 0.004 + 0.004083 + 0.005167 + 0.005041 + 0.004792 + 0.005 + 0.004375 + 0.004291 + 0.004417 + 0.0045 + 0.0085 + 0.004 + 0.004125 + 0.004833 + 0.005167 + 0.005083 + 0.005875 + 0.005709 + 0.005834 + 0.005625 + 0.005 + 0.004583 + 0.004792 + 0.0055 + 0.005917 + 0.005667 + 0.006125 + 0.006167 + 0.005083 + 0.004208 + 0.004125 + 0.003708 + 0.00375 + 0.00375 + 0.004 + 0.003875 + 0.003542 + 0.00425 + 0.003625 + 0.003791 + 0.003542 + 0.004042 + 0.003916 + 0.004125 + 0.003958 + 0.003625 + 0.003791 + 0.003875 + 0.003833 + 0.004708 + 0.004708 + 0.004542 + 0.003916 + 0.004041 + 0.003834 + 0.003916 + 0.004125 + 0.004 + 0.003583 + 0.004083 + 0.003792 + 0.003416 + 0.003708 + 0.003417 + 0.0035 + 0.003375 + 0.003875 + 0.003916 + 0.004167 + 0.00375 + 0.003875 + 0.003708 + 0.00375 + 0.003875 + 0.003916 + 0.003834 + 0.003916 + 0.004375 + 0.003958 + 0.00375 + 0.004125 + 0.010334 + 0.005167 + 0.004791 + 0.004542 + 0.004667 + 0.0045 + 0.005209 + 0.004958 + 0.005167 + 0.00475 + 0.005625 + 0.005667 + 0.005709 + 0.004709 + 0.004916 + 0.00475 + 0.004208 + 0.004292 + 0.004083 + 0.00425 + 0.004208 + 0.003709 + 0.004084 + 0.004 + 0.004291 + 0.004458 + 0.004208 + 0.004042 + 0.004542 + 0.005125 + 0.00475 + 0.004792 + 0.006041 + 0.0055 + 0.005833 + 0.005 + 0.004959 + 0.006 + 0.005292 + 0.005584 + 0.00475 + 0.005208 + 0.005083 + 0.004458 + 0.005458 + 0.005916 + 0.005583 + 0.005625 + 0.004583 + 0.0045 + 0.003792 + 0.004208 + 0.003625 + 0.004041 + 0.004375 + 0.004334 + 0.005417 + 0.003916 + 0.003792 + 0.003625 + 0.003875 + 0.003625 + 0.004084 + 0.00375 + 0.003917 + 0.003541 + 0.004125 + 0.003917 + 0.003667 + 0.003667 + 0.00375 + 0.004208 + 0.004 + 0.003834 + 0.004416 + 0.004 + 0.004125 + 0.004083 + 0.004125 + 0.004 + 0.004167 + 0.003583 + 0.003667 + 0.003875 + 0.00375 + 0.003291 + 0.003625 + 0.003958 + 0.003792 + 0.003959 + 0.003875 + 0.004875 + 0.003791 + 0.00375 + 0.00375 + 0.004166 + 0.004083 + 0.005625 + 0.005042 + 0.004958 + 0.003916 + 0.004084 + 0.003916 + 0.004083 + 0.004083 + 0.004375 + 0.007125 + 0.006958 + 0.0065 + 0.006208 + 0.006458 + 0.006542 + 0.00575 + 0.00525 + 0.004958 + 0.004333 + 0.007875 + 0.005208 + 0.005375 + 0.005208 + 0.005292 + 0.004834 + 0.004875 + 0.004042 + 0.004416 + 0.004625 + 0.004625 + 0.004292 + 0.004583 + 0.004625 + 0.004458 + 0.004208 + 0.003875 + 0.005459 + 0.006042 + 0.006 + 0.005667 + 0.005834 + 0.012334 + 0.00475 + 0.004042 + 0.011417 + 0.005583 + 0.005333 + 0.006 + 0.005375 + 0.005875 + 0.007375 + 0.00625 + 0.005083 + 0.004792 + 0.004917 + 0.005167 + 0.004792 + 0.004666 + 0.003875 + 0.004083 + 0.003875 + 0.0035 + 0.004417 + 0.003833 + 0.003666 + 0.003542 + 0.003958 + 0.004041 + 0.003625 + 0.003708 + 0.003833 + 0.00425 + 0.003791 + 0.004084 + 0.003833 + 0.003708 + 0.0035 + 0.003709 + 0.004041 + 0.003917 + 0.011209 + 0.004041 + 0.003667 + 0.004041 + 0.003625 + 0.003834 + 0.003709 + 0.00375 + 0.003667 + 0.003958 + 0.003542 + 0.003666 + 0.0035 + 0.0035 + 0.003875 + 0.003792 + 0.004125 + 0.00375 + 0.003833 + 0.003791 + 0.003833 + 0.004042 + 0.004208 + 0.004125 + 0.004 + 0.00375 + 0.00425 + 0.004208 + 0.004375 + 0.004666 + 0.005 + 0.009208 + 0.006042 + 0.005625 + 0.0055 + 0.004791 + 0.004333 + 0.004042 + 0.004209 + 0.00375 + 0.003834 + 0.00375 + 0.005 + 0.004875 + 0.004792 + 0.004917 + 0.005125 + 0.005167 + 0.004791 + 0.004292 + 0.003959 + 0.004417 + 0.004041 + 0.004417 + 0.004083 + 0.00425 + 0.005084 + 0.005292 + 0.005791 + 0.00575 + 0.005666 + 0.00575 + 0.005417 + 0.004625 + 0.005542 + 0.005334 + 0.006375 + 0.006125 + 0.007333 + 0.006042 + 0.005542 + 0.006166 + 0.005125 + 0.005333 + 0.005292 + 0.004833 + 0.004833 + 0.00525 + 0.005708 + 0.005416 + 0.006292 + 0.005833 + 0.004583 + 0.004625 + 0.004375 + 0.004167 + 0.004666 + 0.004709 + 0.004417 + 0.004375 + 0.00475 + 0.005166 + 0.005292 + 0.005708 + 0.005916 + 0.005417 + 0.004875 + 0.004333 + 0.004458 + 0.004292 + 0.004375 + 0.004625 + 0.004583 + 0.004667 + 0.003959 + 0.00475 + 0.004459 + 0.004166 + 0.003625 + 0.003791 + 0.004208 + 0.003542 + 0.003542 + 0.004916 + 0.005 + 0.004916 + 0.005042 + 0.005208 + 0.0055 + 0.005333 + 0.00625 + 0.005917 + 0.006042 + 0.006292 + 0.005209 + 0.005708 + 0.005 + 0.004042 + 0.003959 + 0.005084 + 0.004584 + 0.004417 + 0.004958 + 0.006625 + 0.00675 + 0.006125 + 0.006167 + 0.005416 + 0.00425 + 0.004166 + 0.004208 + 0.004417 + 0.004125 + 0.003792 + 0.003708 + 0.005666 + 0.005708 + 0.005375 + 0.005667 + 0.006083 + 0.004458 + 0.004458 + 0.005458 + 0.005167 + 0.005125 + 0.005292 + 0.006084 + 0.006 + 0.006917 + 0.008209 + 0.00675 + 0.006666 + 0.006458 + 0.006667 + 0.005167 + 0.005958 + 0.006208 + 0.006959 + 0.006459 + 0.005917 + 0.005333 + 0.005125 + 0.004833 + 0.004708 + 0.004875 + 0.005916 + 0.00575 + 0.004666 + 0.004917 + 0.005417 + 0.004792 + 0.005291 + 0.004708 + 0.005375 + 0.005333 + 0.004791 + 0.012083 + 0.005084 + 0.004458 + 0.005291 + 0.004833 + 0.004833 + 0.004458 + 0.005 + 0.004958 + 0.004958 + 0.00525 + 0.0045 + 0.005125 + 0.004709 + 0.0065 + 0.0075 + 0.007541 + 0.014916 + 0.006125 + 0.00575 + 0.006375 + 0.006916 + 0.005542 + 0.006458 + 0.005458 + 0.005166 + 0.005209 + 0.004209 + 0.004542 + 0.00425 + 0.003875 + 0.004209 + 0.004708 + 0.005042 + 0.003917 + 0.005417 + 0.005 + 0.004959 + 0.005459 + 0.008958 + 0.006791 + 0.006083 + 0.006417 + 0.005625 + 0.006125 + 0.005041 + 0.004917 + 0.004791 + 0.006 + 0.006 + 0.005667 + 0.012375 + 0.004666 + 0.004334 + 0.003917 + 0.004208 + 0.004666 + 0.00475 + 0.00475 + 0.004792 + 0.00425 + 0.005209 + 0.00425 + 0.004459 + 0.004625 + 0.005375 + 0.005 + 0.005375 + 0.004959 + 0.005542 + 0.006375 + 0.00675 + 0.005375 + 0.005292 + 0.006209 + 0.006792 + 0.006167 + 0.005875 + 0.00475 + 0.005917 + 0.004875 + 0.006042 + 0.006167 + 0.005333 + 0.005875 + 0.00525 + 0.004834 + 0.004 + 0.003959 + 0.003917 + 0.004125 + 0.00375 + 0.003875 + 0.005292 + 0.005125 + 0.004375 + 0.004042 + 0.003916 + 0.004083 + 0.00425 + 0.003625 + 0.00375 + 0.003667 + 0.003666 + 0.004292 + 0.00475 + 0.003625 + 0.004084 + 0.003666 + 0.0035 + 0.003916 + 0.003917 + 0.004042 + 0.003958 + 0.003792 + 0.004 + 0.003875 + 0.003959 + 0.00375 + 0.004209 + 0.004291 + 0.0045 + 0.00375 + 0.003875 + 0.004042 + 0.004125 + 0.003834 + 0.00375 + 0.004042 + 0.003625 + 0.003708 + 0.00375 + 0.003708 + 0.004 + 0.004417 + 0.004792 + 0.004333 + 0.004458 + 0.004667 + 0.00425 + 0.004417 + 0.005916 + 0.005708 + 0.005583 + 0.006875 + 0.006 + 0.006666 + 0.006583 + 0.005958 + 0.006958 + 0.006917 + 0.005167 + 0.005 + 0.005459 + 0.005167 + 0.004875 + 0.005708 + 0.005458 + 0.004375 + 0.00425 + 0.004333 + 0.003333 + 0.004208 + 0.004667 + 0.004083 + 0.004208 + 0.003917 + 0.004084 + 0.004417 + 0.004375 + 0.004208 + 0.007125 + 0.006625 + 0.006167 + 0.006208 + 0.006042 + 0.005375 + 0.006417 + 0.005459 + 0.006375 + 0.005083 + 0.00525 + 0.004958 + 0.005083 + 0.0065 + 0.006416 + 0.006042 + 0.005334 + 0.005791 + 0.005541 + 0.005042 + 0.004959 + 0.004667 + 0.004833 + 0.004459 + 0.003708 + 0.004416 + 0.004042 + 0.004458 + 0.003834 + 0.004125 + 0.003792 + 0.003792 + 0.003583 + 0.004208 + 0.00375 + 0.00375 + 0.00425 + 0.003708 + 0.004125 + 0.004083 + 0.003583 + 0.004041 + 0.003792 + 0.004 + 0.004166 + 0.003875 + 0.003833 + 0.003875 + 0.00425 + 0.003667 + 0.003958 + 0.004333 + 0.004417 + 0.004208 + 0.004292 + 0.00375 + 0.003959 + 0.004084 + 0.004167 + 0.003958 + 0.003708 + 0.00375 + 0.0035 + 0.004167 + 0.003834 + 0.00425 + 0.004458 + 0.003958 + 0.004083 + 0.004167 + 0.004084 + 0.004042 + 0.003958 + 0.005125 + 0.01025 + 0.006792 + 0.006166 + 0.006292 + 0.00675 + 0.006125 + 0.007334 + 0.005459 + 0.005458 + 0.005416 + 0.005375 + 0.0055 + 0.005542 + 0.00525 + 0.005042 + 0.005125 + 0.005084 + 0.003958 + 0.004083 + 0.004 + 0.004291 + 0.003958 + 0.004458 + 0.0045 + 0.004125 + 0.004042 + 0.007208 + 0.0065 + 0.006125 + 0.006208 + 0.006375 + 0.005958 + 0.006166 + 0.00525 + 0.006083 + 0.006458 + 0.005959 + 0.006292 + 0.006583 + 0.005666 + 0.006958 + 0.0065 + 0.004791 + 0.004792 + 0.004625 + 0.004792 + 0.004709 + 0.004917 + 0.004666 + 0.005542 + 0.004667 + 0.004125 + 0.003958 + 0.004125 + 0.003833 + 0.004083 + 0.004 + 0.003833 + 0.00375 + 0.003875 + 0.003834 + 0.003625 + 0.003792 + 0.004 + 0.003917 + 0.00375 + 0.003958 + 0.003917 + 0.003834 + 0.003625 + 0.00375 + 0.003666 + 0.004292 + 0.003792 + 0.003833 + 0.003833 + 0.003958 + 0.003833 + 0.003958 + 0.004042 + 0.003959 + 0.004041 + 0.003834 + 0.004042 + 0.004167 + 0.003458 + 0.003625 + 0.00375 + 0.003583 + 0.003542 + 0.004292 + 0.003917 + 0.004167 + 0.003917 + 0.003833 + 0.004083 + 0.004166 + 0.004125 + 0.004125 + 0.005042 + 0.006041 + 0.006583 + 0.007417 + 0.00725 + 0.00625 + 0.006875 + 0.0075 + 0.005542 + 0.004875 + 0.007834 + 0.005667 + 0.0055 + 0.004917 + 0.004542 + 0.004417 + 0.00425 + 0.004583 + 0.00425 + 0.003958 + 0.004083 + 0.004167 + 0.003833 + 0.004375 + 0.004084 + 0.003875 + 0.004417 + 0.007 + 0.013625 + 0.006375 + 0.005792 + 0.005375 + 0.005417 + 0.006833 + 0.00625 + 0.005333 + 0.0065 + 0.006541 + 0.00625 + 0.006291 + 0.007583 + 0.005833 + 0.013333 + 0.005083 + 0.00425 + 0.004625 + 0.00475 + 0.004541 + 0.004375 + 0.003666 + 0.004375 + 0.0045 + 0.004167 + 0.003583 + 0.003375 + 0.004083 + 0.003958 + 0.004 + 0.004291 + 0.004042 + 0.003542 + 0.003834 + 0.003708 + 0.003875 + 0.003583 + 0.003833 + 0.003583 + 0.004 + 0.003709 + 0.003834 + 0.003792 + 0.004 + 0.00375 + 0.004209 + 0.003958 + 0.003417 + 0.003666 + 0.00375 + 0.003625 + 0.004 + 0.003625 + 0.003875 + 0.003708 + 0.003667 + 0.003666 + 0.003959 + 0.003833 + 0.003833 + 0.003625 + 0.0035 + 0.00375 + 0.003959 + 0.003958 + 0.00425 + 0.003875 + 0.004 + 0.003791 + 0.00375 + 0.004125 + 0.004209 + 0.004417 + 0.004625 + 0.006333 + 0.005875 + 0.00575 + 0.005708 + 0.00575 + 0.005541 + 0.003958 + 0.003875 + 0.0045 + 0.005 + 0.004958 + 0.005166 + 0.005 + 0.004875 + 0.005125 + 0.004209 + 0.004042 + 0.00375 + 0.004 + 0.003792 + 0.003709 + 0.004084 + 0.003625 + 0.004208 + 0.00425 + 0.005167 + 0.005541 + 0.005833 + 0.00575 + 0.005792 + 0.005709 + 0.015459 + 0.006209 + 0.005166 + 0.005833 + 0.005708 + 0.0045 + 0.004833 + 0.004458 + 0.009042 + 0.0065 + 0.004792 + 0.004917 + 0.003083 + 0.003542 + 0.003625 + 0.003792 + 0.003667 + 0.003917 + 0.004125 + 0.003125 + 0.003417 + 0.003416 + 0.003375 + 0.00325 + 0.003792 + 0.00375 + 0.003084 + 0.003083 + 0.003166 + 0.003333 + 0.003625 + 0.003833 + 0.0035 + 0.003667 + 0.00375 + 0.003625 + 0.00475 + 0.003917 + 0.004209 + 0.003416 + 0.003334 + 0.003166 + 0.003125 + 0.003292 + 0.003292 + 0.003542 + 0.003 + 0.003375 + 0.003417 + 0.003458 + 0.003333 + 0.0035 + 0.003708 + 0.003167 + 0.003208 + 0.002958 + 0.003292 + 0.003167 + 0.003416 + 0.003875 + 0.005041 + 0.004833 + 0.004417 + 0.004166 + 0.004167 + 0.00425 + 0.00475 + 0.003709 + 0.004667 + 0.004416 + 0.00425 + 0.005583 + 0.005167 + 0.005375 + 0.005958 + 0.004375 + 0.0045 + 0.00375 + 0.003459 + 0.004333 + 0.003708 + 0.004083 + 0.004083 + 0.003458 + 0.003709 + 0.003625 + 0.003458 + 0.003667 + 0.003708 + 0.004458 + 0.004333 + 0.004333 + 0.0045 + 0.004 + 0.004625 + 0.005667 + 0.005292 + 0.004667 + 0.004375 + 0.005125 + 0.005416 + 0.00525 + 0.004958 + 0.005625 + 0.005417 + 0.004459 + 0.00525 + 0.006375 + 0.0065 + 0.005834 + 0.006208 + 0.005334 + 0.005 + 0.004417 + 0.003917 + 0.004541 + 0.004167 + 0.004125 + 0.004625 + 0.004166 + 0.004042 + 0.003917 + 0.004 + 0.0045 + 0.004459 + 0.004375 + 0.003709 + 0.00375 + 0.003667 + 0.003875 + 0.003666 + 0.004125 + 0.004125 + 0.00425 + 0.003709 + 0.01175 + 0.003834 + 0.003584 + 0.004084 + 0.003875 + 0.004167 + 0.003916 + 0.004417 + 0.004917 + 0.004833 + 0.004333 + 0.004792 + 0.005167 + 0.00525 + 0.004375 + 0.004833 + 0.004666 + 0.004833 + 0.004708 + 0.004875 + 0.004709 + 0.004916 + 0.00425 + 0.004083 + 0.0045 + 0.004166 + 0.004084 + 0.004459 + 0.004083 + 0.004083 + 0.005542 + 0.004666 + 0.004667 + 0.005959 + 0.005875 + 0.005792 + 0.004875 + 0.005 + 0.006334 + 0.0065 + 0.005875 + 0.017583 + 0.004375 + 0.0045 + 0.004334 + 0.004041 + 0.004167 + 0.0045 + 0.00425 + 0.004291 + 0.004625 + 0.004208 + 0.004333 + 0.005958 + 0.00525 + 0.004958 + 0.005 + 0.004583 + 0.005167 + 0.004167 + 0.004541 + 0.005167 + 0.005166 + 0.00525 + 0.00625 + 0.006292 + 0.005625 + 0.004958 + 0.005083 + 0.005834 + 0.004958 + 0.005291 + 0.006667 + 0.006583 + 0.005833 + 0.006083 + 0.004833 + 0.004042 + 0.003959 + 0.003959 + 0.003459 + 0.00425 + 0.004083 + 0.004125 + 0.005375 + 0.003458 + 0.003625 + 0.004666 + 0.004209 + 0.003792 + 0.004083 + 0.003875 + 0.003917 + 0.007917 + 0.005459 + 0.005291 + 0.005667 + 0.00575 + 0.005292 + 0.005 + 0.00475 + 0.004833 + 0.005083 + 0.004375 + 0.004458 + 0.004792 + 0.004959 + 0.005042 + 0.004458 + 0.005042 + 0.004416 + 0.004875 + 0.004708 + 0.005375 + 0.005084 + 0.00475 + 0.0045 + 0.005667 + 0.005792 + 0.005416 + 0.005625 + 0.005 + 0.004042 + 0.004375 + 0.005291 + 0.005208 + 0.004708 + 0.004708 + 0.004917 + 0.004792 + 0.004666 + 0.00475 + 0.004375 + 0.009042 + 0.006042 + 0.005875 + 0.006291 + 0.006042 + 0.006542 + 0.007708 + 0.007208 + 0.005125 + 0.005166 + 0.005792 + 0.005292 + 0.006084 + 0.005458 + 0.006208 + 0.006333 + 0.005958 + 0.006292 + 0.004708 + 0.004875 + 0.005041 + 0.004584 + 0.004167 + 0.004291 + 0.0055 + 0.005625 + 0.005666 + 0.006334 + 0.006083 + 0.00625 + 0.006875 + 0.00625 + 0.0065 + 0.00625 + 0.006583 + 0.005416 + 0.006583 + 0.005792 + 0.00575 + 0.006583 + 0.005666 + 0.006042 + 0.006084 + 0.00525 + 0.004584 + 0.005167 + 0.004833 + 0.005416 + 0.005125 + 0.005541 + 0.005375 + 0.006417 + 0.005709 + 0.005042 + 0.00425 + 0.004709 + 0.004625 + 0.004625 + 0.004916 + 0.004583 + 0.004209 + 0.004792 + 0.004166 + 0.0045 + 0.004708 + 0.004417 + 0.004792 + 0.004791 + 0.004 + 0.006541 + 0.004834 + 0.004209 + 0.003791 + 0.007958 + 0.005458 + 0.005375 + 0.005166 + 0.004917 + 0.005333 + 0.004041 + 0.004542 + 0.004292 + 0.004791 + 0.007708 + 0.007375 + 0.009333 + 0.008291 + 0.008167 + 0.008333 + 0.00725 + 0.006042 + 0.008125 + 0.006416 + 0.005875 + 0.006291 + 0.005875 + 0.005334 + 0.005708 + 0.005625 + 0.0055 + 0.00525 + 0.010583 + 0.008125 + 0.007792 + 0.007125 + 0.007417 + 0.007542 + 0.009292 + 0.005125 + 0.004167 + 0.004583 + 0.004625 + 0.004083 + 0.004416 + 0.0045 + 0.004208 + 0.012042 + 0.004625 + 0.004083 + 0.004167 + 0.004041 + 0.004917 + 0.004833 + 0.005291 + 0.005042 + 0.004709 + 0.004542 + 0.006708 + 0.006375 + 0.005625 + 0.005834 + 0.005834 + 0.008334 + 0.005166 + 0.0065 + 0.005167 + 0.005792 + 0.005791 + 0.006292 + 0.005667 + 0.014167 + 0.006667 + 0.006166 + 0.005 + 0.005 + 0.0045 + 0.004792 + 0.004917 + 0.004667 + 0.004625 + 0.005416 + 0.006125 + 0.004916 + 0.004417 + 0.005375 + 0.005625 + 0.005042 + 0.005 + 0.004958 + 0.004667 + 0.004167 + 0.004083 + 0.003916 + 0.00425 + 0.004292 + 0.004458 + 0.004292 + 0.004333 + 0.004125 + 0.003583 + 0.003958 + 0.004 + 0.004083 + 0.004334 + 0.004167 + 0.0045 + 0.004292 + 0.004042 + 0.003834 + 0.00375 + 0.00425 + 0.004084 + 0.004125 + 0.004 + 0.004292 + 0.004292 + 0.004083 + 0.003833 + 0.004125 + 0.003833 + 0.0045 + 0.004416 + 0.005083 + 0.004334 + 0.004041 + 0.003917 + 0.004917 + 0.003833 + 0.00425 + 0.004042 + 0.004167 + 0.005 + 0.005208 + 0.006042 + 0.006792 + 0.006458 + 0.006208 + 0.006375 + 0.005083 + 0.005291 + 0.006333 + 0.004917 + 0.004417 + 0.004291 + 0.004375 + 0.004708 + 0.004166 + 0.0055 + 0.005792 + 0.005667 + 0.005875 + 0.0055 + 0.005292 + 0.005542 + 0.005375 + 0.005209 + 0.004292 + 0.004666 + 0.005625 + 0.004541 + 0.004959 + 0.008834 + 0.006084 + 0.006 + 0.006125 + 0.005291 + 0.006042 + 0.006167 + 0.007375 + 0.00625 + 0.006417 + 0.006709 + 0.006208 + 0.005833 + 0.005333 + 0.00525 + 0.005208 + 0.004125 + 0.003792 + 0.003875 + 0.004583 + 0.005416 + 0.005084 + 0.005209 + 0.004833 + 0.005333 + 0.005084 + 0.005125 + 0.005 + 0.004458 + 0.004 + 0.004208 + 0.004625 + 0.004042 + 0.003542 + 0.004375 + 0.004042 + 0.00375 + 0.004375 + 0.004667 + 0.004666 + 0.003875 + 0.003917 + 0.003792 + 0.003875 + 0.004167 + 0.003875 + 0.003834 + 0.003959 + 0.003958 + 0.003666 + 0.004209 + 0.004 + 0.004292 + 0.003958 + 0.00425 + 0.004333 + 0.003916 + 0.003958 + 0.003667 + 0.003958 + 0.004333 + 0.004458 + 0.006458 + 0.005708 + 0.00525 + 0.005334 + 0.005167 + 0.0055 + 0.005 + 0.005458 + 0.006042 + 0.005209 + 0.004958 + 0.006541 + 0.006083 + 0.006 + 0.006333 + 0.006333 + 0.005416 + 0.005083 + 0.005125 + 0.005125 + 0.004 + 0.00425 + 0.004041 + 0.004334 + 0.010041 + 0.00575 + 0.005708 + 0.005459 + 0.005209 + 0.005375 + 0.005417 + 0.005625 + 0.005542 + 0.004458 + 0.004125 + 0.005208 + 0.005167 + 0.004917 + 0.006667 + 0.006167 + 0.006208 + 0.006041 + 0.00525 + 0.004708 + 0.006583 + 0.006625 + 0.006708 + 0.006875 + 0.00625 + 0.006375 + 0.006125 + 0.005417 + 0.004958 + 0.00525 + 0.005167 + 0.0045 + 0.003791 + 0.004042 + 0.004292 + 0.004041 + 0.003708 + 0.004 + 0.004583 + 0.003959 + 0.003875 + 0.003958 + 0.004041 + 0.003958 + 0.004083 + 0.004 + 0.004125 + 0.004166 + 0.00425 + 0.004166 + 0.004125 + 0.003959 + 0.004125 + 0.003958 + 0.004208 + 0.0045 + 0.003458 + 0.003709 + 0.003792 + 0.003792 + 0.004291 + 0.003375 + 0.004041 + 0.004125 + 0.003834 + 0.004167 + 0.003875 + 0.004 + 0.003542 + 0.003625 + 0.003959 + 0.004125 + 0.003834 + 0.003875 + 0.004 + 0.004709 + 0.004125 + 0.004166 + 0.004208 + 0.006 + 0.005417 + 0.005375 + 0.00525 + 0.005333 + 0.005667 + 0.006167 + 0.006416 + 0.005459 + 0.006584 + 0.006334 + 0.006209 + 0.005791 + 0.006375 + 0.013416 + 0.00575 + 0.004083 + 0.004333 + 0.004292 + 0.004958 + 0.00525 + 0.005375 + 0.005875 + 0.00875 + 0.006 + 0.006041 + 0.005875 + 0.00525 + 0.015667 + 0.0055 + 0.005459 + 0.004541 + 0.005042 + 0.005125 + 0.004958 + 0.016958 + 0.006208 + 0.0045 + 0.004834 + 0.004667 + 0.004625 + 0.00625 + 0.007958 + 0.006417 + 0.006291 + 0.006042 + 0.007208 + 0.005958 + 0.004666 + 0.00475 + 0.004917 + 0.004041 + 0.004083 + 0.003833 + 0.004125 + 0.004 + 0.005375 + 0.004542 + 0.003583 + 0.004333 + 0.005208 + 0.005333 + 0.005125 + 0.005083 + 0.00475 + 0.00425 + 0.0045 + 0.004292 + 0.004458 + 0.004541 + 0.004958 + 0.004167 + 0.004083 + 0.004375 + 0.004208 + 0.004083 + 0.003917 + 0.003834 + 0.004333 + 0.003458 + 0.003542 + 0.003791 + 0.003458 + 0.003625 + 0.004 + 0.003541 + 0.004 + 0.003667 + 0.00375 + 0.004375 + 0.004042 + 0.003916 + 0.003834 + 0.003792 + 0.003708 + 0.003708 + 0.003875 + 0.004083 + 0.0045 + 0.004375 + 0.007833 + 0.005 + 0.004875 + 0.004709 + 0.005041 + 0.004833 + 0.005417 + 0.0045 + 0.004792 + 0.004625 + 0.005667 + 0.006125 + 0.006083 + 0.004958 + 0.004958 + 0.005167 + 0.004334 + 0.004125 + 0.004042 + 0.004458 + 0.004166 + 0.004167 + 0.004 + 0.004291 + 0.0045 + 0.004 + 0.004083 + 0.005791 + 0.005083 + 0.005084 + 0.004667 + 0.004916 + 0.005208 + 0.0055 + 0.00475 + 0.004959 + 0.009 + 0.005625 + 0.005375 + 0.005417 + 0.004792 + 0.005334 + 0.004917 + 0.004791 + 0.00575 + 0.005791 + 0.014625 + 0.005625 + 0.004875 + 0.004375 + 0.004708 + 0.00475 + 0.003958 + 0.00375 + 0.004292 + 0.004125 + 0.004417 + 0.003208 + 0.004083 + 0.004333 + 0.004042 + 0.003959 + 0.004292 + 0.005125 + 0.005084 + 0.004834 + 0.004875 + 0.005042 + 0.005167 + 0.004208 + 0.004416 + 0.004333 + 0.004709 + 0.007084 + 0.004084 + 0.004167 + 0.004333 + 0.00425 + 0.00425 + 0.004208 + 0.004208 + 0.003709 + 0.00375 + 0.003542 + 0.00375 + 0.003709 + 0.003709 + 0.003916 + 0.004 + 0.003666 + 0.003875 + 0.004209 + 0.003917 + 0.003916 + 0.003625 + 0.003958 + 0.003833 + 0.004292 + 0.004209 + 0.004209 + 0.004959 + 0.005125 + 0.005833 + 0.004958 + 0.004917 + 0.004084 + 0.00525 + 0.004916 + 0.00475 + 0.004708 + 0.0045 + 0.006459 + 0.005791 + 0.004958 + 0.004625 + 0.004833 + 0.004833 + 0.004125 + 0.003875 + 0.004333 + 0.004083 + 0.004125 + 0.004 + 0.004416 + 0.003917 + 0.004042 + 0.005125 + 0.004916 + 0.004917 + 0.005208 + 0.005333 + 0.004792 + 0.004167 + 0.005125 + 0.004584 + 0.005125 + 0.004792 + 0.006292 + 0.006042 + 0.005166 + 0.005208 + 0.005584 + 0.00525 + 0.004708 + 0.00575 + 0.00575 + 0.005791 + 0.005292 + 0.005583 + 0.004666 + 0.004125 + 0.00375 + 0.00375 + 0.00375 + 0.003792 + 0.011083 + 0.004458 + 0.003917 + 0.003542 + 0.004 + 0.003875 + 0.003667 + 0.004083 + 0.004625 + 0.004208 + 0.004083 + 0.004916 + 0.005333 + 0.004792 + 0.004792 + 0.00475 + 0.004916 + 0.004833 + 0.005 + 0.005042 + 0.004709 + 0.004667 + 0.004709 + 0.004667 + 0.003958 + 0.003541 + 0.003917 + 0.003625 + 0.003542 + 0.003625 + 0.00375 + 0.003666 + 0.003667 + 0.003625 + 0.003833 + 0.00425 + 0.004708 + 0.004792 + 0.004917 + 0.004625 + 0.004584 + 0.005417 + 0.005291 + 0.005125 + 0.005167 + 0.004667 + 0.004125 + 0.00375 + 0.003875 + 0.006042 + 0.00475 + 0.005958 + 0.00625 + 0.0065 + 0.006 + 0.006333 + 0.007333 + 0.00625 + 0.005834 + 0.004542 + 0.004041 + 0.004292 + 0.003834 + 0.003875 + 0.00425 + 0.00425 + 0.004125 + 0.004209 + 0.00475 + 0.004417 + 0.004166 + 0.004 + 0.004542 + 0.004458 + 0.006625 + 0.005375 + 0.005417 + 0.005083 + 0.006375 + 0.006541 + 0.006417 + 0.006209 + 0.005667 + 0.006625 + 0.006792 + 0.005292 + 0.005083 + 0.006416 + 0.006167 + 0.005666 + 0.006 + 0.005666 + 0.006375 + 0.006125 + 0.005291 + 0.004834 + 0.005084 + 0.004834 + 0.0045 + 0.004958 + 0.004875 + 0.004375 + 0.00375 + 0.003334 + 0.003708 + 0.003583 + 0.004292 + 0.003833 + 0.004041 + 0.003958 + 0.003875 + 0.003791 + 0.004125 + 0.004 + 0.004 + 0.004 + 0.004083 + 0.003875 + 0.004 + 0.003958 + 0.003792 + 0.003959 + 0.004209 + 0.004375 + 0.0045 + 0.003792 + 0.004083 + 0.003833 + 0.004041 + 0.003667 + 0.003584 + 0.003833 + 0.0035 + 0.004125 + 0.004625 + 0.004667 + 0.00475 + 0.004792 + 0.004667 + 0.005084 + 0.005125 + 0.004833 + 0.005333 + 0.005042 + 0.004291 + 0.004167 + 0.004875 + 0.00475 + 0.004625 + 0.004125 + 0.00425 + 0.004292 + 0.005167 + 0.007125 + 0.006416 + 0.006167 + 0.006459 + 0.006459 + 0.00575 + 0.005375 + 0.00525 + 0.00475 + 0.003917 + 0.003875 + 0.004125 + 0.005334 + 0.005625 + 0.005292 + 0.0055 + 0.005083 + 0.005583 + 0.005375 + 0.00525 + 0.00525 + 0.004583 + 0.003917 + 0.004375 + 0.004375 + 0.004833 + 0.004791 + 0.006709 + 0.006458 + 0.006459 + 0.006042 + 0.006083 + 0.006375 + 0.005334 + 0.007417 + 0.006333 + 0.005833 + 0.006166 + 0.005958 + 0.005833 + 0.006625 + 0.004958 + 0.004375 + 0.004042 + 0.004041 + 0.003917 + 0.003833 + 0.004042 + 0.00375 + 0.004625 + 0.003875 + 0.003625 + 0.003916 + 0.004 + 0.003834 + 0.003666 + 0.003542 + 0.00375 + 0.004042 + 0.003875 + 0.003875 + 0.004166 + 0.004167 + 0.004041 + 0.003625 + 0.003917 + 0.003708 + 0.004292 + 0.004 + 0.0045 + 0.003792 + 0.004041 + 0.003917 + 0.004084 + 0.003791 + 0.004042 + 0.003875 + 0.004541 + 0.0035 + 0.003791 + 0.004042 + 0.004583 + 0.00475 + 0.004791 + 0.004875 + 0.004875 + 0.004917 + 0.004791 + 0.004833 + 0.005167 + 0.0045 + 0.004042 + 0.004292 + 0.004 + 0.003792 + 0.004208 + 0.004208 + 0.003708 + 0.004083 + 0.005208 + 0.005333 + 0.006292 + 0.006625 + 0.005959 + 0.006542 + 0.006458 + 0.005125 + 0.004959 + 0.00525 + 0.004625 + 0.004125 + 0.004458 + 0.00425 + 0.003875 + 0.004083 + 0.003958 + 0.005292 + 0.005375 + 0.005292 + 0.005667 + 0.005375 + 0.005417 + 0.005416 + 0.005625 + 0.00525 + 0.004375 + 0.005292 + 0.004958 + 0.004792 + 0.006834 + 0.007042 + 0.00675 + 0.006584 + 0.005041 + 0.006708 + 0.006792 + 0.007542 + 0.006666 + 0.005958 + 0.005958 + 0.005792 + 0.006459 + 0.005959 + 0.004959 + 0.004541 + 0.005167 + 0.011833 + 0.007666 + 0.004417 + 0.005291 + 0.004083 + 0.008833 + 0.005208 + 0.005875 + 0.017833 + 0.013 + 0.005833 + 0.0055 + 0.00575 + 0.011666 + 0.004375 + 0.00375 + 0.004459 + 0.005458 + 0.0045 + 0.004625 + 0.005209 + 0.004583 + 0.004291 + 0.005125 + 0.005125 + 0.004583 + 0.004792 + 0.01225 + 0.004917 + 0.004834 + 0.004583 + 0.004416 + 0.004584 + 0.004458 + 0.00425 + 0.004208 + 0.012125 + 0.004125 + 0.004958 + 0.004167 + 0.004875 + 0.005417 + 0.004959 + 0.004459 + 0.00625 + 0.004667 + 0.007167 + 0.007 + 0.005917 + 0.005541 + 0.005417 + 0.005916 + 0.005792 + 0.008541 + 0.007042 + 0.006 + 0.006834 + 0.005959 + 0.007083 + 0.005916 + 0.008792 + 0.012375 + 0.004709 + 0.004791 + 0.004292 + 0.004209 + 0.006125 + 0.005834 + 0.005041 + 0.005125 + 0.005583 + 0.005041 + 0.004333 + 0.00475 + 0.004209 + 0.004542 + 0.004041 + 0.004417 + 0.004584 + 0.00475 + 0.005459 + 0.005584 + 0.008959 + 0.006292 + 0.0055 + 0.005959 + 0.005959 + 0.004417 + 0.005166 + 0.005292 + 0.006167 + 0.0065 + 0.00625 + 0.00575 + 0.005708 + 0.005458 + 0.004416 + 0.003875 + 0.004166 + 0.008542 + 0.004083 + 0.0045 + 0.004125 + 0.004333 + 0.004208 + 0.004 + 0.0035 + 0.004208 + 0.004416 + 0.004042 + 0.004042 + 0.004458 + 0.00375 + 0.00375 + 0.004459 + 0.004875 + 0.004875 + 0.004167 + 0.004042 + 0.004625 + 0.00375 + 0.004291 + 0.004125 + 0.003917 + 0.00425 + 0.003708 + 0.004125 + 0.004209 + 0.003792 + 0.004166 + 0.004042 + 0.004083 + 0.004083 + 0.003833 + 0.004375 + 0.004083 + 0.004292 + 0.004458 + 0.004916 + 0.005 + 0.005083 + 0.0055 + 0.00525 + 0.004875 + 0.005417 + 0.00525 + 0.006417 + 0.004917 + 0.004792 + 0.00525 + 0.004916 + 0.005167 + 0.00475 + 0.00525 + 0.005083 + 0.005167 + 0.006666 + 0.006375 + 0.006333 + 0.006125 + 0.006375 + 0.004625 + 0.004167 + 0.004292 + 0.004542 + 0.004167 + 0.003958 + 0.005834 + 0.004959 + 0.004708 + 0.0055 + 0.004667 + 0.00475 + 0.004625 + 0.004208 + 0.004333 + 0.00425 + 0.004125 + 0.004166 + 0.004125 + 0.004708 + 0.005792 + 0.006333 + 0.006333 + 0.005875 + 0.005959 + 0.005875 + 0.005292 + 0.004125 + 0.005125 + 0.005833 + 0.006125 + 0.006166 + 0.005833 + 0.013375 + 0.004791 + 0.004208 + 0.004292 + 0.004041 + 0.003958 + 0.008167 + 0.003959 + 0.00375 + 0.004042 + 0.004458 + 0.003833 + 0.003709 + 0.004 + 0.004333 + 0.003958 + 0.004 + 0.003708 + 0.003375 + 0.003791 + 0.00375 + 0.003792 + 0.00425 + 0.004042 + 0.003834 + 0.003959 + 0.003834 + 0.003708 + 0.003791 + 0.004292 + 0.003792 + 0.003875 + 0.003834 + 0.003792 + 0.00425 + 0.003458 + 0.003625 + 0.004167 + 0.003959 + 0.00375 + 0.004042 + 0.004041 + 0.003834 + 0.003834 + 0.003667 + 0.003791 + 0.003542 + 0.003959 + 0.003916 + 0.003542 + 0.003458 + 0.004042 + 0.005375 + 0.005375 + 0.004917 + 0.004958 + 0.004792 + 0.004791 + 0.0045 + 0.004083 + 0.005709 + 0.00475 + 0.004584 + 0.005667 + 0.00625 + 0.005834 + 0.006 + 0.004875 + 0.0045 + 0.004125 + 0.004125 + 0.00425 + 0.004083 + 0.003875 + 0.003833 + 0.003833 + 0.004167 + 0.003709 + 0.004 + 0.004958 + 0.004833 + 0.004625 + 0.004709 + 0.004875 + 0.004959 + 0.004792 + 0.005042 + 0.0055 + 0.005042 + 0.005 + 0.005875 + 0.006125 + 0.005542 + 0.005833 + 0.00475 + 0.005709 + 0.005125 + 0.006375 + 0.006208 + 0.006166 + 0.00575 + 0.005541 + 0.004292 + 0.003917 + 0.004041 + 0.003833 + 0.004042 + 0.003916 + 0.004292 + 0.004458 + 0.004958 + 0.004333 + 0.00325 + 0.003917 + 0.004291 + 0.00375 + 0.004041 + 0.004292 + 0.003708 + 0.003833 + 0.003917 + 0.003875 + 0.003541 + 0.003792 + 0.004083 + 0.004208 + 0.004334 + 0.003875 + 0.003875 + 0.004291 + 0.00425 + 0.004167 + 0.004208 + 0.004125 + 0.00375 + 0.003917 + 0.003959 + 0.004458 + 0.004 + 0.004625 + 0.004333 + 0.004583 + 0.004166 + 0.004584 + 0.004291 + 0.0045 + 0.003583 + 0.004333 + 0.003833 + 0.003667 + 0.005 + 0.004334 + 0.004417 + 0.003917 + 0.004292 + 0.004541 + 0.004417 + 0.004375 + 0.008917 + 0.006 + 0.006458 + 0.005833 + 0.006042 + 0.00525 + 0.004834 + 0.007084 + 0.006208 + 0.005167 + 0.004958 + 0.005125 + 0.005 + 0.003959 + 0.004459 + 0.011834 + 0.004791 + 0.003917 + 0.004083 + 0.00325 + 0.003708 + 0.003875 + 0.005167 + 0.004709 + 0.005 + 0.005709 + 0.006625 + 0.0055 + 0.005208 + 0.005666 + 0.0055 + 0.005667 + 0.005458 + 0.005 + 0.00625 + 0.006166 + 0.004833 + 0.006 + 0.005791 + 0.006125 + 0.005167 + 0.004708 + 0.006042 + 0.006084 + 0.00575 + 0.004458 + 0.004417 + 0.004625 + 0.004167 + 0.003625 + 0.003958 + 0.004292 + 0.004125 + 0.003709 + 0.004209 + 0.003708 + 0.00425 + 0.003791 + 0.004 + 0.00375 + 0.004292 + 0.003792 + 0.003583 + 0.003792 + 0.00375 + 0.003583 + 0.004 + 0.003625 + 0.004667 + 0.003625 + 0.004 + 0.004125 + 0.003542 + 0.004292 + 0.004 + 0.004041 + 0.003667 + 0.003709 + 0.00375 + 0.004084 + 0.003916 + 0.003792 + 0.003625 + 0.004333 + 0.003958 + 0.003625 + 0.003833 + 0.004333 + 0.003541 + 0.003917 + 0.003833 + 0.003917 + 0.00375 + 0.003959 + 0.00375 + 0.003875 + 0.003833 + 0.003834 + 0.003917 + 0.004083 + 0.003958 + 0.004 + 0.004875 + 0.00625 + 0.005917 + 0.005417 + 0.005584 + 0.005958 + 0.005458 + 0.004708 + 0.0045 + 0.008958 + 0.005583 + 0.005166 + 0.004875 + 0.004833 + 0.004792 + 0.004875 + 0.004542 + 0.004125 + 0.003833 + 0.003541 + 0.00425 + 0.003667 + 0.00375 + 0.004042 + 0.004167 + 0.004125 + 0.003958 + 0.005291 + 0.006583 + 0.005709 + 0.006166 + 0.00575 + 0.005833 + 0.005083 + 0.004208 + 0.004875 + 0.005958 + 0.005875 + 0.006166 + 0.010875 + 0.006042 + 0.005167 + 0.005792 + 0.004625 + 0.007792 + 0.003667 + 0.003917 + 0.004 + 0.003792 + 0.004 + 0.004792 + 0.003791 + 0.00375 + 0.003416 + 0.003958 + 0.003708 + 0.003917 + 0.004 + 0.003917 + 0.003875 + 0.004042 + 0.003792 + 0.00375 + 0.003792 + 0.003958 + 0.003792 + 0.003417 + 0.003542 + 0.003667 + 0.004041 + 0.003792 + 0.004083 + 0.00375 + 0.004042 + 0.003917 + 0.003916 + 0.003917 + 0.003959 + 0.0035 + 0.003875 + 0.003792 + 0.004166 + 0.003792 + 0.004125 + 0.003792 + 0.004417 + 0.003792 + 0.004 + 0.00375 + 0.004 + 0.003709 + 0.004416 + 0.008375 + 0.004583 + 0.005042 + 0.009791 + 0.004833 + 0.00375 + 0.003833 + 0.004083 + 0.005167 + 0.0045 + 0.004833 + 0.006542 + 0.006167 + 0.005875 + 0.005791 + 0.005625 + 0.004583 + 0.003875 + 0.004416 + 0.00425 + 0.004125 + 0.004041 + 0.005125 + 0.005 + 0.005 + 0.004667 + 0.005209 + 0.00475 + 0.004125 + 0.003834 + 0.003917 + 0.003958 + 0.003667 + 0.003709 + 0.004083 + 0.004708 + 0.005 + 0.006583 + 0.006083 + 0.005708 + 0.005625 + 0.005792 + 0.005 + 0.0045 + 0.006209 + 0.006375 + 0.005584 + 0.005667 + 0.005583 + 0.005708 + 0.004625 + 0.003583 + 0.003834 + 0.004041 + 0.003792 + 0.003834 + 0.004208 + 0.003625 + 0.004084 + 0.004417 + 0.004333 + 0.003708 + 0.003583 + 0.003875 + 0.004334 + 0.003959 + 0.004083 + 0.003833 + 0.003917 + 0.003709 + 0.004042 + 0.003875 + 0.003625 + 0.003667 + 0.003625 + 0.003625 + 0.003667 + 0.0035 + 0.004084 + 0.003959 + 0.003875 + 0.004125 + 0.003625 + 0.003625 + 0.00375 + 0.004125 + 0.003334 + 0.003667 + 0.003875 + 0.004083 + 0.003708 + 0.003667 + 0.004208 + 0.004041 + 0.003833 + 0.004125 + 0.003917 + 0.00425 + 0.003667 + 0.003708 + 0.00425 + 0.005833 + 0.004917 + 0.004916 + 0.004625 + 0.004416 + 0.0045 + 0.004208 + 0.004375 + 0.00475 + 0.005042 + 0.004792 + 0.006042 + 0.005834 + 0.005292 + 0.005666 + 0.004959 + 0.004334 + 0.004375 + 0.004417 + 0.004417 + 0.004042 + 0.00425 + 0.004166 + 0.004042 + 0.003959 + 0.004125 + 0.005042 + 0.004916 + 0.005 + 0.004583 + 0.004541 + 0.004542 + 0.004333 + 0.004375 + 0.004917 + 0.00475 + 0.004708 + 0.00575 + 0.005667 + 0.005625 + 0.005959 + 0.005792 + 0.005875 + 0.006167 + 0.005625 + 0.006542 + 0.005458 + 0.005083 + 0.005583 + 0.006125 + 0.005416 + 0.005042 + 0.005458 + 0.005583 + 0.005041 + 0.0045 + 0.004959 + 0.0055 + 0.006 + 0.004625 + 0.005208 + 0.004917 + 0.004375 + 0.004667 + 0.004958 + 0.005291 + 0.005083 + 0.00525 + 0.00475 + 0.004708 + 0.004708 + 0.004417 + 0.004541 + 0.003917 + 0.005458 + 0.005208 + 0.005583 + 0.005541 + 0.004834 + 0.004083 + 0.004541 + 0.004875 + 0.004833 + 0.004709 + 0.00475 + 0.004667 + 0.004875 + 0.004709 + 0.004917 + 0.004791 + 0.005333 + 0.004417 + 0.0045 + 0.004209 + 0.004542 + 0.003708 + 0.004083 + 0.003958 + 0.003916 + 0.004542 + 0.004416 + 0.004375 + 0.004542 + 0.005291 + 0.005042 + 0.005 + 0.004958 + 0.005417 + 0.00625 + 0.006208 + 0.006083 + 0.006 + 0.006583 + 0.006542 + 0.006541 + 0.005708 + 0.005292 + 0.00575 + 0.005625 + 0.006083 + 0.005292 + 0.004542 + 0.004125 + 0.004125 + 0.004042 + 0.004042 + 0.004125 + 0.004541 + 0.00425 + 0.004333 + 0.003833 + 0.004 + 0.006125 + 0.005542 + 0.005125 + 0.006417 + 0.006292 + 0.006375 + 0.009041 + 0.005833 + 0.006458 + 0.006208 + 0.005375 + 0.00625 + 0.006834 + 0.007166 + 0.0125 + 0.006625 + 0.005375 + 0.004875 + 0.005666 + 0.003583 + 0.003458 + 0.0035 + 0.003292 + 0.003416 + 0.003292 + 0.002833 + 0.003417 + 0.003042 + 0.00375 + 0.003375 + 0.003 + 0.003667 + 0.003875 + 0.003833 + 0.003958 + 0.004 + 0.003542 + 0.003542 + 0.003875 + 0.003166 + 0.003042 + 0.003 + 0.003125 + 0.003292 + 0.003666 + 0.00325 + 0.00325 + 0.003042 + 0.003125 + 0.003292 + 0.00375 + 0.003667 + 0.003708 + 0.003125 + 0.0035 + 0.002958 + 0.003708 + 0.003209 + 0.003 + 0.003042 + 0.003041 + 0.003042 + 0.003083 + 0.003125 + 0.003625 + 0.003125 + 0.00325 + 0.003916 + 0.00375 + 0.003459 + 0.00325 + 0.005167 + 0.004041 + 0.00425 + 0.004458 + 0.004042 + 0.005125 + 0.004875 + 0.00525 + 0.005542 + 0.006459 + 0.00625 + 0.005417 + 0.005833 + 0.005125 + 0.00475 + 0.004625 + 0.004625 + 0.004542 + 0.0045 + 0.003625 + 0.003334 + 0.00325 + 0.003417 + 0.003667 + 0.003709 + 0.00475 + 0.004209 + 0.003917 + 0.004 + 0.004417 + 0.004583 + 0.0045 + 0.004333 + 0.004208 + 0.004125 + 0.004292 + 0.005625 + 0.005833 + 0.006 + 0.004875 + 0.004583 + 0.004958 + 0.004875 + 0.005209 + 0.006625 + 0.006625 + 0.0065 + 0.006041 + 0.005459 + 0.005125 + 0.004791 + 0.004958 + 0.004792 + 0.004209 + 0.003625 + 0.003875 + 0.004791 + 0.00375 + 0.00425 + 0.004416 + 0.004458 + 0.00425 + 0.004334 + 0.004333 + 0.004292 + 0.004291 + 0.004042 + 0.004208 + 0.004125 + 0.004208 + 0.004292 + 0.004459 + 0.004042 + 0.00425 + 0.0045 + 0.00425 + 0.00425 + 0.004125 + 0.004125 + 0.004167 + 0.004292 + 0.004125 + 0.004084 + 0.003916 + 0.003709 + 0.004125 + 0.003791 + 0.004167 + 0.004042 + 0.004041 + 0.004083 + 0.00375 + 0.003625 + 0.004042 + 0.004166 + 0.00375 + 0.004125 + 0.00425 + 0.004042 + 0.004208 + 0.004125 + 0.004167 + 0.004125 + 0.004125 + 0.004125 + 0.006583 + 0.006166 + 0.005792 + 0.006 + 0.006375 + 0.005958 + 0.006083 + 0.007208 + 0.00525 + 0.004625 + 0.00575 + 0.005625 + 0.0055 + 0.005208 + 0.005042 + 0.005666 + 0.004917 + 0.004709 + 0.004416 + 0.005125 + 0.005084 + 0.004334 + 0.00425 + 0.004417 + 0.004166 + 0.00425 + 0.00425 + 0.006167 + 0.007042 + 0.006375 + 0.006416 + 0.00625 + 0.005959 + 0.006209 + 0.006959 + 0.005542 + 0.006541 + 0.006292 + 0.006041 + 0.006208 + 0.006084 + 0.006875 + 0.006375 + 0.005 + 0.005042 + 0.004875 + 0.005125 + 0.005167 + 0.004875 + 0.004792 + 0.004709 + 0.004375 + 0.004042 + 0.00375 + 0.003708 + 0.004209 + 0.004208 + 0.004166 + 0.003709 + 0.003875 + 0.003875 + 0.004084 + 0.00425 + 0.004375 + 0.004042 + 0.00425 + 0.00425 + 0.003791 + 0.004 + 0.004 + 0.004042 + 0.004083 + 0.004208 + 0.004084 + 0.003541 + 0.003792 + 0.0035 + 0.003625 + 0.003917 + 0.003958 + 0.003875 + 0.003625 + 0.003458 + 0.003958 + 0.004292 + 0.003958 + 0.003791 + 0.004042 + 0.004167 + 0.003709 + 0.004167 + 0.004458 + 0.005542 + 0.004208 + 0.004291 + 0.004083 + 0.003958 + 0.004166 + 0.0045 + 0.0045 + 0.00425 + 0.005375 + 0.005292 + 0.006667 + 0.006666 + 0.006042 + 0.006167 + 0.006208 + 0.005291 + 0.005709 + 0.005167 + 0.004875 + 0.004208 + 0.004167 + 0.004041 + 0.004458 + 0.004125 + 0.00425 + 0.005458 + 0.005583 + 0.005 + 0.006 + 0.005209 + 0.00525 + 0.005583 + 0.005208 + 0.005417 + 0.005375 + 0.004875 + 0.004666 + 0.005958 + 0.007083 + 0.006042 + 0.006125 + 0.006167 + 0.005459 + 0.006209 + 0.00675 + 0.006916 + 0.006042 + 0.00625 + 0.006 + 0.00625 + 0.005 + 0.004875 + 0.004917 + 0.004334 + 0.003791 + 0.003708 + 0.003667 + 0.004625 + 0.004459 + 0.004042 + 0.003667 + 0.004459 + 0.004625 + 0.00375 + 0.003792 + 0.003833 + 0.00425 + 0.003875 + 0.004125 + 0.004 + 0.004 + 0.003709 + 0.004583 + 0.005125 + 0.003959 + 0.003584 + 0.004209 + 0.00575 + 0.005083 + 0.00425 + 0.004042 + 0.004042 + 0.003833 + 0.004 + 0.004291 + 0.005292 + 0.004708 + 0.0055 + 0.014792 + 0.0065 + 0.005167 + 0.005209 + 0.005167 + 0.005083 + 0.004625 + 0.005125 + 0.004667 + 0.003833 + 0.004375 + 0.005 + 0.00475 + 0.00425 + 0.004125 + 0.006125 + 0.004875 + 0.004834 + 0.005167 + 0.00475 + 0.006625 + 0.005084 + 0.006125 + 0.005709 + 0.006917 + 0.006542 + 0.00625 + 0.005458 + 0.005125 + 0.00525 + 0.005334 + 0.005417 + 0.004792 + 0.004125 + 0.00425 + 0.00425 + 0.003917 + 0.004541 + 0.004417 + 0.004291 + 0.00475 + 0.004958 + 0.005083 + 0.005958 + 0.0055 + 0.005584 + 0.005417 + 0.006541 + 0.006542 + 0.006417 + 0.006583 + 0.006041 + 0.006583 + 0.006125 + 0.004916 + 0.005958 + 0.005625 + 0.006083 + 0.0045 + 0.005791 + 0.005875 + 0.005958 + 0.005291 + 0.004792 + 0.004417 + 0.004375 + 0.00375 + 0.003834 + 0.003875 + 0.003875 + 0.003958 + 0.003834 + 0.003875 + 0.00375 + 0.004334 + 0.003834 + 0.003959 + 0.00375 + 0.003625 + 0.003917 + 0.003917 + 0.003583 + 0.003916 + 0.0035 + 0.004125 + 0.00375 + 0.004042 + 0.003791 + 0.004084 + 0.003917 + 0.003833 + 0.003791 + 0.003917 + 0.003959 + 0.003708 + 0.003625 + 0.003791 + 0.003292 + 0.0035 + 0.003959 + 0.003708 + 0.00425 + 0.00375 + 0.00375 + 0.003875 + 0.004 + 0.003834 + 0.00375 + 0.003792 + 0.004 + 0.003875 + 0.004208 + 0.004625 + 0.004167 + 0.004083 + 0.004292 + 0.004083 + 0.003916 + 0.003917 + 0.005791 + 0.006 + 0.005666 + 0.005667 + 0.005167 + 0.005083 + 0.004458 + 0.00625 + 0.005667 + 0.005583 + 0.004666 + 0.005084 + 0.004833 + 0.005 + 0.004917 + 0.005125 + 0.004625 + 0.003958 + 0.004375 + 0.004209 + 0.004167 + 0.004292 + 0.004417 + 0.004208 + 0.004167 + 0.004917 + 0.004833 + 0.005208 + 0.00775 + 0.006959 + 0.007 + 0.006625 + 0.0065 + 0.005958 + 0.005958 + 0.004958 + 0.003833 + 0.006 + 0.005875 + 0.005833 + 0.005583 + 0.005875 + 0.005334 + 0.004458 + 0.004166 + 0.003709 + 0.004042 + 0.003667 + 0.004084 + 0.003666 + 0.003625 + 0.003875 + 0.004375 + 0.003875 + 0.003834 + 0.004834 + 0.004333 + 0.004833 + 0.004041 + 0.00425 + 0.003625 + 0.00425 + 0.003833 + 0.003667 + 0.003958 + 0.004 + 0.003792 + 0.004375 + 0.003833 + 0.003875 + 0.003833 + 0.004125 + 0.003875 + 0.003833 + 0.003583 + 0.003833 + 0.003792 + 0.00375 + 0.003542 + 0.003625 + 0.003833 + 0.003333 + 0.004 + 0.003833 + 0.003916 + 0.003834 + 0.0035 + 0.003875 + 0.004375 + 0.004 + 0.004084 + 0.003708 + 0.004083 + 0.005625 + 0.005041 + 0.004917 + 0.004917 + 0.004458 + 0.004916 + 0.004375 + 0.004333 + 0.004334 + 0.004917 + 0.005083 + 0.006709 + 0.005666 + 0.005958 + 0.005334 + 0.005209 + 0.004584 + 0.00425 + 0.004208 + 0.004 + 0.004084 + 0.004125 + 0.004 + 0.003875 + 0.004083 + 0.004375 + 0.005792 + 0.004833 + 0.00475 + 0.005042 + 0.005 + 0.004709 + 0.004959 + 0.005 + 0.004792 + 0.005375 + 0.006667 + 0.006708 + 0.007541 + 0.006167 + 0.006416 + 0.006083 + 0.005917 + 0.005084 + 0.005625 + 0.005667 + 0.005334 + 0.005375 + 0.005333 + 0.00575 + 0.005542 + 0.004292 + 0.003917 + 0.004375 + 0.003792 + 0.003666 + 0.004125 + 0.003834 + 0.00325 + 0.003792 + 0.003958 + 0.003583 + 0.004166 + 0.003709 + 0.003541 + 0.003667 + 0.00375 + 0.003542 + 0.003959 + 0.003875 + 0.004084 + 0.004333 + 0.004458 + 0.004583 + 0.00525 + 0.005083 + 0.005 + 0.005042 + 0.005166 + 0.005042 + 0.005166 + 0.004833 + 0.004958 + 0.004583 + 0.004083 + 0.003458 + 0.003791 + 0.003875 + 0.00375 + 0.003458 + 0.003792 + 0.004209 + 0.003667 + 0.004083 + 0.003583 + 0.003708 + 0.003791 + 0.004209 + 0.004 + 0.004084 + 0.004334 + 0.004167 + 0.004042 + 0.004167 + 0.005625 + 0.005167 + 0.004958 + 0.00525 + 0.005083 + 0.006542 + 0.006625 + 0.006375 + 0.005583 + 0.006458 + 0.006083 + 0.00625 + 0.0055 + 0.005334 + 0.005084 + 0.004792 + 0.004791 + 0.005 + 0.004416 + 0.004542 + 0.0045 + 0.004167 + 0.004459 + 0.0045 + 0.00425 + 0.004292 + 0.004334 + 0.004125 + 0.005709 + 0.005709 + 0.00575 + 0.005958 + 0.006125 + 0.008041 + 0.006167 + 0.006333 + 0.006625 + 0.006292 + 0.006125 + 0.005459 + 0.005959 + 0.005792 + 0.005916 + 0.006708 + 0.006375 + 0.00575 + 0.005833 + 0.005416 + 0.004917 + 0.004959 + 0.005375 + 0.005167 + 0.004875 + 0.004833 + 0.004083 + 0.003958 + 0.0035 + 0.004166 + 0.004084 + 0.004292 + 0.003958 + 0.003666 + 0.004 + 0.003958 + 0.003959 + 0.004041 + 0.003542 + 0.003667 + 0.004083 + 0.004 + 0.003709 + 0.004083 + 0.004 + 0.00375 + 0.004083 + 0.003958 + 0.004125 + 0.003875 + 0.003875 + 0.004 + 0.004334 + 0.004042 + 0.003458 + 0.003791 + 0.003542 + 0.003834 + 0.003417 + 0.0035 + 0.003667 + 0.003875 + 0.003833 + 0.003708 + 0.003875 + 0.004125 + 0.004 + 0.004042 + 0.004166 + 0.004083 + 0.003917 + 0.00425 + 0.005667 + 0.005125 + 0.00525 + 0.005417 + 0.00525 + 0.006208 + 0.006208 + 0.00625 + 0.005541 + 0.006292 + 0.006958 + 0.006083 + 0.005042 + 0.004959 + 0.00525 + 0.005083 + 0.004875 + 0.005041 + 0.004042 + 0.003791 + 0.00425 + 0.003709 + 0.004334 + 0.004167 + 0.004292 + 0.004542 + 0.004208 + 0.005125 + 0.005042 + 0.00525 + 0.0055 + 0.005 + 0.006625 + 0.006667 + 0.006041 + 0.006167 + 0.006125 + 0.00675 + 0.006541 + 0.004792 + 0.00625 + 0.006 + 0.006125 + 0.004917 + 0.005542 + 0.006125 + 0.006125 + 0.006083 + 0.005333 + 0.004875 + 0.004959 + 0.004917 + 0.004541 + 0.004333 + 0.00425 + 0.004333 + 0.003458 + 0.003917 + 0.003709 + 0.004084 + 0.004 + 0.004041 + 0.00375 + 0.0035 + 0.004125 + 0.004208 + 0.00425 + 0.003666 + 0.003833 + 0.004167 + 0.004208 + 0.003666 + 0.003917 + 0.004416 + 0.003917 + 0.003958 + 0.003792 + 0.00375 + 0.004084 + 0.003875 + 0.00375 + 0.003792 + 0.004208 + 0.003625 + 0.004125 + 0.0045 + 0.004292 + 0.004083 + 0.00425 + 0.003666 + 0.003667 + 0.004208 + 0.003917 + 0.003875 + 0.00375 + 0.003917 + 0.004875 + 0.004541 + 0.004167 + 0.004166 + 0.004333 + 0.004 + 0.004167 + 0.004083 + 0.004042 + 0.007208 + 0.006667 + 0.006917 + 0.006041 + 0.006167 + 0.006042 + 0.008375 + 0.020166 + 0.011958 + 0.007583 + 0.005375 + 0.005959 + 0.004458 + 0.00475 + 0.004583 + 0.004375 + 0.005166 + 0.005 + 0.005333 + 0.0045 + 0.003958 + 0.004708 + 0.004625 + 0.00675 + 0.005583 + 0.007041 + 0.005417 + 0.00825 + 0.007 + 0.005459 + 0.006583 + 0.005833 + 0.007375 + 0.007541 + 0.005083 + 0.004833 + 0.006083 + 0.006833 + 0.006833 + 0.0055 + 0.006916 + 0.006958 + 0.006375 + 0.005875 + 0.005625 + 0.005458 + 0.006417 + 0.006041 + 0.006167 + 0.005416 + 0.005042 + 0.004042 + 0.004 + 0.005375 + 0.004417 + 0.005833 + 0.004333 + 0.004958 + 0.004417 + 0.003958 + 0.004208 + 0.004375 + 0.004666 + 0.00425 + 0.004459 + 0.004292 + 0.004208 + 0.00425 + 0.003792 + 0.004416 + 0.004083 + 0.004583 + 0.004084 + 0.004417 + 0.004625 + 0.004458 + 0.004416 + 0.00425 + 0.004292 + 0.0045 + 0.004209 + 0.004292 + 0.004458 + 0.003917 + 0.004333 + 0.003958 + 0.00425 + 0.004042 + 0.004166 + 0.004625 + 0.004416 + 0.003958 + 0.004209 + 0.004542 + 0.005041 + 0.004125 + 0.00425 + 0.004292 + 0.004 + 0.004416 + 0.004167 + 0.006917 + 0.006416 + 0.005792 + 0.005708 + 0.005833 + 0.005375 + 0.004667 + 0.004125 + 0.004083 + 0.004375 + 0.005916 + 0.005625 + 0.005125 + 0.005459 + 0.004792 + 0.005125 + 0.005792 + 0.005458 + 0.004791 + 0.004375 + 0.004625 + 0.004458 + 0.004167 + 0.004417 + 0.004875 + 0.004291 + 0.005834 + 0.006792 + 0.006917 + 0.0065 + 0.006875 + 0.006208 + 0.006541 + 0.007792 + 0.00525 + 0.0065 + 0.00625 + 0.006417 + 0.006917 + 0.006 + 0.0075 + 0.006208 + 0.004958 + 0.004791 + 0.004583 + 0.004708 + 0.004333 + 0.004667 + 0.004084 + 0.00375 + 0.004125 + 0.003541 + 0.003625 + 0.004042 + 0.004125 + 0.004084 + 0.003958 + 0.003541 + 0.004125 + 0.00375 + 0.003542 + 0.003625 + 0.004 + 0.004167 + 0.003875 + 0.003958 + 0.004 + 0.003959 + 0.003791 + 0.004542 + 0.004291 + 0.004042 + 0.004125 + 0.004209 + 0.003959 + 0.004 + 0.004042 + 0.003834 + 0.003667 + 0.003833 + 0.003833 + 0.004167 + 0.003833 + 0.003917 + 0.004167 + 0.003916 + 0.004 + 0.003666 + 0.003458 + 0.004125 + 0.004459 + 0.004292 + 0.004041 + 0.0045 + 0.004208 + 0.003875 + 0.004083 + 0.00425 + 0.004584 + 0.004459 + 0.005042 + 0.0055 + 0.00725 + 0.006583 + 0.006083 + 0.006375 + 0.005333 + 0.004125 + 0.004209 + 0.004459 + 0.004209 + 0.004208 + 0.004208 + 0.004458 + 0.005291 + 0.005042 + 0.005167 + 0.005083 + 0.004375 + 0.005083 + 0.004792 + 0.004584 + 0.004167 + 0.004125 + 0.0045 + 0.004541 + 0.003875 + 0.005125 + 0.005166 + 0.005417 + 0.006084 + 0.005792 + 0.005791 + 0.005708 + 0.004375 + 0.005125 + 0.005291 + 0.006 + 0.00625 + 0.006 + 0.005959 + 0.00575 + 0.005 + 0.004125 + 0.003917 + 0.004209 + 0.003709 + 0.004 + 0.003667 + 0.003791 + 0.00425 + 0.003625 + 0.00375 + 0.004042 + 0.004166 + 0.003958 + 0.003792 + 0.003584 + 0.003959 + 0.003708 + 0.003541 + 0.003667 + 0.005541 + 0.004 + 0.003833 + 0.003959 + 0.00425 + 0.004042 + 0.004 + 0.003792 + 0.004 + 0.003625 + 0.003708 + 0.004167 + 0.003958 + 0.003833 + 0.004 + 0.003583 + 0.003959 + 0.003833 + 0.003833 + 0.003875 + 0.003875 + 0.004 + 0.003958 + 0.003708 + 0.003541 + 0.003708 + 0.003833 + 0.003667 + 0.00425 + 0.003834 + 0.005333 + 0.004833 + 0.005041 + 0.004917 + 0.004709 + 0.004916 + 0.004584 + 0.004209 + 0.004625 + 0.005042 + 0.004958 + 0.006291 + 0.005917 + 0.006375 + 0.006209 + 0.005959 + 0.004375 + 0.004542 + 0.004875 + 0.004959 + 0.00425 + 0.004834 + 0.004041 + 0.004167 + 0.004791 + 0.00525 + 0.004958 + 0.004708 + 0.004791 + 0.004792 + 0.004375 + 0.004209 + 0.004167 + 0.004583 + 0.004333 + 0.005291 + 0.005208 + 0.004958 + 0.006083 + 0.005958 + 0.005875 + 0.00625 + 0.005542 + 0.005708 + 0.005083 + 0.00625 + 0.006209 + 0.005875 + 0.006208 + 0.006334 + 0.005375 + 0.00475 + 0.004333 + 0.004208 + 0.003666 + 0.00425 + 0.004542 + 0.004458 + 0.003917 + 0.00425 + 0.003584 + 0.004459 + 0.004334 + 0.004 + 0.003958 + 0.003958 + 0.004042 + 0.003834 + 0.00375 + 0.003625 + 0.003708 + 0.003792 + 0.003625 + 0.004 + 0.003958 + 0.004 + 0.004291 + 0.004042 + 0.004666 + 0.004625 + 0.004291 + 0.004333 + 0.003834 + 0.003959 + 0.003875 + 0.003875 + 0.00375 + 0.00375 + 0.003916 + 0.003709 + 0.003792 + 0.00375 + 0.004 + 0.003667 + 0.003583 + 0.003583 + 0.003875 + 0.003708 + 0.004166 + 0.004833 + 0.005958 + 0.005417 + 0.005209 + 0.004917 + 0.005166 + 0.00525 + 0.005125 + 0.005167 + 0.006583 + 0.005541 + 0.005667 + 0.0065 + 0.006 + 0.006292 + 0.006 + 0.005834 + 0.004542 + 0.004125 + 0.004042 + 0.00425 + 0.004166 + 0.00425 + 0.005458 + 0.005208 + 0.00475 + 0.004917 + 0.004791 + 0.004958 + 0.004208 + 0.00375 + 0.004041 + 0.004167 + 0.004125 + 0.004334 + 0.004208 + 0.004917 + 0.006 + 0.006542 + 0.006083 + 0.005916 + 0.005625 + 0.005541 + 0.005375 + 0.004333 + 0.005125 + 0.006667 + 0.006125 + 0.005667 + 0.005916 + 0.005833 + 0.005083 + 0.00375 + 0.003708 + 0.004125 + 0.003958 + 0.004541 + 0.003833 + 0.003875 + 0.003584 + 0.00375 + 0.003625 + 0.003542 + 0.004 + 0.004208 + 0.00375 + 0.004 + 0.003792 + 0.003958 + 0.003625 + 0.003625 + 0.003375 + 0.003791 + 0.003667 + 0.0035 + 0.003417 + 0.003708 + 0.003875 + 0.003959 + 0.004125 + 0.004042 + 0.004208 + 0.003708 + 0.004125 + 0.003834 + 0.003667 + 0.003583 + 0.003916 + 0.004083 + 0.003875 + 0.003375 + 0.004 + 0.003792 + 0.003792 + 0.003875 + 0.004083 + 0.003709 + 0.003375 + 0.003709 + 0.003583 + 0.003 + 0.003791 + 0.00375 + 0.003958 + 0.005458 + 0.0045 + 0.004416 + 0.004833 + 0.004875 + 0.004917 + 0.005541 + 0.005083 + 0.004792 + 0.004625 + 0.005875 + 0.005916 + 0.00575 + 0.004625 + 0.005292 + 0.007125 + 0.00425 + 0.004208 + 0.004458 + 0.003833 + 0.004375 + 0.004041 + 0.004333 + 0.003792 + 0.003959 + 0.003875 + 0.003833 + 0.004875 + 0.004583 + 0.004791 + 0.004833 + 0.004958 + 0.005625 + 0.005375 + 0.004708 + 0.004708 + 0.006417 + 0.006167 + 0.005875 + 0.006375 + 0.00525 + 0.006791 + 0.006125 + 0.007375 + 0.006459 + 0.006334 + 0.005584 + 0.004708 + 0.004042 + 0.003792 + 0.003583 + 0.003542 + 0.003959 + 0.003917 + 0.004083 + 0.004333 + 0.00375 + 0.003666 + 0.003125 + 0.003708 + 0.004583 + 0.004125 + 0.003959 + 0.003875 + 0.003959 + 0.003875 + 0.003958 + 0.003834 + 0.00375 + 0.003625 + 0.003584 + 0.003917 + 0.003625 + 0.003333 + 0.003666 + 0.004125 + 0.003958 + 0.00375 + 0.003792 + 0.003875 + 0.00375 + 0.003625 + 0.003834 + 0.003833 + 0.0035 + 0.003958 + 0.003916 + 0.004083 + 0.00375 + 0.004083 + 0.004 + 0.003833 + 0.003958 + 0.003875 + 0.003916 + 0.003625 + 0.003833 + 0.004541 + 0.004 + 0.003833 + 0.003625 + 0.004 + 0.003667 + 0.004875 + 0.004625 + 0.004791 + 0.005958 + 0.00575 + 0.005708 + 0.004958 + 0.004917 + 0.00625 + 0.006041 + 0.004875 + 0.005125 + 0.005 + 0.005 + 0.00425 + 0.004 + 0.003791 + 0.004167 + 0.00425 + 0.003667 + 0.00375 + 0.004167 + 0.00375 + 0.00425 + 0.004 + 0.004166 + 0.003708 + 0.003625 + 0.004167 + 0.004125 + 0.006583 + 0.006042 + 0.005959 + 0.005792 + 0.005541 + 0.005208 + 0.006125 + 0.004833 + 0.006041 + 0.005667 + 0.006125 + 0.005458 + 0.005083 + 0.006458 + 0.006125 + 0.005875 + 0.0045 + 0.004375 + 0.004209 + 0.003959 + 0.003541 + 0.004125 + 0.004125 + 0.003792 + 0.003416 + 0.003833 + 0.003625 + 0.003667 + 0.003917 + 0.003958 + 0.003667 + 0.003834 + 0.004 + 0.003834 + 0.003708 + 0.004 + 0.003792 + 0.003625 + 0.003583 + 0.003625 + 0.003583 + 0.00325 + 0.003666 + 0.003667 + 0.003916 + 0.003625 + 0.004166 + 0.003917 + 0.004125 + 0.003709 + 0.003917 + 0.004042 + 0.003916 + 0.003834 + 0.004166 + 0.003625 + 0.003917 + 0.003709 + 0.004084 + 0.003542 + 0.003833 + 0.003792 + 0.003791 + 0.003375 + 0.00425 + 0.004166 + 0.004375 + 0.00375 + 0.003709 + 0.004 + 0.003709 + 0.003584 + 0.003583 + 0.006791 + 0.00625 + 0.006208 + 0.005583 + 0.007833 + 0.005459 + 0.005583 + 0.00575 + 0.005209 + 0.004917 + 0.004791 + 0.0045 + 0.004625 + 0.003958 + 0.004041 + 0.003875 + 0.004417 + 0.004 + 0.004166 + 0.004 + 0.004375 + 0.003791 + 0.004 + 0.00425 + 0.005667 + 0.005375 + 0.004459 + 0.006375 + 0.006375 + 0.00625 + 0.006167 + 0.005416 + 0.007 + 0.005833 + 0.005792 + 0.006916 + 0.006333 + 0.006292 + 0.005834 + 0.023292 + 0.012916 + 0.005791 + 0.005875 + 0.005292 + 0.004416 + 0.004417 + 0.004375 + 0.004875 + 0.004708 + 0.004708 + 0.005167 + 0.003792 + 0.005125 + 0.005208 + 0.006334 + 0.005584 + 0.006083 + 0.005166 + 0.004667 + 0.004708 + 0.00475 + 0.004375 + 0.004333 + 0.004084 + 0.005167 + 0.005 + 0.004625 + 0.00475 + 0.005542 + 0.004792 + 0.004833 + 0.005375 + 0.005667 + 0.005541 + 0.005375 + 0.005958 + 0.005541 + 0.005125 + 0.005084 + 0.004834 + 0.004959 + 0.004709 + 0.005 + 0.00525 + 0.005333 + 0.004583 + 0.004458 + 0.004958 + 0.0045 + 0.004333 + 0.0045 + 0.00475 + 0.004666 + 0.005167 + 0.005333 + 0.004958 + 0.005292 + 0.004917 + 0.006125 + 0.00525 + 0.00525 + 0.0055 + 0.00575 + 0.005625 + 0.00625 + 0.007375 + 0.007625 + 0.006542 + 0.005459 + 0.004792 + 0.005666 + 0.004958 + 0.005125 + 0.004833 + 0.004833 + 0.005125 + 0.00525 + 0.005125 + 0.005708 + 0.00525 + 0.005417 + 0.005333 + 0.00525 + 0.006458 + 0.01475 + 0.005583 + 0.005208 + 0.006042 + 0.007 + 0.006541 + 0.006208 + 0.009167 + 0.007209 + 0.007 + 0.006958 + 0.005375 + 0.005916 + 0.005792 + 0.007334 + 0.006584 + 0.005959 + 0.005834 + 0.005625 + 0.004542 + 0.0045 + 0.003958 + 0.004125 + 0.003958 + 0.003792 + 0.003917 + 0.005083 + 0.005458 + 0.005292 + 0.004875 + 0.004833 + 0.005708 + 0.005375 + 0.005125 + 0.005208 + 0.004458 + 0.007125 + 0.007959 + 0.007625 + 0.006833 + 0.007333 + 0.007416 + 0.006125 + 0.007875 + 0.00575 + 0.005583 + 0.006 + 0.006 + 0.006333 + 0.005375 + 0.004375 + 0.004542 + 0.004416 + 0.005125 + 0.00525 + 0.005583 + 0.005541 + 0.004792 + 0.005334 + 0.005667 + 0.0045 + 0.005708 + 0.005209 + 0.005292 + 0.005791 + 0.005333 + 0.00525 + 0.0055 + 0.004416 + 0.004625 + 0.004667 + 0.004333 + 0.004375 + 0.0045 + 0.004084 + 0.006167 + 0.006666 + 0.007042 + 0.006334 + 0.005834 + 0.006 + 0.005625 + 0.007209 + 0.006875 + 0.005833 + 0.004833 + 0.004208 + 0.004292 + 0.004541 + 0.004208 + 0.004292 + 0.004625 + 0.004709 + 0.004834 + 0.004416 + 0.00425 + 0.004459 + 0.00425 + 0.004292 + 0.004375 + 0.004291 + 0.0045 + 0.004667 + 0.00625 + 0.006917 + 0.006333 + 0.006167 + 0.006584 + 0.006625 + 0.0065 + 0.007208 + 0.005875 + 0.006959 + 0.006375 + 0.006959 + 0.007166 + 0.006375 + 0.006625 + 0.0055 + 0.004291 + 0.004 + 0.004375 + 0.004458 + 0.003667 + 0.003959 + 0.003667 + 0.004625 + 0.004416 + 0.0035 + 0.003875 + 0.004334 + 0.0045 + 0.00425 + 0.003875 + 0.004333 + 0.004292 + 0.00375 + 0.004167 + 0.004167 + 0.003959 + 0.004084 + 0.004417 + 0.004042 + 0.004584 + 0.004042 + 0.004125 + 0.003959 + 0.004083 + 0.003667 + 0.004459 + 0.003792 + 0.003708 + 0.003875 + 0.00375 + 0.004 + 0.004208 + 0.004042 + 0.00375 + 0.00375 + 0.004084 + 0.003917 + 0.004375 + 0.004 + 0.004166 + 0.004042 + 0.004 + 0.004125 + 0.004125 + 0.004083 + 0.0045 + 0.004208 + 0.004292 + 0.004834 + 0.004291 + 0.004459 + 0.004375 + 0.004375 + 0.005375 + 0.004917 + 0.006542 + 0.005792 + 0.00575 + 0.005666 + 0.0055 + 0.004416 + 0.00425 + 0.004375 + 0.004416 + 0.005 + 0.004416 + 0.004875 + 0.005084 + 0.00525 + 0.004875 + 0.004625 + 0.00525 + 0.004584 + 0.004666 + 0.004333 + 0.004292 + 0.004292 + 0.004125 + 0.004583 + 0.004416 + 0.005417 + 0.0055 + 0.005542 + 0.006333 + 0.006 + 0.005125 + 0.006542 + 0.004708 + 0.005208 + 0.006208 + 0.005958 + 0.006125 + 0.005916 + 0.005625 + 0.00575 + 0.0055 + 0.004875 + 0.004542 + 0.004834 + 0.004709 + 0.004792 + 0.004083 + 0.00425 + 0.004416 + 0.003667 + 0.003584 + 0.00375 + 0.004542 + 0.004083 + 0.004083 + 0.003916 + 0.003958 + 0.004 + 0.004167 + 0.004208 + 0.004 + 0.004292 + 0.004541 + 0.004208 + 0.003708 + 0.003833 + 0.003792 + 0.004625 + 0.004125 + 0.00375 + 0.003917 + 0.003875 + 0.003792 + 0.003958 + 0.004042 + 0.003417 + 0.003666 + 0.003917 + 0.004125 + 0.003917 + 0.003958 + 0.003833 + 0.003958 + 0.003708 + 0.003875 + 0.003833 + 0.004041 + 0.004042 + 0.0045 + 0.00475 + 0.005 + 0.004792 + 0.004459 + 0.004584 + 0.004334 + 0.004125 + 0.004209 + 0.00425 + 0.004875 + 0.005042 + 0.006708 + 0.005875 + 0.005375 + 0.005875 + 0.005875 + 0.005916 + 0.005458 + 0.004625 + 0.004042 + 0.004334 + 0.004209 + 0.005834 + 0.005042 + 0.005333 + 0.004917 + 0.005125 + 0.005041 + 0.004208 + 0.004417 + 0.004083 + 0.004042 + 0.004083 + 0.003958 + 0.004167 + 0.004041 + 0.005334 + 0.004834 + 0.00575 + 0.006458 + 0.0055 + 0.005583 + 0.005625 + 0.004083 + 0.005541 + 0.006917 + 0.006625 + 0.006917 + 0.005667 + 0.00575 + 0.005667 + 0.005125 + 0.00475 + 0.00525 + 0.004625 + 0.004125 + 0.004375 + 0.00325 + 0.003375 + 0.003625 + 0.003625 + 0.003042 + 0.003833 + 0.00325 + 0.004667 + 0.003334 + 0.003167 + 0.003417 + 0.003416 + 0.003417 + 0.003709 + 0.003583 + 0.00375 + 0.004 + 0.004292 + 0.003958 + 0.004042 + 0.003833 + 0.003375 + 0.003125 + 0.003042 + 0.00325 + 0.003417 + 0.003208 + 0.003375 + 0.003209 + 0.003542 + 0.004208 + 0.008458 + 0.003875 + 0.00375 + 0.003709 + 0.00375 + 0.00375 + 0.004 + 0.00375 + 0.003416 + 0.003208 + 0.003459 + 0.004 + 0.004959 + 0.005834 + 0.0045 + 0.004625 + 0.004792 + 0.004958 + 0.004791 + 0.005083 + 0.004083 + 0.004416 + 0.004417 + 0.0045 + 0.005958 + 0.007083 + 0.007333 + 0.009291 + 0.005917 + 0.004667 + 0.003917 + 0.004459 + 0.004833 + 0.003833 + 0.003958 + 0.003791 + 0.00375 + 0.003833 + 0.003834 + 0.005666 + 0.004917 + 0.00475 + 0.004708 + 0.004667 + 0.004792 + 0.004291 + 0.004 + 0.004917 + 0.017834 + 0.008583 + 0.006416 + 0.008459 + 0.006834 + 0.005583 + 0.005708 + 0.005625 + 0.0055 + 0.006167 + 0.008583 + 0.00675 + 0.006667 + 0.006667 + 0.00725 + 0.005334 + 0.005166 + 0.00425 + 0.004167 + 0.00425 + 0.004334 + 0.004209 + 0.0045 + 0.004291 + 0.004584 + 0.004417 + 0.003666 + 0.003792 + 0.004209 + 0.004417 + 0.004208 + 0.004166 + 0.004375 + 0.004459 + 0.004417 + 0.00475 + 0.004333 + 0.004042 + 0.004125 + 0.004375 + 0.00425 + 0.004791 + 0.004917 + 0.004291 + 0.00425 + 0.004334 + 0.004291 + 0.004333 + 0.004167 + 0.004375 + 0.004125 + 0.004 + 0.003916 + 0.004209 + 0.004458 + 0.003916 + 0.004917 + 0.004666 + 0.004459 + 0.004167 + 0.004542 + 0.004042 + 0.00425 + 0.004166 + 0.014875 + 0.005041 + 0.006125 + 0.005375 + 0.00525 + 0.005291 + 0.00525 + 0.004916 + 0.004875 + 0.005291 + 0.005417 + 0.005292 + 0.00525 + 0.006458 + 0.006125 + 0.005667 + 0.006166 + 0.005333 + 0.004708 + 0.004375 + 0.004292 + 0.0045 + 0.004542 + 0.004625 + 0.004459 + 0.004666 + 0.004417 + 0.008875 + 0.005542 + 0.004916 + 0.005333 + 0.005542 + 0.005334 + 0.004917 + 0.00425 + 0.004791 + 0.004792 + 0.005583 + 0.005041 + 0.00625 + 0.006583 + 0.006333 + 0.006375 + 0.006084 + 0.004625 + 0.00475 + 0.005958 + 0.006125 + 0.006667 + 0.005708 + 0.006042 + 0.005334 + 0.004209 + 0.004166 + 0.004208 + 0.004334 + 0.004 + 0.004125 + 0.004375 + 0.004916 + 0.004625 + 0.003333 + 0.003583 + 0.004625 + 0.013583 + 0.004167 + 0.004375 + 0.004125 + 0.003875 + 0.004209 + 0.004042 + 0.004125 + 0.003834 + 0.0045 + 0.004167 + 0.003708 + 0.004 + 0.003833 + 0.003875 + 0.003917 + 0.004166 + 0.004125 + 0.004083 + 0.004375 + 0.004125 + 0.004041 + 0.004042 + 0.003875 + 0.003709 + 0.00425 + 0.004708 + 0.004417 + 0.004875 + 0.005 + 0.005 + 0.004209 + 0.005125 + 0.005292 + 0.005458 + 0.004959 + 0.005 + 0.005125 + 0.004833 + 0.005625 + 0.005417 + 0.005708 + 0.004625 + 0.005333 + 0.005708 + 0.005792 + 0.006917 + 0.006083 + 0.006875 + 0.006834 + 0.007084 + 0.006542 + 0.006791 + 0.005792 + 0.00475 + 0.00475 + 0.004708 + 0.005125 + 0.004959 + 0.004875 + 0.005584 + 0.005584 + 0.005292 + 0.005375 + 0.006 + 0.00575 + 0.005417 + 0.005333 + 0.005667 + 0.00675 + 0.005375 + 0.005375 + 0.005125 + 0.006708 + 0.006625 + 0.006375 + 0.006166 + 0.005792 + 0.006791 + 0.006458 + 0.005708 + 0.006 + 0.006167 + 0.006667 + 0.00675 + 0.006917 + 0.006875 + 0.006167 + 0.006333 + 0.004917 + 0.0045 + 0.004792 + 0.004541 + 0.003667 + 0.003875 + 0.003875 + 0.004042 + 0.004041 + 0.003917 + 0.003667 + 0.0045 + 0.003959 + 0.003625 + 0.003834 + 0.003709 + 0.003917 + 0.003791 + 0.003958 + 0.004167 + 0.003958 + 0.004208 + 0.003917 + 0.004292 + 0.003958 + 0.004042 + 0.003666 + 0.004334 + 0.003875 + 0.003792 + 0.003834 + 0.003667 + 0.004 + 0.003833 + 0.003791 + 0.003709 + 0.00375 + 0.004084 + 0.004125 + 0.004458 + 0.0045 + 0.004083 + 0.004 + 0.004 + 0.003959 + 0.003833 + 0.004208 + 0.004333 + 0.004459 + 0.004125 + 0.004292 + 0.004375 + 0.00425 + 0.004625 + 0.003917 + 0.003875 + 0.004125 + 0.006208 + 0.006667 + 0.006209 + 0.006292 + 0.006083 + 0.006125 + 0.006834 + 0.006667 + 0.005666 + 0.005792 + 0.005417 + 0.005125 + 0.005125 + 0.00525 + 0.005125 + 0.005375 + 0.004875 + 0.0045 + 0.004292 + 0.004208 + 0.003875 + 0.004292 + 0.004375 + 0.004459 + 0.004292 + 0.004125 + 0.004458 + 0.00975 + 0.007042 + 0.006417 + 0.0065 + 0.006459 + 0.006375 + 0.006333 + 0.004875 + 0.003125 + 0.006084 + 0.006125 + 0.006291 + 0.006208 + 0.006375 + 0.005875 + 0.007291 + 0.005292 + 0.00475 + 0.004875 + 0.004875 + 0.005083 + 0.004625 + 0.004875 + 0.004958 + 0.005166 + 0.004708 + 0.003958 + 0.003959 + 0.003667 + 0.003791 + 0.00375 + 0.004083 + 0.004167 + 0.004 + 0.004083 + 0.003792 + 0.004416 + 0.004125 + 0.003625 + 0.003875 + 0.004084 + 0.004458 + 0.004041 + 0.005042 + 0.004666 + 0.004792 + 0.004792 + 0.004833 + 0.004834 + 0.005375 + 0.004709 + 0.004792 + 0.004541 + 0.004458 + 0.004458 + 0.004625 + 0.0045 + 0.004917 + 0.005208 + 0.004792 + 0.005 + 0.004833 + 0.004875 + 0.004833 + 0.005417 + 0.005042 + 0.00525 + 0.005166 + 0.005167 + 0.004542 + 0.004333 + 0.004167 + 0.004416 + 0.00475 + 0.004958 + 0.006625 + 0.007708 + 0.006792 + 0.00625 + 0.006416 + 0.006292 + 0.005125 + 0.005083 + 0.00525 + 0.00425 + 0.003833 + 0.003916 + 0.005708 + 0.005583 + 0.005167 + 0.005291 + 0.009208 + 0.006 + 0.005583 + 0.005333 + 0.004583 + 0.004375 + 0.004041 + 0.003875 + 0.004667 + 0.005125 + 0.005334 + 0.009208 + 0.006917 + 0.006083 + 0.006292 + 0.00625 + 0.006417 + 0.004875 + 0.006666 + 0.005792 + 0.006167 + 0.005709 + 0.006042 + 0.005791 + 0.00575 + 0.005417 + 0.005 + 0.004875 + 0.003583 + 0.003833 + 0.003916 + 0.004125 + 0.003709 + 0.003959 + 0.004 + 0.003584 + 0.003625 + 0.00425 + 0.003917 + 0.003708 + 0.003625 + 0.004083 + 0.004 + 0.00375 + 0.004 + 0.003875 + 0.004041 + 0.004125 + 0.003958 + 0.00375 + 0.003917 + 0.004125 + 0.004417 + 0.004083 + 0.003875 + 0.004042 + 0.003875 + 0.003584 + 0.00375 + 0.003708 + 0.003916 + 0.0035 + 0.003542 + 0.00375 + 0.003458 + 0.003666 + 0.003583 + 0.003542 + 0.003417 + 0.004208 + 0.003875 + 0.003833 + 0.003875 + 0.005 + 0.005625 + 0.005542 + 0.006833 + 0.004667 + 0.004584 + 0.004375 + 0.004625 + 0.004417 + 0.004666 + 0.005584 + 0.00475 + 0.00475 + 0.004666 + 0.00675 + 0.006166 + 0.006375 + 0.005375 + 0.005042 + 0.005 + 0.005125 + 0.005375 + 0.004709 + 0.004459 + 0.003958 + 0.003833 + 0.00425 + 0.004209 + 0.004167 + 0.004125 + 0.00425 + 0.004291 + 0.004292 + 0.005666 + 0.005208 + 0.005542 + 0.006042 + 0.006291 + 0.006208 + 0.006584 + 0.006417 + 0.006916 + 0.006333 + 0.006417 + 0.005583 + 0.006166 + 0.006417 + 0.006167 + 0.006167 + 0.006166 + 0.006375 + 0.006167 + 0.00525 + 0.005375 + 0.004916 + 0.004708 + 0.004917 + 0.005 + 0.004209 + 0.00425 + 0.004125 + 0.004 + 0.00375 + 0.003875 + 0.004417 + 0.003833 + 0.003875 + 0.003667 + 0.004041 + 0.0035 + 0.00375 + 0.003917 + 0.003709 + 0.004083 + 0.00425 + 0.003916 + 0.003792 + 0.004208 + 0.003625 + 0.003959 + 0.004125 + 0.004125 + 0.004084 + 0.004 + 0.003833 + 0.003792 + 0.003791 + 0.003833 + 0.003959 + 0.004083 + 0.003916 + 0.00375 + 0.003625 + 0.003709 + 0.004084 + 0.004 + 0.003375 + 0.004875 + 0.005208 + 0.004917 + 0.005209 + 0.005209 + 0.004666 + 0.004667 + 0.005667 + 0.005834 + 0.005458 + 0.0055 + 0.005167 + 0.00525 + 0.007167 + 0.006166 + 0.006542 + 0.005875 + 0.00625 + 0.006083 + 0.007125 + 0.005208 + 0.004916 + 0.00475 + 0.004125 + 0.003875 + 0.003959 + 0.004083 + 0.003791 + 0.003792 + 0.00375 + 0.004416 + 0.004125 + 0.004458 + 0.004167 + 0.004 + 0.004333 + 0.003958 + 0.004292 + 0.004166 + 0.004292 + 0.006417 + 0.006541 + 0.006625 + 0.006292 + 0.006125 + 0.006125 + 0.007 + 0.005167 + 0.0065 + 0.006167 + 0.006334 + 0.00625 + 0.006458 + 0.006917 + 0.006542 + 0.00725 + 0.005542 + 0.005125 + 0.004917 + 0.004875 + 0.00475 + 0.004875 + 0.011958 + 0.004292 + 0.004208 + 0.004125 + 0.003958 + 0.00425 + 0.010875 + 0.004208 + 0.003917 + 0.003667 + 0.00425 + 0.004125 + 0.004084 + 0.003875 + 0.004416 + 0.0045 + 0.004 + 0.003834 + 0.0045 + 0.003917 + 0.004 + 0.003792 + 0.004416 + 0.004709 + 0.003958 + 0.003833 + 0.005083 + 0.004875 + 0.004334 + 0.004084 + 0.004541 + 0.006542 + 0.003959 + 0.004 + 0.004167 + 0.004208 + 0.004375 + 0.003875 + 0.004291 + 0.004584 + 0.004084 + 0.004875 + 0.005625 + 0.004459 + 0.012792 + 0.004792 + 0.006084 + 0.005375 + 0.005625 + 0.028292 + 0.008541 + 0.012666 + 0.006208 + 0.005875 + 0.006125 + 0.005041 + 0.005708 + 0.007208 + 0.006333 + 0.004875 + 0.005167 + 0.005625 + 0.005292 + 0.004708 + 0.004333 + 0.004333 + 0.00425 + 0.004833 + 0.004834 + 0.004333 + 0.004042 + 0.004667 + 0.004166 + 0.0045 + 0.006125 + 0.005167 + 0.005625 + 0.010958 + 0.006375 + 0.006041 + 0.006958 + 0.006209 + 0.005292 + 0.007 + 0.007 + 0.005583 + 0.005667 + 0.00675 + 0.006958 + 0.0065 + 0.006833 + 0.007041 + 0.006542 + 0.005709 + 0.004625 + 0.003834 + 0.004084 + 0.004166 + 0.00375 + 0.004291 + 0.004084 + 0.00425 + 0.004292 + 0.004166 + 0.004292 + 0.003708 + 0.004334 + 0.004333 + 0.004333 + 0.004125 + 0.003833 + 0.004 + 0.00375 + 0.003542 + 0.004167 + 0.004334 + 0.00375 + 0.00375 + 0.004166 + 0.004208 + 0.003792 + 0.004458 + 0.004208 + 0.003708 + 0.004167 + 0.004375 + 0.004375 + 0.004417 + 0.004417 + 0.0045 + 0.004625 + 0.004917 + 0.005166 + 0.004792 + 0.004959 + 0.005209 + 0.004791 + 0.004583 + 0.004708 + 0.004667 + 0.004667 + 0.004208 + 0.004541 + 0.004292 + 0.003833 + 0.00375 + 0.004084 + 0.004 + 0.004333 + 0.003708 + 0.003917 + 0.003584 + 0.0065 + 0.006292 + 0.015416 + 0.005875 + 0.005708 + 0.006333 + 0.006 + 0.00475 + 0.004 + 0.004542 + 0.00625 + 0.0055 + 0.005833 + 0.005375 + 0.0055 + 0.005041 + 0.00575 + 0.005791 + 0.005458 + 0.004833 + 0.00525 + 0.004958 + 0.005542 + 0.005459 + 0.005292 + 0.005958 + 0.006541 + 0.007917 + 0.006917 + 0.006625 + 0.006292 + 0.006291 + 0.006292 + 0.006666 + 0.00525 + 0.007 + 0.007125 + 0.007 + 0.006542 + 0.005209 + 0.005958 + 0.005875 + 0.005 + 0.00525 + 0.004416 + 0.004625 + 0.004458 + 0.004167 + 0.006166 + 0.003667 + 0.004792 + 0.003792 + 0.004792 + 0.005333 + 0.005875 + 0.005417 + 0.005125 + 0.004292 + 0.0045 + 0.004208 + 0.004083 + 0.004041 + 0.004125 + 0.004417 + 0.004083 + 0.003875 + 0.004209 + 0.004 + 0.004125 + 0.004083 + 0.004125 + 0.004083 + 0.004167 + 0.003833 + 0.004333 + 0.004083 + 0.004125 + 0.003958 + 0.003917 + 0.004542 + 0.004666 + 0.004917 + 0.004959 + 0.005209 + 0.005084 + 0.00575 + 0.005 + 0.004917 + 0.004917 + 0.004833 + 0.006042 + 0.005083 + 0.006167 + 0.008167 + 0.005875 + 0.013708 + 0.008833 + 0.005042 + 0.005041 + 0.004792 + 0.006541 + 0.005042 + 0.005167 + 0.008375 + 0.006166 + 0.00675 + 0.006458 + 0.005666 + 0.005292 + 0.005875 + 0.005459 + 0.005209 + 0.005541 + 0.0055 + 0.005208 + 0.004791 + 0.004542 + 0.00425 + 0.008833 + 0.004667 + 0.005584 + 0.004459 + 0.005042 + 0.005125 + 0.004458 + 0.004667 + 0.0035 + 0.004917 + 0.005334 + 0.004916 + 0.006292 + 0.006292 + 0.006166 + 0.00575 + 0.004833 + 0.005666 + 0.005208 + 0.005917 + 0.006375 + 0.01325 + 0.005916 + 0.005208 + 0.004959 + 0.003875 + 0.004375 + 0.004375 + 0.00425 + 0.004375 + 0.004333 + 0.003833 + 0.005 + 0.004375 + 0.005083 + 0.0055 + 0.0055 + 0.006042 + 0.005333 + 0.005333 + 0.004916 + 0.00425 + 0.004208 + 0.004458 + 0.004083 + 0.004083 + 0.004458 + 0.004084 + 0.004125 + 0.00425 + 0.00425 + 0.00425 + 0.004583 + 0.004958 + 0.005583 + 0.004875 + 0.005458 + 0.005583 + 0.004708 + 0.004667 + 0.004958 + 0.004792 + 0.005625 + 0.005416 + 0.005292 + 0.005417 + 0.005125 + 0.00525 + 0.004916 + 0.005708 + 0.005542 + 0.005042 + 0.005666 + 0.005 + 0.006166 + 0.005875 + 0.005542 + 0.005459 + 0.004459 + 0.0045 + 0.004291 + 0.004834 + 0.005792 + 0.006042 + 0.009625 + 0.007208 + 0.005875 + 0.006875 + 0.006 + 0.005833 + 0.005458 + 0.0055 + 0.004959 + 0.005709 + 0.005167 + 0.006041 + 0.006958 + 0.00575 + 0.005541 + 0.005167 + 0.004875 + 0.005041 + 0.004875 + 0.004667 + 0.003958 + 0.003875 + 0.003708 + 0.004333 + 0.004083 + 0.005166 + 0.0055 + 0.006 + 0.006209 + 0.005917 + 0.006083 + 0.00575 + 0.004667 + 0.004875 + 0.006208 + 0.006583 + 0.014541 + 0.005625 + 0.005583 + 0.005042 + 0.006375 + 0.004459 + 0.00425 + 0.004375 + 0.0045 + 0.004708 + 0.004583 + 0.003833 + 0.004291 + 0.004166 + 0.003542 + 0.003834 + 0.004459 + 0.004542 + 0.004 + 0.004084 + 0.003834 + 0.004125 + 0.003875 + 0.004125 + 0.003667 + 0.003542 + 0.003875 + 0.003792 + 0.003833 + 0.003958 + 0.003667 + 0.003959 + 0.004 + 0.004084 + 0.003667 + 0.004083 + 0.00425 + 0.004125 + 0.004125 + 0.003958 + 0.004 + 0.003875 + 0.003959 + 0.003875 + 0.003917 + 0.003917 + 0.003833 + 0.004125 + 0.004 + 0.003917 + 0.003666 + 0.003958 + 0.003375 + 0.004125 + 0.004834 + 0.004417 + 0.004875 + 0.004417 + 0.004166 + 0.003917 + 0.004125 + 0.004334 + 0.004833 + 0.00525 + 0.006167 + 0.006416 + 0.006417 + 0.006208 + 0.006125 + 0.005542 + 0.004792 + 0.004375 + 0.004542 + 0.004625 + 0.004791 + 0.005458 + 0.004917 + 0.00475 + 0.004791 + 0.005 + 0.004417 + 0.005084 + 0.004959 + 0.0045 + 0.004167 + 0.004583 + 0.004375 + 0.00475 + 0.004458 + 0.005 + 0.00525 + 0.006167 + 0.006709 + 0.006459 + 0.00675 + 0.006166 + 0.00625 + 0.005166 + 0.006542 + 0.0065 + 0.0065 + 0.006625 + 0.006375 + 0.005875 + 0.006167 + 0.00525 + 0.004667 + 0.004292 + 0.004708 + 0.004625 + 0.003959 + 0.003834 + 0.003417 + 0.004333 + 0.004 + 0.003917 + 0.004458 + 0.004417 + 0.004208 + 0.004375 + 0.003709 + 0.003917 + 0.004625 + 0.0055 + 0.005542 + 0.005625 + 0.005834 + 0.004667 + 0.0045 + 0.004625 + 0.004083 + 0.004084 + 0.004417 + 0.004166 + 0.0045 + 0.0045 + 0.004375 + 0.004667 + 0.00375 + 0.00375 + 0.004167 + 0.003917 + 0.003916 + 0.003708 + 0.004208 + 0.004292 + 0.004125 + 0.004125 + 0.005416 + 0.005292 + 0.005291 + 0.005625 + 0.005917 + 0.004875 + 0.005208 + 0.004708 + 0.005 + 0.005958 + 0.004208 + 0.004916 + 0.004792 + 0.004542 + 0.005041 + 0.006083 + 0.007083 + 0.007125 + 0.006875 + 0.008125 + 0.006625 + 0.006708 + 0.006291 + 0.006 + 0.006167 + 0.006042 + 0.004917 + 0.004708 + 0.005041 + 0.004917 + 0.005792 + 0.005 + 0.004875 + 0.004625 + 0.005 + 0.009167 + 0.006125 + 0.004959 + 0.004875 + 0.004958 + 0.00575 + 0.006333 + 0.005375 + 0.005708 + 0.005459 + 0.00725 + 0.006917 + 0.00675 + 0.006292 + 0.0055 + 0.006583 + 0.00525 + 0.00925 + 0.006125 + 0.006375 + 0.006292 + 0.006375 + 0.005375 + 0.004584 + 0.005 + 0.004375 + 0.003916 + 0.003917 + 0.003792 + 0.004916 + 0.0045 + 0.004625 + 0.003709 + 0.004375 + 0.004208 + 0.004042 + 0.004042 + 0.004041 + 0.004125 + 0.003708 + 0.00425 + 0.004291 + 0.004542 + 0.004084 + 0.003917 + 0.003833 + 0.003875 + 0.003834 + 0.003709 + 0.004209 + 0.003917 + 0.003625 + 0.004 + 0.004125 + 0.004125 + 0.004208 + 0.004042 + 0.004209 + 0.00375 + 0.004083 + 0.003916 + 0.004334 + 0.00425 + 0.00425 + 0.005291 + 0.00525 + 0.005583 + 0.005792 + 0.005542 + 0.004792 + 0.005125 + 0.0055 + 0.005125 + 0.005333 + 0.005291 + 0.012458 + 0.005917 + 0.0055 + 0.004916 + 0.004917 + 0.006042 + 0.005125 + 0.005667 + 0.006291 + 0.007084 + 0.007 + 0.007125 + 0.006166 + 0.005708 + 0.005375 + 0.005375 + 0.005542 + 0.005666 + 0.005875 + 0.006208 + 0.005875 + 0.006166 + 0.005417 + 0.005375 + 0.005416 + 0.006542 + 0.006292 + 0.006708 + 0.005917 + 0.005084 + 0.005459 + 0.005167 + 0.00725 + 0.007042 + 0.006666 + 0.006791 + 0.008167 + 0.007334 + 0.007125 + 0.006208 + 0.007542 + 0.007 + 0.007167 + 0.105333 + 0.021708 + 0.0065 + 0.006167 + 0.005584 + 0.005042 + 0.004083 + 0.004125 + 0.004416 + 0.004167 + 0.004125 + 0.004125 + 0.006125 + 0.004208 + 0.00425 + 0.004875 + 0.005709 + 0.004791 + 0.004709 + 0.004709 + 0.004375 + 0.00425 + 0.004041 + 0.004042 + 0.004666 + 0.004167 + 0.00475 + 0.004333 + 0.0045 + 0.004042 + 0.004209 + 0.004041 + 0.003917 + 0.004125 + 0.018 + 0.004208 + 0.004417 + 0.004375 + 0.004167 + 0.004375 + 0.00425 + 0.004292 + 0.004333 + 0.004417 + 0.004333 + 0.004375 + 0.004625 + 0.004375 + 0.004125 + 0.004583 + 0.004042 + 0.004291 + 0.004542 + 0.005458 + 0.004583 + 0.004417 + 0.004458 + 0.007 + 0.005084 + 0.005334 + 0.004416 + 0.004084 + 0.006875 + 0.004958 + 0.005042 + 0.00575 + 0.008292 + 0.006792 + 0.00575 + 0.005666 + 0.005792 + 0.006125 + 0.004792 + 0.004166 + 0.004583 + 0.004709 + 0.004334 + 0.004459 + 0.004916 + 0.005209 + 0.005292 + 0.004541 + 0.004625 + 0.004459 + 0.004541 + 0.020375 + 0.0055 + 0.006042 + 0.004625 + 0.007375 + 0.005833 + 0.008958 + 0.005459 + 0.010541 + 0.008209 + 0.005458 + 0.005333 + 0.005417 + 0.007041 + 0.008417 + 0.007916 + 0.016875 + 0.009375 + 0.006917 + 0.007167 + 0.00675 + 0.006459 + 0.0055 + 0.006166 + 0.005459 + 0.004834 + 0.004791 + 0.005292 + 0.005291 + 0.005208 + 0.005833 + 0.005417 + 0.005041 + 0.006708 + 0.005208 + 0.00525 + 0.004833 + 0.005416 + 0.006375 + 0.005 + 0.005375 + 0.005125 + 0.004792 + 0.004542 + 0.004167 + 0.004375 + 0.003917 + 0.004125 + 0.003833 + 0.011375 + 0.004667 + 0.004667 + 0.004291 + 0.004083 + 0.004584 + 0.004292 + 0.004333 + 0.004333 + 0.004375 + 0.004333 + 0.004042 + 0.00425 + 0.004291 + 0.004209 + 0.004584 + 0.004542 + 0.004958 + 0.00425 + 0.004625 + 0.004708 + 0.004333 + 0.004333 + 0.004417 + 0.004916 + 0.004667 + 0.004625 + 0.004209 + 0.005459 + 0.008458 + 0.005416 + 0.006125 + 0.005458 + 0.005583 + 0.004916 + 0.00525 + 0.004458 + 0.003833 + 0.007208 + 0.004666 + 0.005042 + 0.00475 + 0.004417 + 0.004583 + 0.004417 + 0.004875 + 0.004459 + 0.00525 + 0.004916 + 0.004833 + 0.004542 + 0.004125 + 0.004917 + 0.003958 + 0.005 + 0.005417 + 0.008042 + 0.006333 + 0.005708 + 0.004708 + 0.005125 + 0.0045 + 0.005041 + 0.0045 + 0.007917 + 0.005084 + 0.005584 + 0.005208 + 0.00525 + 0.005042 + 0.005041 + 0.004458 + 0.004459 + 0.00425 + 0.003875 + 0.004291 + 0.004125 + 0.003792 + 0.004375 + 0.004 + 0.00475 + 0.004042 + 0.00525 + 0.004584 + 0.004042 + 0.004291 + 0.004084 + 0.0045 + 0.004125 + 0.004708 + 0.004292 + 0.004458 + 0.004208 + 0.00425 + 0.003958 + 0.004542 + 0.00425 + 0.0045 + 0.004375 + 0.004375 + 0.004042 + 0.004375 + 0.004 + 0.003708 + 0.004167 + 0.004166 + 0.004458 + 0.003958 + 0.004 + 0.003834 + 0.004292 + 0.004083 + 0.004333 + 0.004208 + 0.004334 + 0.004 + 0.003709 + 0.00425 + 0.004167 + 0.003542 + 0.004458 + 0.010625 + 0.004959 + 0.004666 + 0.004375 + 0.004458 + 0.004458 + 0.004083 + 0.004917 + 0.005208 + 0.00525 + 0.005291 + 0.007667 + 0.005416 + 0.004875 + 0.005 + 0.004542 + 0.004209 + 0.004417 + 0.004417 + 0.004125 + 0.004334 + 0.004333 + 0.004666 + 0.004833 + 0.004583 + 0.00425 + 0.00425 + 0.004375 + 0.006792 + 0.004375 + 0.004958 + 0.004667 + 0.00475 + 0.004584 + 0.005125 + 0.005292 + 0.005 + 0.00475 + 0.007333 + 0.005333 + 0.005042 + 0.004625 + 0.004833 + 0.005417 + 0.004875 + 0.004917 + 0.007375 + 0.005333 + 0.005417 + 0.005 + 0.004459 + 0.004458 + 0.00425 + 0.00425 + 0.004875 + 0.004291 + 0.004125 + 0.004542 + 0.004083 + 0.004042 + 0.006583 + 0.012541 + 0.004375 + 0.004 + 0.003916 + 0.003833 + 0.003917 + 0.004 + 0.004291 + 0.004708 + 0.004125 + 0.004166 + 0.004333 + 0.004042 + 0.00425 + 0.004 + 0.004333 + 0.004125 + 0.004416 + 0.004083 + 0.004458 + 0.003875 + 0.003958 + 0.004083 + 0.004042 + 0.004166 + 0.003875 + 0.004083 + 0.004417 + 0.004083 + 0.011791 + 0.004167 + 0.004791 + 0.004208 + 0.003667 + 0.003625 + 0.004292 + 0.00425 + 0.004292 + 0.004375 + 0.0045 + 0.0045 + 0.00425 + 0.004334 + 0.004416 + 0.004083 + 0.004333 + 0.004375 + 0.008791 + 0.005375 + 0.005334 + 0.004709 + 0.012791 + 0.005375 + 0.00525 + 0.004417 + 0.004375 + 0.004291 + 0.007209 + 0.004791 + 0.004625 + 0.004292 + 0.004125 + 0.004458 + 0.004417 + 0.003833 + 0.004458 + 0.004375 + 0.004583 + 0.00425 + 0.004333 + 0.004584 + 0.004334 + 0.004292 + 0.00375 + 0.005625 + 0.007792 + 0.005417 + 0.009209 + 0.005166 + 0.00475 + 0.004958 + 0.004333 + 0.005333 + 0.008208 + 0.005083 + 0.005125 + 0.00475 + 0.005083 + 0.004541 + 0.004375 + 0.003833 + 0.006958 + 0.003625 + 0.003709 + 0.003709 + 0.003875 + 0.003584 + 0.004125 + 0.003584 + 0.003666 + 0.003708 + 0.004167 + 0.003791 + 0.003833 + 0.003917 + 0.004166 + 0.003708 + 0.003792 + 0.003959 + 0.003667 + 0.004167 + 0.003833 + 0.004166 + 0.004208 + 0.00375 + 0.003833 + 0.003791 + 0.003708 + 0.003917 + 0.003875 + 0.003875 + 0.003459 + 0.003792 + 0.0035 + 0.0035 + 0.003292 + 0.003541 + 0.004042 + 0.003625 + 0.003833 + 0.003667 + 0.003791 + 0.003792 + 0.004042 + 0.003875 + 0.00425 + 0.003375 + 0.004 + 0.009333 + 0.007458 + 0.004375 + 0.004458 + 0.004333 + 0.004125 + 0.004 + 0.004166 + 0.011625 + 0.005417 + 0.004875 + 0.004959 + 0.006584 + 0.004459 + 0.005667 + 0.004292 + 0.004625 + 0.004333 + 0.003708 + 0.004292 + 0.004292 + 0.003833 + 0.003833 + 0.00425 + 0.004208 + 0.006666 + 0.004917 + 0.004584 + 0.004541 + 0.004125 + 0.004209 + 0.004208 + 0.004458 + 0.004041 + 0.003917 + 0.004041 + 0.005292 + 0.00475 + 0.005583 + 0.007541 + 0.005334 + 0.004708 + 0.004458 + 0.0045 + 0.004792 + 0.004459 + 0.00425 + 0.007 + 0.00525 + 0.005291 + 0.004542 + 0.004875 + 0.004083 + 0.003792 + 0.00375 + 0.003875 + 0.003666 + 0.004042 + 0.003833 + 0.003708 + 0.004833 + 0.003667 + 0.004291 + 0.004125 + 0.003583 + 0.004042 + 0.003958 + 0.003875 + 0.004 + 0.004 + 0.004042 + 0.003917 + 0.003917 + 0.003792 + 0.004125 + 0.003917 + 0.004 + 0.003916 + 0.003875 + 0.003917 + 0.004166 + 0.003792 + 0.003958 + 0.003708 + 0.003541 + 0.003542 + 0.003542 + 0.003666 + 0.00325 + 0.003542 + 0.003834 + 0.003958 + 0.003875 + 0.004166 + 0.003792 + 0.00375 + 0.003834 + 0.004041 + 0.003625 + 0.003792 + 0.004 + 0.004167 + 0.003916 + 0.004125 + 0.004042 + 0.004209 + 0.004125 + 0.007167 + 0.004208 + 0.005375 + 0.004791 + 0.005125 + 0.005125 + 0.004916 + 0.007625 + 0.00475 + 0.004917 + 0.004291 + 0.003334 + 0.003959 + 0.00375 + 0.004209 + 0.004167 + 0.003791 + 0.004209 + 0.004333 + 0.004625 + 0.01 + 0.004416 + 0.004292 + 0.00425 + 0.00425 + 0.00475 + 0.006458 + 0.004041 + 0.004208 + 0.00525 + 0.009375 + 0.005667 + 0.004459 + 0.004084 + 0.003959 + 0.004 + 0.004084 + 0.003709 + 0.007125 + 0.004834 + 0.004541 + 0.004125 + 0.003833 + 0.004541 + 0.004542 + 0.003583 + 0.003292 + 0.003084 + 0.003125 + 0.002917 + 0.003166 + 0.003584 + 0.003375 + 0.003875 + 0.00325 + 0.002833 + 0.003458 + 0.003667 + 0.003292 + 0.003208 + 0.003875 + 0.004 + 0.003333 + 0.003333 + 0.003084 + 0.002916 + 0.003209 + 0.003042 + 0.003541 + 0.003417 + 0.003042 + 0.002917 + 0.003083 + 0.003459 + 0.003417 + 0.003167 + 0.003542 + 0.003083 + 0.003208 + 0.003083 + 0.003292 + 0.003375 + 0.002959 + 0.003209 + 0.003041 + 0.003167 + 0.004166 + 0.003542 + 0.003667 + 0.003667 + 0.003166 + 0.003042 + 0.003084 + 0.003834 + 0.003709 + 0.005958 + 0.003792 + 0.003458 + 0.0035 + 0.003333 + 0.003417 + 0.003375 + 0.003959 + 0.00475 + 0.004209 + 0.004209 + 0.006959 + 0.004334 + 0.00475 + 0.004209 + 0.004 + 0.004417 + 0.003333 + 0.003666 + 0.003042 + 0.003416 + 0.003959 + 0.004166 + 0.004292 + 0.003708 + 0.003541 + 0.003125 + 0.003541 + 0.0035 + 0.006375 + 0.005 + 0.005 + 0.004458 + 0.006792 + 0.00525 + 0.00525 + 0.005084 + 0.005084 + 0.00775 + 0.005333 + 0.005292 + 0.004959 + 0.00425 + 0.005167 + 0.005208 + 0.005083 + 0.007708 + 0.005375 + 0.0055 + 0.004625 + 0.003791 + 0.004167 + 0.003958 + 0.004458 + 0.004167 + 0.004 + 0.004083 + 0.0045 + 0.004541 + 0.003667 + 0.003833 + 0.004708 + 0.003958 + 0.004333 + 0.003833 + 0.003875 + 0.003958 + 0.005042 + 0.006041 + 0.006083 + 0.007292 + 0.006625 + 0.005917 + 0.00675 + 0.006 + 0.005708 + 0.006 + 0.005416 + 0.006667 + 0.006625 + 0.006041 + 0.005209 + 0.00475 + 0.00525 + 0.004667 + 0.004166 + 0.005167 + 0.004584 + 0.005291 + 0.0055 + 0.00525 + 0.005583 + 0.005292 + 0.005667 + 0.006583 + 0.006208 + 0.005834 + 0.00475 + 0.005417 + 0.005 + 0.004584 + 0.005125 + 0.00525 + 0.005458 + 0.004459 + 0.004334 + 0.004542 + 0.004666 + 0.008459 + 0.005458 + 0.006125 + 0.005625 + 0.005333 + 0.005125 + 0.00475 + 0.004291 + 0.004708 + 0.004584 + 0.008375 + 0.005167 + 0.005041 + 0.004834 + 0.0045 + 0.004375 + 0.00475 + 0.004208 + 0.004833 + 0.004958 + 0.0045 + 0.004208 + 0.004375 + 0.004542 + 0.008625 + 0.004292 + 0.0045 + 0.005959 + 0.008542 + 0.005416 + 0.005459 + 0.005459 + 0.005083 + 0.005541 + 0.00475 + 0.0055 + 0.0085 + 0.005459 + 0.005542 + 0.004792 + 0.004958 + 0.005458 + 0.005167 + 0.004291 + 0.004375 + 0.004292 + 0.004209 + 0.007834 + 0.004167 + 0.004333 + 0.004208 + 0.004292 + 0.003875 + 0.004083 + 0.004958 + 0.0045 + 0.004458 + 0.003959 + 0.00375 + 0.004125 + 0.004625 + 0.004458 + 0.004083 + 0.003875 + 0.004292 + 0.004375 + 0.004791 + 0.004625 + 0.0045 + 0.004417 + 0.004459 + 0.004291 + 0.004083 + 0.004125 + 0.004 + 0.00875 + 0.004417 + 0.004375 + 0.004 + 0.00425 + 0.004208 + 0.003875 + 0.003834 + 0.004666 + 0.004333 + 0.003917 + 0.004208 + 0.004334 + 0.004291 + 0.004667 + 0.004708 + 0.004584 + 0.010959 + 0.004792 + 0.004666 + 0.004292 + 0.004584 + 0.004334 + 0.00425 + 0.004583 + 0.005209 + 0.005167 + 0.005208 + 0.007458 + 0.005125 + 0.005625 + 0.0055 + 0.014583 + 0.004292 + 0.0045 + 0.004375 + 0.004834 + 0.004625 + 0.004 + 0.005042 + 0.0045 + 0.00675 + 0.005 + 0.004792 + 0.0045 + 0.004209 + 0.004709 + 0.004458 + 0.005125 + 0.004166 + 0.004542 + 0.00425 + 0.0055 + 0.004708 + 0.005208 + 0.0085 + 0.005208 + 0.005208 + 0.005125 + 0.004167 + 0.004333 + 0.005125 + 0.005125 + 0.007917 + 0.005708 + 0.005292 + 0.004958 + 0.005 + 0.004208 + 0.004625 + 0.004166 + 0.003917 + 0.00425 + 0.004375 + 0.004209 + 0.003875 + 0.004792 + 0.003583 + 0.004333 + 0.004458 + 0.00425 + 0.004291 + 0.004458 + 0.003833 + 0.00375 + 0.004125 + 0.004042 + 0.003667 + 0.003667 + 0.003958 + 0.003833 + 0.003708 + 0.00425 + 0.004291 + 0.004334 + 0.004041 + 0.00375 + 0.004042 + 0.004292 + 0.004041 + 0.004209 + 0.0045 + 0.003875 + 0.003875 + 0.003917 + 0.004167 + 0.004125 + 0.003875 + 0.00425 + 0.004333 + 0.003792 + 0.007958 + 0.004042 + 0.004125 + 0.003917 + 0.004166 + 0.004417 + 0.004208 + 0.004041 + 0.0045 + 0.00425 + 0.004083 + 0.003958 + 0.006875 + 0.004791 + 0.004625 + 0.005333 + 0.005 + 0.004958 + 0.005209 + 0.007792 + 0.005292 + 0.005375 + 0.004292 + 0.003667 + 0.004958 + 0.004083 + 0.004083 + 0.004291 + 0.004083 + 0.004125 + 0.004125 + 0.004709 + 0.004417 + 0.004625 + 0.004541 + 0.004417 + 0.004458 + 0.004166 + 0.004292 + 0.004541 + 0.004584 + 0.010792 + 0.005 + 0.005458 + 0.005208 + 0.005125 + 0.004791 + 0.005042 + 0.0115 + 0.004167 + 0.0055 + 0.004833 + 0.005125 + 0.004583 + 0.004958 + 0.011625 + 0.00475 + 0.004167 + 0.003959 + 0.004333 + 0.0035 + 0.003875 + 0.004125 + 0.003708 + 0.003292 + 0.0035 + 0.004208 + 0.003459 + 0.003833 + 0.004041 + 0.003834 + 0.003958 + 0.0035 + 0.0035 + 0.003459 + 0.003583 + 0.003459 + 0.003875 + 0.003833 + 0.00375 + 0.003875 + 0.003625 + 0.016125 + 0.004125 + 0.003708 + 0.003834 + 0.003834 + 0.004042 + 0.003625 + 0.003667 + 0.003916 + 0.003584 + 0.004334 + 0.003709 + 0.003875 + 0.004042 + 0.003959 + 0.003584 + 0.003958 + 0.003708 + 0.00375 + 0.003083 + 0.003458 + 0.003417 + 0.003416 + 0.003917 + 0.007125 + 0.004042 + 0.004167 + 0.003958 + 0.004292 + 0.004083 + 0.003958 + 0.004166 + 0.004292 + 0.005209 + 0.004625 + 0.007792 + 0.004917 + 0.005417 + 0.004542 + 0.004875 + 0.003792 + 0.003875 + 0.004084 + 0.004333 + 0.003958 + 0.003958 + 0.004042 + 0.004125 + 0.004084 + 0.004333 + 0.006125 + 0.00375 + 0.004125 + 0.004042 + 0.004083 + 0.004125 + 0.004667 + 0.00425 + 0.004834 + 0.00475 + 0.004875 + 0.004708 + 0.007417 + 0.00525 + 0.00575 + 0.00475 + 0.005083 + 0.003792 + 0.005125 + 0.004708 + 0.007875 + 0.005208 + 0.005042 + 0.00475 + 0.005166 + 0.003709 + 0.003917 + 0.004 + 0.003917 + 0.003958 + 0.003458 + 0.004083 + 0.004625 + 0.004208 + 0.003583 + 0.003584 + 0.00425 + 0.004416 + 0.003875 + 0.003875 + 0.00375 + 0.003625 + 0.003583 + 0.003792 + 0.00325 + 0.00325 + 0.00325 + 0.003959 + 0.003875 + 0.003833 + 0.004042 + 0.003834 + 0.003709 + 0.003959 + 0.004084 + 0.003791 + 0.003667 + 0.003917 + 0.003958 + 0.003583 + 0.003709 + 0.003834 + 0.003917 + 0.003416 + 0.003834 + 0.003709 + 0.003959 + 0.003709 + 0.003541 + 0.0035 + 0.0035 + 0.00325 + 0.003459 + 0.004125 + 0.004458 + 0.003416 + 0.003708 + 0.004 + 0.008708 + 0.004166 + 0.004375 + 0.004375 + 0.004042 + 0.004958 + 0.004791 + 0.004833 + 0.004791 + 0.007292 + 0.005041 + 0.0055 + 0.003917 + 0.003709 + 0.004125 + 0.004333 + 0.004041 + 0.004 + 0.0045 + 0.00425 + 0.003542 + 0.004208 + 0.004542 + 0.003583 + 0.003625 + 0.003958 + 0.003875 + 0.003916 + 0.006167 + 0.004375 + 0.0045 + 0.003875 + 0.005167 + 0.00475 + 0.005041 + 0.004625 + 0.004833 + 0.007166 + 0.005125 + 0.00425 + 0.005042 + 0.005042 + 0.004625 + 0.004833 + 0.004875 + 0.007167 + 0.005125 + 0.005125 + 0.00425 + 0.004167 + 0.003917 + 0.004 + 0.003833 + 0.004 + 0.003708 + 0.00425 + 0.003458 + 0.003958 + 0.003792 + 0.003583 + 0.003833 + 0.003792 + 0.003625 + 0.00375 + 0.003667 + 0.003542 + 0.0035 + 0.003708 + 0.003291 + 0.0035 + 0.003375 + 0.003333 + 0.003833 + 0.00375 + 0.003917 + 0.0035 + 0.004042 + 0.004166 + 0.003959 + 0.004042 + 0.003708 + 0.004291 + 0.004167 + 0.003875 + 0.003584 + 0.00375 + 0.00375 + 0.0035 + 0.004 + 0.003917 + 0.004 + 0.003583 + 0.003542 + 0.003459 + 0.003458 + 0.00325 + 0.004125 + 0.00375 + 0.003583 + 0.003875 + 0.004041 + 0.004 + 0.003916 + 0.004125 + 0.003958 + 0.005083 + 0.007125 + 0.005334 + 0.005417 + 0.004625 + 0.004709 + 0.005042 + 0.004792 + 0.004291 + 0.003833 + 0.006667 + 0.004417 + 0.004125 + 0.003875 + 0.00425 + 0.004083 + 0.003958 + 0.0045 + 0.003916 + 0.00375 + 0.003583 + 0.003959 + 0.003875 + 0.003917 + 0.00425 + 0.004041 + 0.003917 + 0.00475 + 0.007458 + 0.005709 + 0.005334 + 0.004833 + 0.004333 + 0.005083 + 0.004166 + 0.003833 + 0.00525 + 0.007292 + 0.004709 + 0.005542 + 0.004583 + 0.004792 + 0.004875 + 0.004042 + 0.003667 + 0.004875 + 0.00375 + 0.003708 + 0.003958 + 0.003709 + 0.003583 + 0.00425 + 0.003375 + 0.008416 + 0.003708 + 0.004125 + 0.004167 + 0.003708 + 0.003958 + 0.004042 + 0.003958 + 0.003375 + 0.003708 + 0.003375 + 0.003625 + 0.00375 + 0.003542 + 0.003667 + 0.00375 + 0.003625 + 0.003542 + 0.003834 + 0.003459 + 0.003917 + 0.003583 + 0.003833 + 0.003583 + 0.003792 + 0.003916 + 0.003875 + 0.003583 + 0.004208 + 0.003958 + 0.003708 + 0.003625 + 0.004 + 0.004084 + 0.003833 + 0.00375 + 0.003541 + 0.003583 + 0.004125 + 0.003667 + 0.00425 + 0.004 + 0.01125 + 0.003625 + 0.004042 + 0.0045 + 0.004 + 0.003708 + 0.005042 + 0.005125 + 0.004959 + 0.005083 + 0.007541 + 0.005625 + 0.005125 + 0.004125 + 0.00425 + 0.004708 + 0.003792 + 0.004292 + 0.00425 + 0.004041 + 0.004167 + 0.004084 + 0.004125 + 0.003917 + 0.003583 + 0.004 + 0.003875 + 0.003708 + 0.003583 + 0.00625 + 0.004375 + 0.004166 + 0.005208 + 0.004625 + 0.004875 + 0.004792 + 0.004541 + 0.007125 + 0.005333 + 0.005041 + 0.0045 + 0.005208 + 0.005 + 0.004959 + 0.004709 + 0.006917 + 0.005083 + 0.004792 + 0.003708 + 0.003541 + 0.003833 + 0.003375 + 0.003333 + 0.00375 + 0.003625 + 0.003666 + 0.004375 + 0.003583 + 0.003792 + 0.003958 + 0.004167 + 0.004125 + 0.003916 + 0.003792 + 0.003833 + 0.004292 + 0.003709 + 0.004209 + 0.003417 + 0.003625 + 0.003583 + 0.003375 + 0.0035 + 0.003542 + 0.003834 + 0.003666 + 0.003958 + 0.003667 + 0.004209 + 0.003833 + 0.00375 + 0.003666 + 0.003917 + 0.003667 + 0.003834 + 0.003875 + 0.003833 + 0.003958 + 0.004 + 0.004167 + 0.008291 + 0.003792 + 0.003875 + 0.003792 + 0.003917 + 0.003708 + 0.0045 + 0.004041 + 0.00375 + 0.004 + 0.003875 + 0.00375 + 0.003417 + 0.00375 + 0.003917 + 0.00475 + 0.005208 + 0.007958 + 0.005083 + 0.005458 + 0.0045 + 0.015791 + 0.009292 + 0.004333 + 0.004083 + 0.004333 + 0.0045 + 0.004292 + 0.004208 + 0.008542 + 0.004417 + 0.004791 + 0.003917 + 0.004416 + 0.004167 + 0.004333 + 0.004125 + 0.004333 + 0.003833 + 0.004667 + 0.0045 + 0.004791 + 0.003667 + 0.005542 + 0.005458 + 0.008541 + 0.005417 + 0.034708 + 0.027667 + 0.006083 + 0.005958 + 0.008 + 0.023917 + 0.016125 + 0.0085 + 0.007583 + 0.00775 + 0.007958 + 0.007083 + 0.006292 + 0.004875 + 0.005167 + 0.006125 + 0.005083 + 0.00425 + 0.005125 + 0.006792 + 0.009084 + 0.004833 + 0.005625 + 0.007542 + 0.00475 + 0.00525 + 0.005917 + 0.005042 + 0.004709 + 0.004583 + 0.00475 + 0.00425 + 0.00475 + 0.004458 + 0.0045 + 0.004583 + 0.004625 + 0.004541 + 0.004417 + 0.0045 + 0.006083 + 0.005958 + 0.006042 + 0.006042 + 0.005375 + 0.005958 + 0.00575 + 0.005916 + 0.006042 + 0.005542 + 0.005583 + 0.005792 + 0.005166 + 0.005167 + 0.004792 + 0.004417 + 0.004167 + 0.005 + 0.005291 + 0.005375 + 0.006208 + 0.008625 + 0.005833 + 0.005208 + 0.005792 + 0.005375 + 0.0055 + 0.004791 + 0.004125 + 0.006416 + 0.005334 + 0.005291 + 0.007209 + 0.006459 + 0.007042 + 0.006583 + 0.006125 + 0.005875 + 0.005917 + 0.005208 + 0.005 + 0.004875 + 0.005625 + 0.006125 + 0.006209 + 0.005584 + 0.006792 + 0.006 + 0.004709 + 0.00475 + 0.005375 + 0.004833 + 0.004458 + 0.0045 + 0.005625 + 0.004709 + 0.005792 + 0.005875 + 0.009583 + 0.006541 + 0.006375 + 0.00625 + 0.006459 + 0.004459 + 0.005167 + 0.004833 + 0.006333 + 0.01575 + 0.00675 + 0.005916 + 0.006333 + 0.006 + 0.004958 + 0.00425 + 0.00425 + 0.004792 + 0.004292 + 0.0045 + 0.004042 + 0.004292 + 0.005125 + 0.004042 + 0.00475 + 0.005709 + 0.005542 + 0.004833 + 0.005666 + 0.0055 + 0.005458 + 0.00525 + 0.005125 + 0.00475 + 0.003875 + 0.004 + 0.0045 + 0.004583 + 0.004333 + 0.00475 + 0.004 + 0.0045 + 0.004125 + 0.004166 + 0.004792 + 0.00475 + 0.005666 + 0.005292 + 0.005209 + 0.005625 + 0.005167 + 0.006292 + 0.005125 + 0.005 + 0.004833 + 0.0045 + 0.005375 + 0.005375 + 0.005333 + 0.005625 + 0.006 + 0.005292 + 0.005375 + 0.006834 + 0.005792 + 0.005458 + 0.005833 + 0.005958 + 0.00575 + 0.005916 + 0.006 + 0.006083 + 0.006333 + 0.005166 + 0.007334 + 0.006834 + 0.007084 + 0.006834 + 0.006541 + 0.005792 + 0.005167 + 0.00575 + 0.006 + 0.005917 + 0.005916 + 0.007667 + 0.005959 + 0.006125 + 0.005375 + 0.005667 + 0.005666 + 0.006125 + 0.006583 + 0.006333 + 0.005792 + 0.005541 + 0.005959 + 0.005917 + 0.005292 + 0.006 + 0.006292 + 0.012666 + 0.006833 + 0.006542 + 0.006459 + 0.006292 + 0.0055 + 0.007125 + 0.008375 + 0.00625 + 0.006458 + 0.005917 + 0.006334 + 0.006166 + 0.005875 + 0.0055 + 0.005167 + 0.005083 + 0.004875 + 0.005208 + 0.004416 + 0.003583 + 0.003875 + 0.004416 + 0.004334 + 0.004417 + 0.004916 + 0.004833 + 0.0045 + 0.004083 + 0.004208 + 0.004708 + 0.005459 + 0.005583 + 0.006041 + 0.005708 + 0.005083 + 0.004542 + 0.006625 + 0.004959 + 0.005208 + 0.00575 + 0.005833 + 0.005958 + 0.005583 + 0.00575 + 0.005833 + 0.005667 + 0.005958 + 0.005667 + 0.005209 + 0.004541 + 0.004875 + 0.005458 + 0.004541 + 0.004167 + 0.004667 + 0.004834 + 0.005 + 0.004709 + 0.004125 + 0.004041 + 0.004 + 0.009208 + 0.006042 + 0.005959 + 0.00575 + 0.005208 + 0.004917 + 0.004916 + 0.00475 + 0.00475 + 0.006083 + 0.005958 + 0.005041 + 0.006333 + 0.006417 + 0.007166 + 0.006542 + 0.005666 + 0.004958 + 0.005875 + 0.004917 + 0.004584 + 0.005209 + 0.004709 + 0.005042 + 0.005541 + 0.005542 + 0.005709 + 0.005875 + 0.005375 + 0.005291 + 0.005792 + 0.005166 + 0.005042 + 0.004583 + 0.00425 + 0.005166 + 0.008417 + 0.005459 + 0.006875 + 0.007 + 0.005958 + 0.00625 + 0.005541 + 0.006 + 0.006959 + 0.007792 + 0.006916 + 0.006667 + 0.006459 + 0.007084 + 0.005625 + 0.005125 + 0.004916 + 0.005083 + 0.004375 + 0.004458 + 0.004125 + 0.003917 + 0.00475 + 0.005209 + 0.003833 + 0.003792 + 0.004167 + 0.010375 + 0.00425 + 0.003958 + 0.004458 + 0.004292 + 0.004166 + 0.004125 + 0.004458 + 0.00875 + 0.00425 + 0.003916 + 0.004125 + 0.004292 + 0.00425 + 0.004291 + 0.004 + 0.004417 + 0.003916 + 0.004542 + 0.004416 + 0.00425 + 0.003875 + 0.004542 + 0.003958 + 0.0045 + 0.003958 + 0.004125 + 0.004209 + 0.004208 + 0.004333 + 0.004125 + 0.004208 + 0.004458 + 0.003625 + 0.003916 + 0.003958 + 0.004208 + 0.004083 + 0.008708 + 0.005375 + 0.004958 + 0.005083 + 0.004958 + 0.005542 + 0.004417 + 0.004708 + 0.0055 + 0.00525 + 0.00475 + 0.006 + 0.006125 + 0.00625 + 0.006208 + 0.005417 + 0.005042 + 0.004708 + 0.004958 + 0.005125 + 0.005292 + 0.00475 + 0.004375 + 0.004375 + 0.004834 + 0.004334 + 0.011541 + 0.005875 + 0.008416 + 0.006583 + 0.006083 + 0.006708 + 0.00575 + 0.004875 + 0.005208 + 0.006916 + 0.006417 + 0.005542 + 0.006333 + 0.007041 + 0.007 + 0.006584 + 0.006459 + 0.006584 + 0.0065 + 0.057291 + 0.01025 + 0.007167 + 0.006291 + 0.007375 + 0.00725 + 0.005375 + 0.011666 + 0.006125 + 0.005875 + 0.005833 + 0.005458 + 0.00525 + 0.00625 + 0.005292 + 0.005334 + 0.005 + 0.004917 + 0.015709 + 0.007083 + 0.005292 + 0.004875 + 0.004709 + 0.004667 + 0.004625 + 0.005125 + 0.004791 + 0.005167 + 0.005042 + 0.005542 + 0.005 + 0.005083 + 0.005209 + 0.004959 + 0.005584 + 0.006125 + 0.005875 + 0.006208 + 0.005625 + 0.004917 + 0.005959 + 0.004917 + 0.004166 + 0.004459 + 0.003959 + 0.004459 + 0.004291 + 0.003959 + 0.004583 + 0.004417 + 0.004 + 0.003917 + 0.004167 + 0.004208 + 0.0045 + 0.003959 + 0.004459 + 0.004333 + 0.009416 + 0.005417 + 0.005416 + 0.006208 + 0.005917 + 0.00725 + 0.006334 + 0.006375 + 0.005333 + 0.005833 + 0.007292 + 0.006875 + 0.006333 + 0.005708 + 0.006 + 0.005625 + 0.005625 + 0.004709 + 0.004417 + 0.00425 + 0.004416 + 0.004792 + 0.004291 + 0.004708 + 0.004 + 0.005708 + 0.006333 + 0.005625 + 0.0055 + 0.005667 + 0.005208 + 0.005 + 0.007209 + 0.0065 + 0.004583 + 0.005125 + 0.006416 + 0.006625 + 0.006167 + 0.005583 + 0.006375 + 0.006375 + 0.006417 + 0.007875 + 0.006917 + 0.006625 + 0.006125 + 0.006084 + 0.0055 + 0.005125 + 0.005166 + 0.005167 + 0.004541 + 0.004459 + 0.004291 + 0.0045 + 0.00425 + 0.00425 + 0.003875 + 0.0045 + 0.004041 + 0.003959 + 0.004417 + 0.003875 + 0.003875 + 0.004125 + 0.004292 + 0.004 + 0.003958 + 0.004167 + 0.004083 + 0.004208 + 0.004042 + 0.004334 + 0.004041 + 0.00425 + 0.003875 + 0.004375 + 0.004292 + 0.004041 + 0.003709 + 0.003959 + 0.003875 + 0.003667 + 0.003834 + 0.003959 + 0.003959 + 0.004 + 0.004333 + 0.004125 + 0.004458 + 0.003791 + 0.004125 + 0.003958 + 0.00425 + 0.004083 + 0.004375 + 0.004041 + 0.011 + 0.004333 + 0.006166 + 0.005542 + 0.005666 + 0.005375 + 0.005416 + 0.006292 + 0.006125 + 0.0065 + 0.00625 + 0.008208 + 0.006833 + 0.006625 + 0.006583 + 0.005541 + 0.00475 + 0.005958 + 0.005417 + 0.005334 + 0.005209 + 0.006542 + 0.005833 + 0.005666 + 0.005417 + 0.014625 + 0.012834 + 0.01925 + 0.0065 + 0.018667 + 0.005958 + 0.005375 + 0.005584 + 0.005667 + 0.008167 + 0.00775 + 0.0075 + 0.006292 + 0.005959 + 0.01075 + 0.007417 + 0.015625 + 0.006042 + 0.006792 + 0.015791 + 0.010042 + 0.007292 + 0.009041 + 0.007583 + 0.038667 + 0.023666 + 0.005084 + 0.004875 + 0.0045 + 0.00475 + 0.004459 + 0.0045 + 0.005917 + 0.0045 + 0.0045 + 0.004667 + 0.004875 + 0.005833 + 0.004708 + 0.004416 + 0.004792 + 0.004458 + 0.004292 + 0.004584 + 0.00425 + 0.004958 + 0.005 + 0.005167 + 0.005125 + 0.004666 + 0.004542 + 0.004667 + 0.004542 + 0.004417 + 0.004625 + 0.004459 + 0.0045 + 0.003875 + 0.003875 + 0.004 + 0.004417 + 0.004292 + 0.004041 + 0.00425 + 0.004125 + 0.004166 + 0.004208 + 0.004 + 0.004416 + 0.004583 + 0.004 + 0.004292 + 0.00425 + 0.005208 + 0.005125 + 0.004959 + 0.004917 + 0.004708 + 0.0045 + 0.00525 + 0.004542 + 0.004792 + 0.005333 + 0.007083 + 0.009917 + 0.00625 + 0.005875 + 0.02 + 0.005583 + 0.005291 + 0.005542 + 0.00475 + 0.006125 + 0.00475 + 0.004875 + 0.004458 + 0.00725 + 0.005709 + 0.005917 + 0.00675 + 0.006042 + 0.004833 + 0.005084 + 0.005166 + 0.005625 + 0.004916 + 0.00525 + 0.004625 + 0.006 + 0.007541 + 0.006 + 0.00925 + 0.006041 + 0.013125 + 0.005416 + 0.005125 + 0.005416 + 0.006083 + 0.006542 + 0.008375 + 0.005292 + 0.005125 + 0.004917 + 0.006125 + 0.005166 + 0.004333 + 0.004 + 0.003958 + 0.004167 + 0.004209 + 0.004042 + 0.00425 + 0.005417 + 0.005084 + 0.004208 + 0.003917 + 0.003833 + 0.003875 + 0.003625 + 0.004417 + 0.004041 + 0.004292 + 0.004208 + 0.004167 + 0.004 + 0.004958 + 0.00725 + 0.007291 + 0.006208 + 0.006417 + 0.007334 + 0.004833 + 0.006417 + 0.006125 + 0.007334 + 0.006541 + 0.006792 + 0.006041 + 0.00625 + 0.005291 + 0.00475 + 0.004583 + 0.00625 + 0.006375 + 0.006625 + 0.005417 + 0.004833 + 0.005583 + 0.005084 + 0.005917 + 0.006167 + 0.005584 + 0.005292 + 0.005166 + 0.006042 + 0.005125 + 0.006125 + 0.005125 + 0.005083 + 0.004667 + 0.005083 + 0.004583 + 0.004709 + 0.011542 + 0.005583 + 0.005666 + 0.005583 + 0.008709 + 0.005834 + 0.005583 + 0.004917 + 0.004375 + 0.004333 + 0.005208 + 0.004791 + 0.004708 + 0.004625 + 0.004416 + 0.004708 + 0.004542 + 0.00375 + 0.004834 + 0.004792 + 0.004833 + 0.004208 + 0.004542 + 0.004458 + 0.007167 + 0.004667 + 0.004334 + 0.006041 + 0.005375 + 0.005208 + 0.004833 + 0.005375 + 0.007541 + 0.005583 + 0.004833 + 0.0055 + 0.005542 + 0.005167 + 0.005458 + 0.00525 + 0.006916 + 0.00575 + 0.005458 + 0.00475 + 0.0045 + 0.004666 + 0.00425 + 0.004292 + 0.004042 + 0.004375 + 0.004375 + 0.003875 + 0.004167 + 0.003708 + 0.004792 + 0.004625 + 0.004209 + 0.004125 + 0.004 + 0.004 + 0.004292 + 0.004292 + 0.0045 + 0.003958 + 0.004125 + 0.004375 + 0.003875 + 0.003875 + 0.004834 + 0.004333 + 0.005041 + 0.003958 + 0.004 + 0.004041 + 0.004208 + 0.003917 + 0.00425 + 0.004417 + 0.00425 + 0.003709 + 0.004125 + 0.004042 + 0.003583 + 0.00425 + 0.004292 + 0.004292 + 0.004125 + 0.004084 + 0.004375 + 0.004041 + 0.004208 + 0.004208 + 0.005042 + 0.004833 + 0.004375 + 0.004458 + 0.004541 + 0.004208 + 0.004167 + 0.004666 + 0.005167 + 0.007792 + 0.005083 + 0.005625 + 0.005459 + 0.005959 + 0.004959 + 0.005417 + 0.004875 + 0.004458 + 0.006708 + 0.00475 + 0.004375 + 0.004459 + 0.004667 + 0.004375 + 0.005 + 0.004667 + 0.004209 + 0.004542 + 0.004333 + 0.004125 + 0.004459 + 0.004959 + 0.004875 + 0.004709 + 0.004375 + 0.005375 + 0.008542 + 0.005334 + 0.005709 + 0.005375 + 0.005459 + 0.005542 + 0.004375 + 0.005208 + 0.007709 + 0.005334 + 0.005666 + 0.0055 + 0.005292 + 0.005 + 0.005542 + 0.004208 + 0.004 + 0.00425 + 0.004292 + 0.004125 + 0.003917 + 0.004041 + 0.004625 + 0.004416 + 0.003875 + 0.004375 + 0.004125 + 0.004166 + 0.003917 + 0.004 + 0.004208 + 0.003917 + 0.004209 + 0.004334 + 0.004334 + 0.003708 + 0.003875 + 0.004917 + 0.004 + 0.00425 + 0.004 + 0.004291 + 0.004417 + 0.003958 + 0.004 + 0.004 + 0.00425 + 0.003708 + 0.00425 + 0.004167 + 0.004125 + 0.003875 + 0.003959 + 0.004459 + 0.004167 + 0.004125 + 0.004166 + 0.004125 + 0.003916 + 0.003833 + 0.003917 + 0.004125 + 0.003917 + 0.004458 + 0.007417 + 0.004917 + 0.004542 + 0.004333 + 0.004208 + 0.00425 + 0.004083 + 0.004125 + 0.006708 + 0.00575 + 0.012958 + 0.007959 + 0.005666 + 0.00575 + 0.004875 + 0.012333 + 0.004834 + 0.003084 + 0.00425 + 0.004208 + 0.004291 + 0.004333 + 0.004167 + 0.0045 + 0.004833 + 0.0045 + 0.014625 + 0.004666 + 0.004833 + 0.004459 + 0.004916 + 0.01225 + 0.004625 + 0.004042 + 0.004583 + 0.005417 + 0.005084 + 0.005041 + 0.007583 + 0.005625 + 0.005208 + 0.004625 + 0.004875 + 0.004333 + 0.00575 + 0.008208 + 0.005041 + 0.005083 + 0.004958 + 0.004917 + 0.0045 + 0.011792 + 0.004125 + 0.00375 + 0.004125 + 0.004083 + 0.003667 + 0.003542 + 0.004042 + 0.004458 + 0.0035 + 0.003584 + 0.003541 + 0.00375 + 0.003667 + 0.004 + 0.003542 + 0.003209 + 0.004 + 0.003709 + 0.003916 + 0.003792 + 0.003833 + 0.004209 + 0.003875 + 0.003875 + 0.003708 + 0.004083 + 0.003583 + 0.003958 + 0.003917 + 0.003917 + 0.003708 + 0.00375 + 0.003542 + 0.003708 + 0.003708 + 0.003959 + 0.003459 + 0.003417 + 0.003708 + 0.003458 + 0.003625 + 0.003458 + 0.003292 + 0.003584 + 0.003958 + 0.003666 + 0.003542 + 0.003667 + 0.00425 + 0.004167 + 0.004291 + 0.004209 + 0.014625 + 0.004 + 0.004167 + 0.004375 + 0.004459 + 0.005041 + 0.005167 + 0.004833 + 0.005 + 0.004666 + 0.007917 + 0.004875 + 0.003542 + 0.003709 + 0.004 + 0.003917 + 0.003834 + 0.003625 + 0.003958 + 0.003709 + 0.003458 + 0.004541 + 0.004334 + 0.004333 + 0.004125 + 0.003791 + 0.003958 + 0.004959 + 0.003666 + 0.003792 + 0.003792 + 0.007958 + 0.0045 + 0.004542 + 0.004209 + 0.004125 + 0.004042 + 0.004333 + 0.007541 + 0.003625 + 0.004542 + 0.004125 + 0.003875 + 0.003958 + 0.003875 + 0.008041 + 0.00425 + 0.003667 + 0.003208 + 0.003292 + 0.003041 + 0.003167 + 0.003334 + 0.003 + 0.003542 + 0.003333 + 0.003375 + 0.003208 + 0.003708 + 0.003292 + 0.003042 + 0.00325 + 0.003084 + 0.003167 + 0.003208 + 0.002917 + 0.003375 + 0.003792 + 0.003875 + 0.003542 + 0.003167 + 0.003083 + 0.003042 + 0.003084 + 0.003292 + 0.003125 + 0.003416 + 0.003042 + 0.003083 + 0.003208 + 0.003458 + 0.00375 + 0.003875 + 0.002917 + 0.002959 + 0.003167 + 0.003083 + 0.003375 + 0.003417 + 0.003416 + 0.003209 + 0.00325 + 0.002875 + 0.003 + 0.0035 + 0.003625 + 0.004375 + 0.003916 + 0.004125 + 0.003792 + 0.003458 + 0.00325 + 0.003292 + 0.003875 + 0.003917 + 0.004375 + 0.003916 + 0.007292 + 0.004375 + 0.00525 + 0.004416 + 0.004375 + 0.003791 + 0.003459 + 0.003459 + 0.003542 + 0.003667 + 0.003375 + 0.003833 + 0.003542 + 0.003458 + 0.006625 + 0.004208 + 0.005 + 0.004459 + 0.004 + 0.004083 + 0.004584 + 0.004584 + 0.003958 + 0.004167 + 0.004459 + 0.005416 + 0.005334 + 0.026667 + 0.010291 + 0.006958 + 0.006708 + 0.00625 + 0.006167 + 0.00575 + 0.005875 + 0.007125 + 0.006917 + 0.006375 + 0.006459 + 0.005792 + 0.005625 + 0.004292 + 0.004166 + 0.004375 + 0.004667 + 0.004208 + 0.003916 + 0.004375 + 0.005041 + 0.004208 + 0.003625 + 0.004542 + 0.004542 + 0.004416 + 0.004167 + 0.003959 + 0.0045 + 0.004292 + 0.003708 + 0.003958 + 0.003625 + 0.004125 + 0.004292 + 0.004125 + 0.004583 + 0.003875 + 0.004167 + 0.003958 + 0.004333 + 0.00425 + 0.004125 + 0.003917 + 0.00425 + 0.004042 + 0.004167 + 0.003959 + 0.004083 + 0.003916 + 0.003875 + 0.003875 + 0.004375 + 0.004208 + 0.004 + 0.003667 + 0.004166 + 0.003833 + 0.003917 + 0.003708 + 0.004292 + 0.004375 + 0.005834 + 0.005333 + 0.005167 + 0.005084 + 0.004625 + 0.005458 + 0.004542 + 0.003958 + 0.005333 + 0.008292 + 0.005916 + 0.007292 + 0.0065 + 0.006958 + 0.005375 + 0.0055 + 0.005458 + 0.004708 + 0.004958 + 0.00425 + 0.004583 + 0.004875 + 0.006209 + 0.005416 + 0.005583 + 0.005833 + 0.005375 + 0.005167 + 0.004834 + 0.00475 + 0.004542 + 0.005 + 0.005 + 0.005125 + 0.004541 + 0.005417 + 0.005958 + 0.006833 + 0.0065 + 0.006917 + 0.005959 + 0.006 + 0.004625 + 0.0045 + 0.007042 + 0.007084 + 0.006834 + 0.007542 + 0.005833 + 0.005167 + 0.005917 + 0.005833 + 0.005458 + 0.008417 + 0.006041 + 0.004542 + 0.00475 + 0.004875 + 0.005208 + 0.00525 + 0.004 + 0.004625 + 0.005666 + 0.005 + 0.005125 + 0.004958 + 0.005208 + 0.004958 + 0.011417 + 0.004958 + 0.004875 + 0.00575 + 0.006334 + 0.004791 + 0.0045 + 0.004833 + 0.005292 + 0.005667 + 0.006 + 0.006375 + 0.005708 + 0.006041 + 0.00525 + 0.006 + 0.009417 + 0.005541 + 0.005458 + 0.005083 + 0.005625 + 0.005958 + 0.005208 + 0.005042 + 0.004916 + 0.005083 + 0.005417 + 0.006 + 0.01 + 0.00625 + 0.005334 + 0.006 + 0.005625 + 0.005542 + 0.00675 + 0.005875 + 0.005916 + 0.005959 + 0.005583 + 0.006125 + 0.006708 + 0.006875 + 0.007 + 0.006625 + 0.0065 + 0.006333 + 0.005959 + 0.005833 + 0.005541 + 0.0045 + 0.005375 + 0.006125 + 0.006083 + 0.005667 + 0.00575 + 0.005792 + 0.005584 + 0.00625 + 0.005917 + 0.005583 + 0.004708 + 0.004875 + 0.004584 + 0.003959 + 0.004875 + 0.005 + 0.00525 + 0.005917 + 0.006959 + 0.007 + 0.007083 + 0.006792 + 0.006584 + 0.007375 + 0.007834 + 0.00575 + 0.006583 + 0.006375 + 0.006375 + 0.007 + 0.006709 + 0.00775 + 0.007208 + 0.005708 + 0.005375 + 0.005583 + 0.005125 + 0.005209 + 0.005125 + 0.00575 + 0.004041 + 0.00475 + 0.004792 + 0.004416 + 0.0045 + 0.004625 + 0.004458 + 0.0045 + 0.004291 + 0.004125 + 0.004375 + 0.00475 + 0.0045 + 0.004583 + 0.004708 + 0.004583 + 0.004292 + 0.004416 + 0.0045 + 0.004583 + 0.004584 + 0.004625 + 0.004209 + 0.004167 + 0.003875 + 0.00475 + 0.004625 + 0.004833 + 0.00425 + 0.004 + 0.004208 + 0.0045 + 0.004125 + 0.004666 + 0.004584 + 0.004083 + 0.004208 + 0.004167 + 0.0045 + 0.004667 + 0.005375 + 0.007 + 0.005958 + 0.005958 + 0.005417 + 0.005666 + 0.0055 + 0.004958 + 0.005125 + 0.004583 + 0.005 + 0.005583 + 0.007 + 0.007334 + 0.006334 + 0.006416 + 0.007625 + 0.007625 + 0.006042 + 0.004959 + 0.004584 + 0.005958 + 0.005958 + 0.005875 + 0.0065 + 0.005708 + 0.005833 + 0.005958 + 0.0055 + 0.005125 + 0.004083 + 0.005042 + 0.004625 + 0.004583 + 0.004916 + 0.004708 + 0.005042 + 0.004875 + 0.006917 + 0.008167 + 0.006916 + 0.007208 + 0.006625 + 0.006625 + 0.007334 + 0.0065 + 0.006792 + 0.007167 + 0.007375 + 0.00625 + 0.006292 + 0.007334 + 0.006625 + 0.005833 + 0.005291 + 0.005333 + 0.005625 + 0.005291 + 0.005458 + 0.005375 + 0.00625 + 0.005709 + 0.004334 + 0.004375 + 0.005042 + 0.00475 + 0.004292 + 0.004541 + 0.00425 + 0.004166 + 0.004083 + 0.004792 + 0.004833 + 0.004417 + 0.00475 + 0.004917 + 0.00425 + 0.004459 + 0.004292 + 0.004375 + 0.004209 + 0.004375 + 0.003958 + 0.00425 + 0.004 + 0.004334 + 0.004041 + 0.00425 + 0.003833 + 0.004 + 0.004167 + 0.0045 + 0.004291 + 0.003833 + 0.0045 + 0.004333 + 0.004167 + 0.00425 + 0.004542 + 0.004542 + 0.004458 + 0.004625 + 0.004875 + 0.004458 + 0.004208 + 0.005042 + 0.006292 + 0.005458 + 0.00575 + 0.005541 + 0.005458 + 0.006958 + 0.0065 + 0.007709 + 0.007 + 0.006875 + 0.006417 + 0.006666 + 0.005583 + 0.005458 + 0.00625 + 0.005625 + 0.005334 + 0.004542 + 0.00575 + 0.005917 + 0.005709 + 0.005083 + 0.005333 + 0.006125 + 0.005708 + 0.006125 + 0.005375 + 0.004542 + 0.004417 + 0.005042 + 0.00475 + 0.0045 + 0.005792 + 0.004916 + 0.006334 + 0.00675 + 0.00625 + 0.006208 + 0.006417 + 0.005209 + 0.0065 + 0.007125 + 0.00675 + 0.006167 + 0.006291 + 0.006417 + 0.006792 + 0.005958 + 0.005 + 0.004125 + 0.003875 + 0.004042 + 0.004459 + 0.004125 + 0.00425 + 0.004292 + 0.004 + 0.004709 + 0.003708 + 0.004542 + 0.004 + 0.004209 + 0.004125 + 0.004041 + 0.003833 + 0.004333 + 0.004084 + 0.003875 + 0.003917 + 0.004208 + 0.003834 + 0.003959 + 0.003958 + 0.003792 + 0.003667 + 0.003833 + 0.003958 + 0.003959 + 0.003709 + 0.004 + 0.004292 + 0.003791 + 0.003917 + 0.003625 + 0.004042 + 0.003875 + 0.004041 + 0.003833 + 0.003833 + 0.004042 + 0.004125 + 0.003958 + 0.004125 + 0.003708 + 0.004083 + 0.004125 + 0.006292 + 0.005709 + 0.005167 + 0.005125 + 0.005416 + 0.005125 + 0.005 + 0.005334 + 0.004959 + 0.005709 + 0.004834 + 0.005208 + 0.006875 + 0.006625 + 0.006041 + 0.006333 + 0.006583 + 0.005292 + 0.005 + 0.005125 + 0.004708 + 0.004208 + 0.004292 + 0.004417 + 0.004125 + 0.005333 + 0.005416 + 0.005333 + 0.005625 + 0.005416 + 0.005167 + 0.005041 + 0.005375 + 0.004917 + 0.0045 + 0.004333 + 0.00575 + 0.004791 + 0.006583 + 0.007125 + 0.006333 + 0.006291 + 0.006125 + 0.005333 + 0.005 + 0.00625 + 0.00625 + 0.006583 + 0.006292 + 0.006042 + 0.006458 + 0.005792 + 0.005 + 0.004584 + 0.004583 + 0.004375 + 0.003708 + 0.003542 + 0.003791 + 0.004 + 0.004291 + 0.003833 + 0.004708 + 0.004625 + 0.004666 + 0.004625 + 0.004625 + 0.00475 + 0.004167 + 0.003667 + 0.003875 + 0.004084 + 0.004209 + 0.003959 + 0.003791 + 0.003917 + 0.003625 + 0.003833 + 0.00375 + 0.003625 + 0.003917 + 0.003875 + 0.004083 + 0.003875 + 0.003958 + 0.003916 + 0.003833 + 0.003917 + 0.003834 + 0.003834 + 0.003625 + 0.003834 + 0.004125 + 0.004958 + 0.005417 + 0.0055 + 0.01625 + 0.013583 + 0.005791 + 0.005458 + 0.00675 + 0.005583 + 0.007708 + 0.006333 + 0.005041 + 0.004458 + 0.003834 + 0.003792 + 0.003958 + 0.003916 + 0.006167 + 0.004875 + 0.004875 + 0.008417 + 0.006416 + 0.005708 + 0.005625 + 0.005 + 0.0045 + 0.004334 + 0.004375 + 0.004167 + 0.004875 + 0.004417 + 0.004416 + 0.004292 + 0.005292 + 0.004916 + 0.006583 + 0.005917 + 0.005792 + 0.005458 + 0.005041 + 0.005542 + 0.00525 + 0.005375 + 0.006959 + 0.005792 + 0.005666 + 0.007208 + 0.006834 + 0.006959 + 0.006209 + 0.006125 + 0.005917 + 0.006666 + 0.008208 + 0.006333 + 0.005917 + 0.005875 + 0.006 + 0.005542 + 0.005291 + 0.004375 + 0.004375 + 0.004209 + 0.003959 + 0.004125 + 0.0035 + 0.003958 + 0.004708 + 0.004583 + 0.003708 + 0.004333 + 0.004125 + 0.005125 + 0.004583 + 0.0045 + 0.004125 + 0.004625 + 0.004333 + 0.004958 + 0.004791 + 0.0045 + 0.005333 + 0.004333 + 0.004417 + 0.004584 + 0.004917 + 0.005083 + 0.004625 + 0.004291 + 0.004334 + 0.003916 + 0.005042 + 0.004416 + 0.0045 + 0.00425 + 0.004125 + 0.003917 + 0.004958 + 0.004167 + 0.004917 + 0.004292 + 0.003959 + 0.0045 + 0.004708 + 0.004208 + 0.004708 + 0.004375 + 0.004625 + 0.004833 + 0.005 + 0.006917 + 0.005834 + 0.006 + 0.005333 + 0.005583 + 0.00575 + 0.00575 + 0.006458 + 0.007125 + 0.005417 + 0.006959 + 0.006625 + 0.007042 + 0.006667 + 0.005833 + 0.005708 + 0.005625 + 0.005666 + 0.004875 + 0.00475 + 0.004833 + 0.004166 + 0.0045 + 0.0045 + 0.005917 + 0.006084 + 0.005667 + 0.00575 + 0.005875 + 0.006125 + 0.00525 + 0.005958 + 0.005667 + 0.004209 + 0.006209 + 0.006459 + 0.006625 + 0.007917 + 0.006791 + 0.007083 + 0.006583 + 0.005542 + 0.006 + 0.006083 + 0.007375 + 0.006584 + 0.006125 + 0.006083 + 0.005583 + 0.005458 + 0.0045 + 0.005333 + 0.004792 + 0.004 + 0.004916 + 0.004833 + 0.004917 + 0.005625 + 0.004875 + 0.005542 + 0.005458 + 0.005125 + 0.005083 + 0.004083 + 0.004125 + 0.003917 + 0.003959 + 0.004292 + 0.0045 + 0.004917 + 0.005125 + 0.005125 + 0.004959 + 0.004709 + 0.004584 + 0.004458 + 0.004333 + 0.004958 + 0.004875 + 0.005541 + 0.005 + 0.005042 + 0.005416 + 0.004958 + 0.004625 + 0.0055 + 0.005542 + 0.005166 + 0.004708 + 0.005042 + 0.005083 + 0.004625 + 0.005166 + 0.004917 + 0.005166 + 0.004458 + 0.004667 + 0.005083 + 0.004916 + 0.004958 + 0.004292 + 0.005833 + 0.005916 + 0.00525 + 0.0055 + 0.005333 + 0.00475 + 0.006167 + 0.006583 + 0.005792 + 0.0055 + 0.006208 + 0.006333 + 0.005709 + 0.006292 + 0.004916 + 0.004625 + 0.004208 + 0.004417 + 0.004125 + 0.004541 + 0.003959 + 0.004209 + 0.005333 + 0.005125 + 0.004875 + 0.005042 + 0.005 + 0.00475 + 0.004417 + 0.004167 + 0.004708 + 0.004083 + 0.004333 + 0.004875 + 0.004917 + 0.004541 + 0.006 + 0.005875 + 0.005875 + 0.005583 + 0.005166 + 0.005042 + 0.004792 + 0.006416 + 0.006125 + 0.00575 + 0.005833 + 0.005875 + 0.005333 + 0.004167 + 0.004 + 0.003917 + 0.00375 + 0.003583 + 0.0035 + 0.004 + 0.004042 + 0.003875 + 0.004208 + 0.003791 + 0.004125 + 0.004 + 0.003708 + 0.00375 + 0.003625 + 0.00425 + 0.00425 + 0.003833 + 0.003584 + 0.004 + 0.003958 + 0.00375 + 0.003792 + 0.003792 + 0.003791 + 0.004167 + 0.004 + 0.003792 + 0.004084 + 0.00375 + 0.003959 + 0.00375 + 0.003708 + 0.003833 + 0.004125 + 0.00375 + 0.003667 + 0.004084 + 0.004125 + 0.003917 + 0.003708 + 0.003708 + 0.004042 + 0.00375 + 0.004167 + 0.00375 + 0.0045 + 0.004834 + 0.004959 + 0.004666 + 0.004292 + 0.005667 + 0.005334 + 0.004917 + 0.0055 + 0.00475 + 0.006458 + 0.006 + 0.005916 + 0.005292 + 0.006042 + 0.006709 + 0.00625 + 0.006042 + 0.005584 + 0.005417 + 0.005083 + 0.004916 + 0.005125 + 0.004667 + 0.003958 + 0.00425 + 0.005375 + 0.005542 + 0.004959 + 0.005 + 0.005125 + 0.004917 + 0.0045 + 0.004166 + 0.004167 + 0.004291 + 0.003917 + 0.005041 + 0.004792 + 0.004375 + 0.006292 + 0.006167 + 0.006125 + 0.005708 + 0.007917 + 0.00425 + 0.006625 + 0.00575 + 0.00625 + 0.006042 + 0.005875 + 0.005459 + 0.005334 + 0.004959 + 0.004875 + 0.004458 + 0.005292 + 0.005042 + 0.005333 + 0.004875 + 0.004042 + 0.005041 + 0.004583 + 0.004833 + 0.005167 + 0.0045 + 0.004833 + 0.005917 + 0.006 + 0.005875 + 0.006333 + 0.00575 + 0.005834 + 0.005667 + 0.004375 + 0.004834 + 0.004875 + 0.004958 + 0.005708 + 0.005667 + 0.005458 + 0.005666 + 0.004833 + 0.005083 + 0.005292 + 0.005083 + 0.00525 + 0.005083 + 0.005167 + 0.005709 + 0.005125 + 0.005292 + 0.00525 + 0.004917 + 0.004625 + 0.004375 + 0.0045 + 0.004333 + 0.0045 + 0.0045 + 0.004542 + 0.005917 + 0.00525 + 0.004792 + 0.005042 + 0.004166 + 0.005167 + 0.005417 + 0.005209 + 0.005334 + 0.005334 + 0.006625 + 0.005958 + 0.005875 + 0.006666 + 0.005709 + 0.005333 + 0.005042 + 0.00425 + 0.004166 + 0.004459 + 0.0045 + 0.004 + 0.004209 + 0.003875 + 0.0045 + 0.004417 + 0.004875 + 0.005958 + 0.004542 + 0.004292 + 0.004208 + 0.004291 + 0.004 + 0.004 + 0.003959 + 0.003833 + 0.00475 + 0.00475 + 0.004584 + 0.006 + 0.006292 + 0.005125 + 0.005625 + 0.005708 + 0.005 + 0.009125 + 0.006375 + 0.006291 + 0.005417 + 0.005875 + 0.005667 + 0.005417 + 0.003959 + 0.003916 + 0.003625 + 0.003916 + 0.003542 + 0.003916 + 0.003625 + 0.004708 + 0.00425 + 0.003833 + 0.003542 + 0.00375 + 0.004334 + 0.004166 + 0.004042 + 0.003708 + 0.004 + 0.003916 + 0.003708 + 0.003959 + 0.004042 + 0.004 + 0.003875 + 0.00375 + 0.004 + 0.003667 + 0.004 + 0.004 + 0.004167 + 0.003625 + 0.004208 + 0.003625 + 0.003583 + 0.003333 + 0.003708 + 0.003833 + 0.00375 + 0.003542 + 0.003625 + 0.004125 + 0.003791 + 0.003542 + 0.003833 + 0.003958 + 0.003792 + 0.0035 + 0.003917 + 0.004 + 0.003792 + 0.004292 + 0.004583 + 0.00625 + 0.004625 + 0.004958 + 0.005542 + 0.005541 + 0.004708 + 0.005292 + 0.006417 + 0.006 + 0.005875 + 0.0055 + 0.006959 + 0.007375 + 0.006458 + 0.005625 + 0.005917 + 0.005125 + 0.00525 + 0.004792 + 0.004792 + 0.005083 + 0.004875 + 0.005 + 0.005333 + 0.004208 + 0.0045 + 0.00425 + 0.004875 + 0.016041 + 0.010583 + 0.004833 + 0.005292 + 0.0045 + 0.004875 + 0.017833 + 0.008958 + 0.00575 + 0.005416 + 0.008042 + 0.008667 + 0.006291 + 0.005583 + 0.015917 + 0.008584 + 0.00675 + 0.006792 + 0.008042 + 0.007166 + 0.006 + 0.006458 + 0.007208 + 0.005292 + 0.005375 + 0.004917 + 0.005375 + 0.004584 + 0.00475 + 0.004625 + 0.004958 + 0.003792 + 0.005 + 0.005417 + 0.004583 + 0.004666 + 0.004625 + 0.004583 + 0.004291 + 0.005 + 0.00475 + 0.005042 + 0.005333 + 0.0055 + 0.004958 + 0.005916 + 0.005583 + 0.004541 + 0.004208 + 0.004625 + 0.004292 + 0.004792 + 0.004959 + 0.0055 + 0.005459 + 0.005209 + 0.005292 + 0.005666 + 0.006 + 0.005458 + 0.005083 + 0.005 + 0.004791 + 0.004792 + 0.005375 + 0.005 + 0.00525 + 0.005333 + 0.005042 + 0.00525 + 0.005791 + 0.005584 + 0.004917 + 0.005291 + 0.005375 + 0.005125 + 0.005541 + 0.005292 + 0.005125 + 0.008542 + 0.00775 + 0.006875 + 0.005833 + 0.005875 + 0.006709 + 0.007458 + 0.007125 + 0.00675 + 0.0065 + 0.006166 + 0.005708 + 0.006333 + 0.006167 + 0.005375 + 0.00525 + 0.005709 + 0.006584 + 0.005834 + 0.021083 + 0.006042 + 0.005458 + 0.005375 + 0.005208 + 0.006208 + 0.006083 + 0.00425 + 0.008375 + 0.006375 + 0.006625 + 0.00625 + 0.005959 + 0.005708 + 0.007292 + 0.006208 + 0.007541 + 0.007584 + 0.006708 + 0.006 + 0.007 + 0.009375 + 0.005708 + 0.005625 + 0.005041 + 0.005084 + 0.004875 + 0.004958 + 0.005291 + 0.005292 + 0.004667 + 0.005375 + 0.005708 + 0.004541 + 0.004375 + 0.004834 + 0.004125 + 0.004083 + 0.004542 + 0.004583 + 0.004541 + 0.004375 + 0.00425 + 0.004375 + 0.004125 + 0.003792 + 0.003875 + 0.004209 + 0.004166 + 0.004333 + 0.004084 + 0.004042 + 0.004042 + 0.004042 + 0.004166 + 0.004417 + 0.004667 + 0.004042 + 0.003958 + 0.003875 + 0.003958 + 0.004083 + 0.003833 + 0.003875 + 0.003917 + 0.003917 + 0.004166 + 0.003959 + 0.004125 + 0.004042 + 0.004125 + 0.00425 + 0.004708 + 0.004833 + 0.004833 + 0.004334 + 0.00425 + 0.004708 + 0.004584 + 0.004542 + 0.004708 + 0.00725 + 0.006875 + 0.006167 + 0.006375 + 0.006459 + 0.006458 + 0.006375 + 0.007542 + 0.005833 + 0.005125 + 0.005125 + 0.005667 + 0.005833 + 0.00575 + 0.005834 + 0.005291 + 0.005125 + 0.00575 + 0.005959 + 0.005959 + 0.006417 + 0.00575 + 0.0695 + 0.061333 + 0.006042 + 0.006458 + 0.005125 + 0.022084 + 0.008625 + 0.007167 + 0.006583 + 0.00625 + 0.0065 + 0.00875 + 0.005708 + 0.006458 + 0.007792 + 0.005333 + 0.005208 + 0.005333 + 0.005375 + 0.008333 + 0.005667 + 0.006416 + 0.004083 + 0.004792 + 0.004917 + 0.004541 + 0.004584 + 0.004583 + 0.005833 + 0.004709 + 0.004916 + 0.004834 + 0.004875 + 0.006709 + 0.0045 + 0.004209 + 0.004667 + 0.003916 + 0.0045 + 0.004792 + 0.004625 + 0.004458 + 0.004166 + 0.004417 + 0.004458 + 0.004334 + 0.004791 + 0.004125 + 0.004708 + 0.004375 + 0.004375 + 0.004333 + 0.0045 + 0.005208 + 0.004167 + 0.003958 + 0.004334 + 0.004334 + 0.004209 + 0.004167 + 0.004541 + 0.004375 + 0.0045 + 0.004666 + 0.006 + 0.004209 + 0.004292 + 0.004 + 0.004666 + 0.006042 + 0.006 + 0.00475 + 0.004792 + 0.004709 + 0.004792 + 0.005083 + 0.00475 + 0.0045 + 0.006375 + 0.00675 + 0.005291 + 0.007834 + 0.00575 + 0.005708 + 0.005417 + 0.006375 + 0.009667 + 0.009708 + 0.010291 + 0.006625 + 0.006792 + 0.007667 + 0.006125 + 0.005708 + 0.006458 + 0.009792 + 0.007584 + 0.006875 + 0.0065 + 0.00625 + 0.005708 + 0.005709 + 0.006375 + 0.005709 + 0.005959 + 0.006792 + 0.006583 + 0.006666 + 0.01 + 0.008166 + 0.006417 + 0.005375 + 0.005167 + 0.004667 + 0.00575 + 0.007375 + 0.005917 + 0.006083 + 0.005292 + 0.005916 + 0.005625 + 0.0045 + 0.004375 + 0.004375 + 0.004917 + 0.004625 + 0.004417 + 0.004208 + 0.004833 + 0.005042 + 0.004 + 0.003667 + 0.004666 + 0.00475 + 0.004125 + 0.004375 + 0.004583 + 0.005125 + 0.004834 + 0.004166 + 0.0045 + 0.004333 + 0.004458 + 0.004167 + 0.004708 + 0.00475 + 0.00425 + 0.004291 + 0.00425 + 0.004667 + 0.005083 + 0.005 + 0.004542 + 0.009541 + 0.004125 + 0.004291 + 0.004083 + 0.004 + 0.0045 + 0.004583 + 0.004542 + 0.004334 + 0.004167 + 0.004541 + 0.004167 + 0.004334 + 0.004709 + 0.004167 + 0.004416 + 0.004583 + 0.00475 + 0.004375 + 0.004458 + 0.004708 + 0.007458 + 0.004792 + 0.005459 + 0.004916 + 0.0045 + 0.0055 + 0.005209 + 0.005333 + 0.005875 + 0.008041 + 0.005917 + 0.005542 + 0.004666 + 0.004542 + 0.004458 + 0.005 + 0.005083 + 0.0045 + 0.004041 + 0.004792 + 0.0045 + 0.004958 + 0.003542 + 0.005 + 0.004917 + 0.005125 + 0.004333 + 0.0045 + 0.004458 + 0.006667 + 0.004542 + 0.004166 + 0.006042 + 0.005708 + 0.005208 + 0.00525 + 0.005667 + 0.007459 + 0.005417 + 0.004666 + 0.005834 + 0.0055 + 0.004709 + 0.005042 + 0.004916 + 0.007417 + 0.005375 + 0.005333 + 0.004833 + 0.004209 + 0.004334 + 0.00425 + 0.00425 + 0.004 + 0.004333 + 0.004083 + 0.003833 + 0.00425 + 0.004125 + 0.00525 + 0.004833 + 0.003833 + 0.003792 + 0.004166 + 0.003583 + 0.004708 + 0.004333 + 0.004458 + 0.004667 + 0.004375 + 0.004083 + 0.004333 + 0.003958 + 0.004583 + 0.004333 + 0.004625 + 0.00425 + 0.004042 + 0.004333 + 0.004041 + 0.003792 + 0.004333 + 0.0045 + 0.004333 + 0.003917 + 0.00425 + 0.004 + 0.004041 + 0.003958 + 0.004291 + 0.004584 + 0.003708 + 0.004083 + 0.005375 + 0.004 + 0.004125 + 0.004292 + 0.005375 + 0.005291 + 0.005208 + 0.0045 + 0.004333 + 0.004625 + 0.004458 + 0.004416 + 0.005542 + 0.00825 + 0.006083 + 0.005625 + 0.004875 + 0.006042 + 0.005041 + 0.005417 + 0.004833 + 0.004667 + 0.007125 + 0.004541 + 0.004583 + 0.004458 + 0.004542 + 0.004209 + 0.0045 + 0.004875 + 0.004458 + 0.004375 + 0.004542 + 0.004708 + 0.004334 + 0.004208 + 0.004333 + 0.004625 + 0.004458 + 0.00525 + 0.008 + 0.004958 + 0.005417 + 0.005209 + 0.00525 + 0.004709 + 0.004541 + 0.004959 + 0.0085 + 0.004959 + 0.005417 + 0.005208 + 0.004916 + 0.004708 + 0.005625 + 0.004375 + 0.003666 + 0.004167 + 0.004166 + 0.003833 + 0.003875 + 0.004125 + 0.004291 + 0.003958 + 0.003375 + 0.003792 + 0.004416 + 0.004 + 0.0035 + 0.003834 + 0.004 + 0.003667 + 0.004292 + 0.004083 + 0.004 + 0.004 + 0.003917 + 0.00375 + 0.004125 + 0.003917 + 0.003958 + 0.003916 + 0.004 + 0.003792 + 0.004041 + 0.004 + 0.004042 + 0.003875 + 0.004459 + 0.004 + 0.00375 + 0.003917 + 0.00375 + 0.004125 + 0.004 + 0.003917 + 0.004 + 0.003958 + 0.003417 + 0.00375 + 0.003666 + 0.004208 + 0.004084 + 0.004292 + 0.006833 + 0.0045 + 0.004166 + 0.003958 + 0.0045 + 0.004166 + 0.004625 + 0.004333 + 0.005042 + 0.004792 + 0.004875 + 0.007875 + 0.005291 + 0.005333 + 0.005042 + 0.005084 + 0.004459 + 0.003458 + 0.004708 + 0.0045 + 0.004334 + 0.00425 + 0.004125 + 0.004167 + 0.007333 + 0.004791 + 0.004959 + 0.004291 + 0.004375 + 0.004375 + 0.004 + 0.004292 + 0.003958 + 0.004208 + 0.004167 + 0.005292 + 0.004375 + 0.007875 + 0.005042 + 0.005292 + 0.00475 + 0.00475 + 0.00475 + 0.003708 + 0.004958 + 0.008167 + 0.005167 + 0.005666 + 0.004542 + 0.004625 + 0.00425 + 0.004167 + 0.004333 + 0.003666 + 0.00375 + 0.00375 + 0.003833 + 0.004 + 0.004334 + 0.0045 + 0.003667 + 0.003667 + 0.003625 + 0.003917 + 0.004083 + 0.004 + 0.003542 + 0.003375 + 0.003459 + 0.003583 + 0.00325 + 0.003375 + 0.003834 + 0.003875 + 0.003875 + 0.003833 + 0.003917 + 0.004 + 0.003709 + 0.003875 + 0.003875 + 0.004 + 0.003792 + 0.003542 + 0.003667 + 0.003833 + 0.003583 + 0.004166 + 0.003958 + 0.003542 + 0.003584 + 0.003875 + 0.003375 + 0.003667 + 0.0035 + 0.00375 + 0.003834 + 0.003541 + 0.003083 + 0.003667 + 0.003916 + 0.003959 + 0.004208 + 0.003917 + 0.006458 + 0.0045 + 0.004834 + 0.00425 + 0.004125 + 0.005 + 0.00475 + 0.004833 + 0.00475 + 0.007083 + 0.005041 + 0.005625 + 0.003833 + 0.003708 + 0.004167 + 0.004291 + 0.003958 + 0.004 + 0.004041 + 0.003792 + 0.003667 + 0.004459 + 0.003917 + 0.003833 + 0.003875 + 0.004208 + 0.004042 + 0.00425 + 0.006584 + 0.004584 + 0.004375 + 0.005167 + 0.004917 + 0.004625 + 0.004708 + 0.004791 + 0.007125 + 0.005291 + 0.005208 + 0.004542 + 0.004958 + 0.004917 + 0.004833 + 0.00475 + 0.007166 + 0.005375 + 0.005209 + 0.00375 + 0.00375 + 0.003792 + 0.003708 + 0.003708 + 0.003917 + 0.003875 + 0.004708 + 0.004542 + 0.003791 + 0.003625 + 0.004084 + 0.004583 + 0.00375 + 0.003666 + 0.003917 + 0.003583 + 0.003583 + 0.0035 + 0.003583 + 0.003292 + 0.003459 + 0.003292 + 0.004167 + 0.003958 + 0.00375 + 0.003791 + 0.00375 + 0.003875 + 0.003833 + 0.004125 + 0.004042 + 0.003833 + 0.003833 + 0.003833 + 0.00375 + 0.00375 + 0.003666 + 0.003875 + 0.003708 + 0.003875 + 0.0035 + 0.004 + 0.0035 + 0.003416 + 0.003542 + 0.003708 + 0.003541 + 0.003917 + 0.003875 + 0.003792 + 0.00375 + 0.003584 + 0.004333 + 0.003917 + 0.004125 + 0.004042 + 0.00425 + 0.005208 + 0.007583 + 0.004958 + 0.005667 + 0.005167 + 0.005167 + 0.004875 + 0.0045 + 0.003958 + 0.004167 + 0.004125 + 0.004625 + 0.004375 + 0.011334 + 0.004459 + 0.004375 + 0.003417], + :inline-eight + [0.047625 + 0.012792 + 0.008292 + 0.007958 + 0.007875 + 0.00775 + 0.00725 + 0.007666 + 0.007708 + 0.008583 + 0.00825 + 0.009541 + 0.009708 + 0.009542 + 0.0095 + 0.008458 + 0.007042 + 0.008083 + 0.007875 + 0.007334 + 0.012 + 0.008 + 0.008625 + 0.008917 + 0.010167 + 0.009875 + 0.009417 + 0.008125 + 0.008458 + 0.007708 + 0.007625 + 0.00825 + 0.009666 + 0.009458 + 0.008875 + 0.008292 + 0.008167 + 0.010875 + 0.009958 + 0.013959 + 0.012333 + 0.010417 + 0.011875 + 0.027625 + 0.014084 + 0.009083 + 0.010584 + 0.010292 + 0.010083 + 0.010042 + 0.011375 + 0.011792 + 0.011 + 0.008459 + 0.01025 + 0.007917 + 0.007916 + 0.007792 + 0.007667 + 0.007833 + 0.007833 + 0.009709 + 0.010208 + 0.008208 + 0.008166 + 0.008041 + 0.008167 + 0.008042 + 0.009 + 0.009875 + 0.008459 + 0.020125 + 0.011833 + 0.0115 + 0.0115 + 0.011292 + 0.009167 + 0.012209 + 0.013541 + 0.012916 + 0.0105 + 0.012375 + 0.012209 + 0.011084 + 0.012792 + 0.013375 + 0.017959 + 0.009416 + 0.007542 + 0.00925 + 0.008167 + 0.021083 + 0.032708 + 0.014458 + 0.008208 + 0.007833 + 0.011084 + 0.009667 + 0.008666 + 0.005625 + 0.0065 + 0.007375 + 0.007208 + 0.00675 + 0.007209 + 0.007542 + 0.007666 + 0.016667 + 0.007042 + 0.007083 + 0.006958 + 0.007417 + 0.0075 + 0.007542 + 0.007084 + 0.007417 + 0.007084 + 0.007042 + 0.006833 + 0.006834 + 0.007917 + 0.007167 + 0.007291 + 0.0075 + 0.006541 + 0.006667 + 0.006792 + 0.007209 + 0.007708 + 0.007416 + 0.007125 + 0.007542 + 0.006667 + 0.007375 + 0.006958 + 0.00675 + 0.007 + 0.007916 + 0.008166 + 0.007625 + 0.007541 + 0.007083 + 0.007416 + 0.006958 + 0.008208 + 0.008333 + 0.0075 + 0.008 + 0.007459 + 0.007417 + 0.0075 + 0.007041 + 0.007375 + 0.007542 + 0.008209 + 0.008334 + 0.007167 + 0.007125 + 0.00725 + 0.009792 + 0.009625 + 0.008708 + 0.007625 + 0.007625 + 0.007625 + 0.007333 + 0.007416 + 0.008208 + 0.009833 + 0.009541 + 0.010458 + 0.010666 + 0.00975 + 0.012125 + 0.012125 + 0.009875 + 0.00925 + 0.01025 + 0.009875 + 0.008833 + 0.012917 + 0.0175 + 0.009541 + 0.009417 + 0.009292 + 0.009542 + 0.010625 + 0.007167 + 0.007 + 0.006667 + 0.006542 + 0.006834 + 0.006667 + 0.006708 + 0.007083 + 0.00625 + 0.007458 + 0.008 + 0.006292 + 0.006125 + 0.005792 + 0.007334 + 0.00775 + 0.008708 + 0.01025 + 0.00975 + 0.008625 + 0.007917 + 0.007542 + 0.008209 + 0.008167 + 0.007833 + 0.007542 + 0.00875 + 0.008875 + 0.00825 + 0.007583 + 0.007958 + 0.008375 + 0.009334 + 0.008666 + 0.008125 + 0.008 + 0.008041 + 0.007833 + 0.008459 + 0.008125 + 0.00825 + 0.008875 + 0.010459 + 0.010375 + 0.0095 + 0.009541 + 0.008958 + 0.009167 + 0.009458 + 0.008458 + 0.008958 + 0.008792 + 0.008875 + 0.009417 + 0.009 + 0.008959 + 0.009666 + 0.009375 + 0.009833 + 0.009167 + 0.0095 + 0.01 + 0.010666 + 0.010208 + 0.009791 + 0.010292 + 0.008625 + 0.0095 + 0.009459 + 0.010542 + 0.00975 + 0.01 + 0.009458 + 0.009292 + 0.012458 + 0.028875 + 0.119 + 0.015834 + 0.008375 + 0.008292 + 0.007625 + 0.011125 + 0.007459 + 0.00825 + 0.013541 + 0.008916 + 0.011583 + 0.009625 + 0.008333 + 0.008583 + 0.008334 + 0.007917 + 0.008417 + 0.012958 + 0.009083 + 0.008541 + 0.008208 + 0.007875 + 0.00825 + 0.013916 + 0.00725 + 0.006791 + 0.0065 + 0.006917 + 0.006625 + 0.006541 + 0.006833 + 0.006208 + 0.005917 + 0.006708 + 0.008333 + 0.00575 + 0.005791 + 0.00625 + 0.008209 + 0.007417 + 0.007291 + 0.007042 + 0.006875 + 0.006959 + 0.006958 + 0.006833 + 0.006792 + 0.007292 + 0.006792 + 0.007667 + 0.00725 + 0.006917 + 0.006833 + 0.006958 + 0.007042 + 0.007125 + 0.006875 + 0.00675 + 0.006834 + 0.006791 + 0.007292 + 0.007167 + 0.007209 + 0.007167 + 0.006916 + 0.006791 + 0.006791 + 0.007 + 0.007084 + 0.006584 + 0.006834 + 0.007417 + 0.007334 + 0.007292 + 0.007541 + 0.007959 + 0.007292 + 0.007292 + 0.007125 + 0.007375 + 0.007166 + 0.01025 + 0.007458 + 0.007458 + 0.007416 + 0.007291 + 0.007625 + 0.007458 + 0.007166 + 0.008458 + 0.007292 + 0.007417 + 0.008375 + 0.007333 + 0.007375 + 0.007084 + 0.012 + 0.0075 + 0.007375 + 0.007458 + 0.007708 + 0.007208 + 0.007167 + 0.00775 + 0.006958 + 0.007292 + 0.007333 + 0.00775 + 0.007333 + 0.008542 + 0.008417 + 0.010875 + 0.008 + 0.008541 + 0.008208 + 0.007791 + 0.007375 + 0.008375 + 0.008 + 0.01075 + 0.00825 + 0.008417 + 0.007917 + 0.008209 + 0.007834 + 0.006292 + 0.007083 + 0.006458 + 0.006416 + 0.017708 + 0.0065 + 0.008333 + 0.015459 + 0.006625 + 0.007041 + 0.007458 + 0.006542 + 0.005709 + 0.005292 + 0.007083 + 0.007 + 0.006708 + 0.006583 + 0.009 + 0.018375 + 0.010833 + 0.008208 + 0.008916 + 0.008667 + 0.00775 + 0.00825 + 0.013334 + 0.00825 + 0.01 + 0.008792 + 0.008208 + 0.007916 + 0.007583 + 0.00875 + 0.016083 + 0.009208 + 0.008041 + 0.007792 + 0.007042 + 0.007708 + 0.01425 + 0.007125 + 0.006958 + 0.01525 + 0.012042 + 0.007166 + 0.007042 + 0.00725 + 0.007125 + 0.007333 + 0.015416 + 0.007333 + 0.007 + 0.007125 + 0.007583 + 0.0075 + 0.006833 + 0.0105 + 0.007833 + 0.007083 + 0.007459 + 0.007708 + 0.008 + 0.021167 + 0.007542 + 0.007291 + 0.007125 + 0.007417 + 0.007333 + 0.007375 + 0.008208 + 0.007167 + 0.007292 + 0.007042 + 0.007417 + 0.0105 + 0.007375 + 0.014792 + 0.007916 + 0.007833 + 0.007625 + 0.007375 + 0.007291 + 0.007042 + 0.011792 + 0.007958 + 0.008375 + 0.008083 + 0.011042 + 0.008458 + 0.007833 + 0.007834 + 0.00675 + 0.013 + 0.008625 + 0.007916 + 0.014083 + 0.008125 + 0.008042 + 0.007709 + 0.006209 + 0.00625 + 0.006334 + 0.005959 + 0.0065 + 0.006333 + 0.006083 + 0.005875 + 0.006666 + 0.006917 + 0.008042 + 0.00725 + 0.008834 + 0.005209 + 0.006875 + 0.006667 + 0.00675 + 0.006458 + 0.019292 + 0.007209 + 0.00725 + 0.007041 + 0.007042 + 0.006791 + 0.006666 + 0.00675 + 0.007416 + 0.006958 + 0.006666 + 0.006875 + 0.006667 + 0.007167 + 0.007417 + 0.00725 + 0.007334 + 0.007292 + 0.00675 + 0.007042 + 0.0065 + 0.007 + 0.006458 + 0.012417 + 0.007417 + 0.007 + 0.006875 + 0.006458 + 0.006958 + 0.007292 + 0.006458 + 0.007125 + 0.007583 + 0.007792 + 0.007541 + 0.007333 + 0.007209 + 0.006959 + 0.006959 + 0.008083 + 0.007458 + 0.007833 + 0.007375 + 0.007208 + 0.006875 + 0.00725 + 0.01 + 0.007667 + 0.007583 + 0.008166 + 0.007208 + 0.007167 + 0.006875 + 0.007208 + 0.007458 + 0.007625 + 0.007917 + 0.008041 + 0.006666 + 0.007209 + 0.00725 + 0.00725 + 0.007042 + 0.007416 + 0.007 + 0.007625 + 0.013458 + 0.008167 + 0.007916 + 0.021042 + 0.008083 + 0.007625 + 0.010458 + 0.00825 + 0.007209 + 0.007959 + 0.008 + 0.008167 + 0.007875 + 0.012709 + 0.0085 + 0.007333 + 0.006458 + 0.013666 + 0.006667 + 0.00575 + 0.005542 + 0.006125 + 0.006125 + 0.006333 + 0.006208 + 0.006833 + 0.007875 + 0.005084 + 0.005708 + 0.005125 + 0.006791 + 0.006875 + 0.006917 + 0.006959 + 0.006667 + 0.006834 + 0.006708 + 0.006625 + 0.007292 + 0.006875 + 0.006708 + 0.006583 + 0.007166 + 0.006916 + 0.005917 + 0.006375 + 0.006416 + 0.006666 + 0.007042 + 0.007084 + 0.006792 + 0.006833 + 0.00625 + 0.006666 + 0.006625 + 0.006583 + 0.006583 + 0.0065 + 0.007042 + 0.006917 + 0.006834 + 0.006875 + 0.007042 + 0.006666 + 0.006375 + 0.006667 + 0.007334 + 0.007042 + 0.006875 + 0.006542 + 0.006833 + 0.007666 + 0.006916 + 0.0075 + 0.007583 + 0.007375 + 0.007 + 0.006709 + 0.007334 + 0.006875 + 0.007042 + 0.006709 + 0.007042 + 0.007416 + 0.006792 + 0.009625 + 0.007458 + 0.007416 + 0.006792 + 0.00675 + 0.0075 + 0.007166 + 0.006791 + 0.006791 + 0.006708 + 0.006625 + 0.007041 + 0.006833 + 0.007167 + 0.006875 + 0.006417 + 0.007541 + 0.011125 + 0.007417 + 0.007333 + 0.00725 + 0.0075 + 0.008667 + 0.006958 + 0.007916 + 0.010041 + 0.00775 + 0.007583 + 0.007584 + 0.0075 + 0.007208 + 0.00975 + 0.006 + 0.005041 + 0.0055 + 0.005792 + 0.00525 + 0.005834 + 0.005292 + 0.005833 + 0.00625 + 0.00775 + 0.006292 + 0.00575 + 0.004708 + 0.007792 + 0.006125 + 0.0065 + 0.006375 + 0.006292 + 0.006333 + 0.006125 + 0.006625 + 0.006084 + 0.006667 + 0.006625 + 0.005958 + 0.006125 + 0.005833 + 0.006208 + 0.006125 + 0.005875 + 0.006 + 0.008417 + 0.006375 + 0.006334 + 0.006792 + 0.006166 + 0.0065 + 0.006667 + 0.006084 + 0.006333 + 0.006417 + 0.006333 + 0.006375 + 0.00675 + 0.00625 + 0.006584 + 0.006333 + 0.006666 + 0.006167 + 0.006917 + 0.006667 + 0.006625 + 0.007292 + 0.006584 + 0.007042 + 0.006791 + 0.005875 + 0.006583 + 0.006791 + 0.006792 + 0.006375 + 0.006667 + 0.0065 + 0.007625 + 0.006542 + 0.006667 + 0.006833 + 0.009792 + 0.006625 + 0.006791 + 0.007125 + 0.009833 + 0.00725 + 0.006833 + 0.007 + 0.007292 + 0.007 + 0.007708 + 0.006791 + 0.006959 + 0.01825 + 0.009791 + 0.007708 + 0.007 + 0.007834 + 0.008292 + 0.017875 + 0.008 + 0.007667 + 0.008 + 0.007125 + 0.007292 + 0.008334 + 0.010417 + 0.007833 + 0.007916 + 0.009208 + 0.008167 + 0.007917 + 0.010375 + 0.006375 + 0.006917 + 0.006375 + 0.006333 + 0.006542 + 0.005792 + 0.006083 + 0.007083 + 0.005708 + 0.007625 + 0.008458 + 0.007208 + 0.006042 + 0.005542 + 0.007291 + 0.007084 + 0.006875 + 0.006625 + 0.007291 + 0.006875 + 0.007166 + 0.0065 + 0.007166 + 0.007 + 0.007166 + 0.006792 + 0.014666 + 0.005875 + 0.006292 + 0.007125 + 0.006541 + 0.006542 + 0.006125 + 0.006625 + 0.007375 + 0.006292 + 0.014 + 0.007333 + 0.006791 + 0.006625 + 0.006958 + 0.007166 + 0.006667 + 0.006375 + 0.006625 + 0.007542 + 0.006542 + 0.006917 + 0.006958 + 0.00675 + 0.006667 + 0.006375 + 0.006416 + 0.006166 + 0.006291 + 0.006791 + 0.007083 + 0.0105 + 0.006667 + 0.006833 + 0.006833 + 0.006667 + 0.007 + 0.006584 + 0.006584 + 0.006959 + 0.006834 + 0.010417 + 0.007 + 0.006833 + 0.006625 + 0.00725 + 0.007125 + 0.007333 + 0.007042 + 0.009834 + 0.013708 + 0.00725 + 0.006833 + 0.007208 + 0.014791 + 0.007291 + 0.006792 + 0.007917 + 0.008125 + 0.007541 + 0.007625 + 0.009666 + 0.008083 + 0.007125 + 0.007292 + 0.007291 + 0.006792 + 0.008209 + 0.0075 + 0.01 + 0.007958 + 0.007541 + 0.0075 + 0.007459 + 0.005792 + 0.006 + 0.005583 + 0.005916 + 0.005917 + 0.005708 + 0.005583 + 0.005459 + 0.005541 + 0.007833 + 0.00725 + 0.00675 + 0.005 + 0.004458 + 0.007208 + 0.006166 + 0.006542 + 0.006125 + 0.006916 + 0.006417 + 0.007125 + 0.006417 + 0.006584 + 0.006625 + 0.006583 + 0.006416 + 0.006792 + 0.006917 + 0.006458 + 0.006083 + 0.005875 + 0.006084 + 0.005792 + 0.006167 + 0.005833 + 0.006667 + 0.006459 + 0.0065 + 0.006375 + 0.013542 + 0.006417 + 0.006333 + 0.0065 + 0.006583 + 0.006375 + 0.006416 + 0.0065 + 0.006583 + 0.028625 + 0.027208 + 0.011834 + 0.00975 + 0.00875 + 0.00875 + 0.008084 + 0.007417 + 0.008417 + 0.0125 + 0.01 + 0.008459 + 0.0085 + 0.012541 + 0.010458 + 0.009333 + 0.008417 + 0.0105 + 0.009708 + 0.009667 + 0.00875 + 0.009917 + 0.009542 + 0.008583 + 0.007791 + 0.007292 + 0.00775 + 0.008375 + 0.007291 + 0.007917 + 0.007375 + 0.007 + 0.007166 + 0.012792 + 0.009833 + 0.00975 + 0.013958 + 0.008583 + 0.008125 + 0.008708 + 0.008375 + 0.010292 + 0.010125 + 0.0095 + 0.009167 + 0.010625 + 0.008333 + 0.007916 + 0.009583 + 0.010333 + 0.010041 + 0.009666 + 0.007708 + 0.006916 + 0.006375 + 0.007417 + 0.007542 + 0.007333 + 0.007541 + 0.006958 + 0.007125 + 0.006542 + 0.0095 + 0.007209 + 0.007042 + 0.005833 + 0.007958 + 0.00725 + 0.007542 + 0.007125 + 0.016667 + 0.014166 + 0.007333 + 0.008042 + 0.008542 + 0.00925 + 0.009208 + 0.008959 + 0.009375 + 0.0095 + 0.007916 + 0.007417 + 0.007 + 0.007375 + 0.007208 + 0.007709 + 0.007042 + 0.007375 + 0.006958 + 0.007125 + 0.007166 + 0.007333 + 0.007042 + 0.00725 + 0.017459 + 0.007459 + 0.007334 + 0.007167 + 0.007041 + 0.006417 + 0.007125 + 0.006917 + 0.008041 + 0.008542 + 0.007292 + 0.0075 + 0.0075 + 0.007459 + 0.007875 + 0.007791 + 0.007292 + 0.007459 + 0.007834 + 0.007584 + 0.0075 + 0.00725 + 0.009208 + 0.009 + 0.008459 + 0.008084 + 0.007583 + 0.0075 + 0.007209 + 0.008334 + 0.007208 + 0.007416 + 0.0075 + 0.00675 + 0.007542 + 0.007625 + 0.007875 + 0.007417 + 0.007084 + 0.007083 + 0.007583 + 0.007208 + 0.006375 + 0.009958 + 0.010041 + 0.00975 + 0.009459 + 0.009041 + 0.008 + 0.010458 + 0.010042 + 0.01025 + 0.0105 + 0.010542 + 0.010209 + 0.010209 + 0.0135 + 0.010583 + 0.009958 + 0.008958 + 0.008125 + 0.006875 + 0.008417 + 0.008458 + 0.007916 + 0.007334 + 0.007209 + 0.007375 + 0.008541 + 0.006458 + 0.007959 + 0.006875 + 0.00925 + 0.008708 + 0.008708 + 0.007041 + 0.007041 + 0.007542 + 0.006667 + 0.00675 + 0.006958 + 0.006875 + 0.006875 + 0.006917 + 0.007583 + 0.006542 + 0.006667 + 0.006541 + 0.006291 + 0.007083 + 0.006917 + 0.006667 + 0.006834 + 0.006833 + 0.006625 + 0.006916 + 0.006708 + 0.007583 + 0.006959 + 0.006916 + 0.006792 + 0.00675 + 0.006667 + 0.0105 + 0.007042 + 0.007084 + 0.006791 + 0.006791 + 0.006542 + 0.007833 + 0.007458 + 0.007458 + 0.006958 + 0.007125 + 0.006875 + 0.00625 + 0.0075 + 0.0075 + 0.007125 + 0.007125 + 0.007166 + 0.007041 + 0.007041 + 0.007042 + 0.007625 + 0.007208 + 0.006833 + 0.009041 + 0.008416 + 0.010583 + 0.008 + 0.007458 + 0.006958 + 0.006834 + 0.007209 + 0.007083 + 0.007292 + 0.006833 + 0.00775 + 0.007459 + 0.007667 + 0.007417 + 0.006834 + 0.012792 + 0.009459 + 0.0095 + 0.008792 + 0.008375 + 0.008417 + 0.009125 + 0.009416 + 0.009542 + 0.0085 + 0.007666 + 0.008041 + 0.008084 + 0.017292 + 0.010291 + 0.008541 + 0.006375 + 0.0065 + 0.005958 + 0.006125 + 0.0065 + 0.006375 + 0.005958 + 0.010125 + 0.006834 + 0.007708 + 0.007709 + 0.007625 + 0.006833 + 0.0075 + 0.010542 + 0.008875 + 0.008625 + 0.008375 + 0.008625 + 0.008708 + 0.008959 + 0.009167 + 0.009542 + 0.006958 + 0.006833 + 0.007 + 0.007167 + 0.006791 + 0.007208 + 0.007083 + 0.00775 + 0.007042 + 0.007291 + 0.006833 + 0.007 + 0.0065 + 0.0075 + 0.007084 + 0.006792 + 0.006667 + 0.007 + 0.006708 + 0.00675 + 0.006917 + 0.006834 + 0.00725 + 0.006459 + 0.0085 + 0.009333 + 0.009583 + 0.009458 + 0.009208 + 0.008625 + 0.008208 + 0.007625 + 0.008084 + 0.006833 + 0.007417 + 0.00725 + 0.007083 + 0.007667 + 0.007334 + 0.007792 + 0.007084 + 0.006917 + 0.007458 + 0.007208 + 0.008709 + 0.023125 + 0.014167 + 0.008375 + 0.010375 + 0.010459 + 0.009041 + 0.009292 + 0.009125 + 0.008834 + 0.009333 + 0.008542 + 0.008833 + 0.008791 + 0.0085 + 0.009708 + 0.008542 + 0.010708 + 0.013292 + 0.011042 + 0.009834 + 0.010042 + 0.011334 + 0.01125 + 0.012208 + 0.009459 + 0.0105 + 0.0095 + 0.010833 + 0.01225 + 0.010709 + 0.011375 + 0.01125 + 0.008583 + 0.007917 + 0.008083 + 0.009 + 0.038583 + 0.009625 + 0.008 + 0.008042 + 0.008125 + 0.008791 + 0.010042 + 0.008583 + 0.006958 + 0.005875 + 0.009042 + 0.008417 + 0.008042 + 0.007542 + 0.007875 + 0.008333 + 0.009583 + 0.008959 + 0.007625 + 0.00725 + 0.007583 + 0.00725 + 0.006917 + 0.007167 + 0.006708 + 0.007167 + 0.006917 + 0.006791 + 0.007291 + 0.006833 + 0.007166 + 0.006667 + 0.006834 + 0.006833 + 0.007167 + 0.006792 + 0.006958 + 0.007208 + 0.00675 + 0.006875 + 0.00675 + 0.006834 + 0.006708 + 0.006917 + 0.00675 + 0.006583 + 0.007416 + 0.007042 + 0.007167 + 0.007583 + 0.007292 + 0.006833 + 0.007125 + 0.008083 + 0.007833 + 0.007417 + 0.007583 + 0.007625 + 0.007 + 0.007042 + 0.00725 + 0.007542 + 0.007208 + 0.007708 + 0.006958 + 0.006792 + 0.007084 + 0.007042 + 0.0125 + 0.009333 + 0.010333 + 0.010375 + 0.00925 + 0.007417 + 0.007542 + 0.009459 + 0.009875 + 0.009792 + 0.009334 + 0.008916 + 0.009 + 0.007875 + 0.010333 + 0.010584 + 0.010666 + 0.010208 + 0.01 + 0.010292 + 0.009667 + 0.0125 + 0.012125 + 0.010542 + 0.008625 + 0.008541 + 0.008458 + 0.010416 + 0.008375 + 0.007791 + 0.007083 + 0.007875 + 0.007583 + 0.007667 + 0.007667 + 0.007417 + 0.009167 + 0.008375 + 0.010292 + 0.006416 + 0.006708 + 0.007042 + 0.008375 + 0.008125 + 0.007833 + 0.008625 + 0.009375 + 0.009417 + 0.00825 + 0.008375 + 0.007625 + 0.008041 + 0.008334 + 0.008708 + 0.0085 + 0.009291 + 0.009875 + 0.007709 + 0.00825 + 0.007792 + 0.008625 + 0.009 + 0.010333 + 0.009958 + 0.00975 + 0.009458 + 0.009708 + 0.008416 + 0.0095 + 0.011125 + 0.00825 + 0.012375 + 0.011625 + 0.011125 + 0.011125 + 0.011375 + 0.010416 + 0.009917 + 0.008916 + 0.008666 + 0.009667 + 0.009916 + 0.01025 + 0.010667 + 0.011209 + 0.010417 + 0.009584 + 0.009042 + 0.010583 + 0.010292 + 0.010208 + 0.008333 + 0.00775 + 0.009042 + 0.007833 + 0.006708 + 0.0085 + 0.009833 + 0.009125 + 0.008959 + 0.008708 + 0.007292 + 0.007958 + 0.006417 + 0.007542 + 0.007292 + 0.009292 + 0.009917 + 0.012583 + 0.008834 + 0.007334 + 0.0075 + 0.007375 + 0.009 + 0.008208 + 0.007917 + 0.007834 + 0.0165 + 0.010042 + 0.009667 + 0.008791 + 0.007917 + 0.008208 + 0.007334 + 0.009833 + 0.009708 + 0.00975 + 0.008875 + 0.007875 + 0.006583 + 0.006375 + 0.006 + 0.006208 + 0.006042 + 0.009709 + 0.006625 + 0.006791 + 0.00675 + 0.008333 + 0.00575 + 0.005834 + 0.005625 + 0.0075 + 0.006917 + 0.007041 + 0.007083 + 0.007333 + 0.006833 + 0.006792 + 0.006833 + 0.006792 + 0.0065 + 0.006667 + 0.007 + 0.0075 + 0.006583 + 0.006583 + 0.006584 + 0.006833 + 0.006584 + 0.007208 + 0.007583 + 0.006917 + 0.006541 + 0.006583 + 0.006916 + 0.006958 + 0.006625 + 0.007417 + 0.006667 + 0.007042 + 0.0065 + 0.006666 + 0.006875 + 0.008792 + 0.007208 + 0.007125 + 0.006792 + 0.007292 + 0.009292 + 0.00775 + 0.007417 + 0.0075 + 0.007584 + 0.007167 + 0.006916 + 0.007791 + 0.011292 + 0.009458 + 0.009125 + 0.009208 + 0.009333 + 0.009792 + 0.00775 + 0.0075 + 0.006875 + 0.007084 + 0.007917 + 0.007 + 0.007375 + 0.007125 + 0.00675 + 0.006584 + 0.007417 + 0.007291 + 0.007458 + 0.008459 + 0.008916 + 0.009208 + 0.009083 + 0.007375 + 0.0075 + 0.008417 + 0.010083 + 0.010125 + 0.010708 + 0.009083 + 0.011042 + 0.010334 + 0.009667 + 0.008209 + 0.008667 + 0.008917 + 0.008958 + 0.010958 + 0.011125 + 0.010583 + 0.0105 + 0.01 + 0.007666 + 0.006958 + 0.006667 + 0.006292 + 0.006459 + 0.005875 + 0.006208 + 0.006125 + 0.005875 + 0.007 + 0.007667 + 0.007834 + 0.006 + 0.006083 + 0.007958 + 0.006875 + 0.006959 + 0.00675 + 0.007 + 0.007 + 0.006792 + 0.007 + 0.006958 + 0.00775 + 0.007917 + 0.00675 + 0.006625 + 0.006958 + 0.007041 + 0.007042 + 0.0075 + 0.008167 + 0.007375 + 0.006792 + 0.00875 + 0.008917 + 0.009042 + 0.009 + 0.00875 + 0.007416 + 0.007208 + 0.0075 + 0.006583 + 0.007 + 0.007375 + 0.007708 + 0.00775 + 0.007083 + 0.006917 + 0.006625 + 0.007041 + 0.007083 + 0.007334 + 0.007125 + 0.007708 + 0.00875 + 0.007459 + 0.007375 + 0.007166 + 0.007625 + 0.009958 + 0.009333 + 0.009084 + 0.009167 + 0.009584 + 0.007958 + 0.006875 + 0.007334 + 0.007667 + 0.00725 + 0.007 + 0.007334 + 0.007667 + 0.00775 + 0.006958 + 0.007292 + 0.00725 + 0.006958 + 0.007375 + 0.007333 + 0.009791 + 0.009458 + 0.009041 + 0.009791 + 0.010666 + 0.008042 + 0.008084 + 0.008208 + 0.009416 + 0.010042 + 0.010292 + 0.010791 + 0.009 + 0.008083 + 0.007958 + 0.01 + 0.01025 + 0.010375 + 0.010375 + 0.009958 + 0.006042 + 0.006208 + 0.005875 + 0.00625 + 0.006459 + 0.005916 + 0.005916 + 0.006 + 0.005833 + 0.006125 + 0.007 + 0.004958 + 0.00525 + 0.004917 + 0.006666 + 0.00625 + 0.006417 + 0.006584 + 0.007 + 0.00875 + 0.00875 + 0.008625 + 0.008334 + 0.00825 + 0.007125 + 0.006584 + 0.006917 + 0.007042 + 0.006625 + 0.006583 + 0.006375 + 0.00725 + 0.008084 + 0.006292 + 0.007 + 0.006583 + 0.006833 + 0.006833 + 0.006875 + 0.007375 + 0.006625 + 0.006583 + 0.006625 + 0.006625 + 0.006334 + 0.006666 + 0.006875 + 0.006375 + 0.006417 + 0.007291 + 0.007333 + 0.006958 + 0.007041 + 0.00725 + 0.006958 + 0.006834 + 0.00725 + 0.006916 + 0.007917 + 0.007625 + 0.007042 + 0.0095 + 0.009375 + 0.009083 + 0.009041 + 0.009084 + 0.007792 + 0.007333 + 0.006916 + 0.007 + 0.007042 + 0.007083 + 0.007291 + 0.00725 + 0.007417 + 0.007084 + 0.006875 + 0.006709 + 0.006792 + 0.006917 + 0.006959 + 0.009417 + 0.009542 + 0.0095 + 0.010208 + 0.010042 + 0.007959 + 0.007625 + 0.0085 + 0.010958 + 0.009833 + 0.010208 + 0.009417 + 0.008667 + 0.007459 + 0.007542 + 0.01025 + 0.009958 + 0.010083 + 0.01075 + 0.007958 + 0.006125 + 0.006042 + 0.006167 + 0.00625 + 0.006 + 0.006167 + 0.006291 + 0.005958 + 0.00575 + 0.008166 + 0.005792 + 0.005292 + 0.005083 + 0.006416 + 0.009417 + 0.006834 + 0.006542 + 0.006417 + 0.006125 + 0.006708 + 0.006292 + 0.007 + 0.0065 + 0.006584 + 0.006959 + 0.006375 + 0.006458 + 0.006291 + 0.006625 + 0.00625 + 0.006541 + 0.007042 + 0.006666 + 0.006333 + 0.006542 + 0.006792 + 0.006334 + 0.006334 + 0.006709 + 0.00675 + 0.006791 + 0.00675 + 0.006833 + 0.006583 + 0.006625 + 0.006167 + 0.006291 + 0.006542 + 0.006292 + 0.006583 + 0.007083 + 0.006833 + 0.006792 + 0.006792 + 0.006833 + 0.007125 + 0.007333 + 0.007375 + 0.006958 + 0.007125 + 0.006625 + 0.007125 + 0.006583 + 0.009792 + 0.009958 + 0.009 + 0.008958 + 0.009625 + 0.007583 + 0.006917 + 0.007209 + 0.007 + 0.0075 + 0.005958 + 0.007166 + 0.006625 + 0.00675 + 0.006833 + 0.006917 + 0.006833 + 0.006875 + 0.007125 + 0.007208 + 0.007041 + 0.011 + 0.010167 + 0.010875 + 0.009875 + 0.009667 + 0.007667 + 0.01075 + 0.009458 + 0.0105 + 0.011083 + 0.009791 + 0.008 + 0.007542 + 0.011167 + 0.011334 + 0.01025 + 0.008541 + 0.008041 + 0.006583 + 0.006375 + 0.007125 + 0.007166 + 0.006417 + 0.006958 + 0.007334 + 0.008209 + 0.005792 + 0.006166 + 0.006375 + 0.007541 + 0.008375 + 0.00775 + 0.010667 + 0.00975 + 0.009083 + 0.008375 + 0.008 + 0.007708 + 0.007416 + 0.007917 + 0.007208 + 0.007708 + 0.007209 + 0.007458 + 0.006708 + 0.007208 + 0.007083 + 0.007125 + 0.007667 + 0.007417 + 0.006875 + 0.007042 + 0.006917 + 0.006666 + 0.006958 + 0.006917 + 0.006459 + 0.0075 + 0.006541 + 0.007667 + 0.007209 + 0.00675 + 0.00825 + 0.008333 + 0.007375 + 0.00725 + 0.008 + 0.007375 + 0.007125 + 0.007875 + 0.006958 + 0.007583 + 0.008083 + 0.00775 + 0.007083 + 0.007167 + 0.006916 + 0.007209 + 0.007709 + 0.007167 + 0.007208 + 0.007292 + 0.006917 + 0.007166 + 0.007042 + 0.012125 + 0.008667 + 0.009 + 0.008125 + 0.006875 + 0.007417 + 0.007041 + 0.007 + 0.007 + 0.0075 + 0.007583 + 0.006916 + 0.007 + 0.007083 + 0.006875 + 0.00875 + 0.009917 + 0.016583 + 0.009625 + 0.007792 + 0.007959 + 0.007375 + 0.007458 + 0.008958 + 0.00975 + 0.009833 + 0.009916 + 0.008833 + 0.008041 + 0.01025 + 0.009916 + 0.00775 + 0.0075 + 0.009208 + 0.007708 + 0.007458 + 0.008333 + 0.007958 + 0.008167 + 0.019 + 0.009958 + 0.008667 + 0.008708 + 0.007042 + 0.007875 + 0.010959 + 0.007833 + 0.00825 + 0.008167 + 0.007709 + 0.008292 + 0.008 + 0.007958 + 0.008041 + 0.008 + 0.007958 + 0.012208 + 0.008542 + 0.008959 + 0.008292 + 0.008541 + 0.008167 + 0.008167 + 0.008291 + 0.009125 + 0.007958 + 0.008708 + 0.010459 + 0.009209 + 0.008042 + 0.008 + 0.008542 + 0.00825 + 0.0085 + 0.009584 + 0.009334 + 0.009083 + 0.008583 + 0.009917 + 0.008834 + 0.008709 + 0.010041 + 0.008833 + 0.010167 + 0.009708 + 0.01 + 0.008709 + 0.010084 + 0.013792 + 0.009875 + 0.008709 + 0.007958 + 0.008166 + 0.008333 + 0.00925 + 0.01 + 0.012625 + 0.010375 + 0.022958 + 0.015458 + 0.01 + 0.008584 + 0.010459 + 0.009458 + 0.010042 + 0.012084 + 0.012417 + 0.01 + 0.010583 + 0.009666 + 0.009833 + 0.009708 + 0.010042 + 0.009917 + 0.010958 + 0.01375 + 0.008625 + 0.011 + 0.011625 + 0.011459 + 0.011709 + 0.008916 + 0.009292 + 0.008916 + 0.013167 + 0.009459 + 0.008167 + 0.008375 + 0.008791 + 0.01 + 0.010333 + 0.008875 + 0.007916 + 0.007583 + 0.006625 + 0.008833 + 0.007 + 0.006625 + 0.006541 + 0.006625 + 0.008167 + 0.008875 + 0.006833 + 0.006292 + 0.005625 + 0.008 + 0.008167 + 0.010084 + 0.00925 + 0.017042 + 0.007333 + 0.008041 + 0.008958 + 0.008375 + 0.007083 + 0.007375 + 0.006875 + 0.007208 + 0.007708 + 0.007667 + 0.008166 + 0.008542 + 0.008458 + 0.00825 + 0.007458 + 0.007708 + 0.008458 + 0.007209 + 0.007584 + 0.007917 + 0.008041 + 0.008375 + 0.007542 + 0.007291 + 0.007459 + 0.007583 + 0.008541 + 0.007792 + 0.007666 + 0.008208 + 0.007584 + 0.008041 + 0.007459 + 0.007667 + 0.007833 + 0.007792 + 0.007625 + 0.008167 + 0.007 + 0.0075 + 0.007167 + 0.0075 + 0.008792 + 0.009291 + 0.008708 + 0.008625 + 0.007875 + 0.007541 + 0.007333 + 0.008167 + 0.008417 + 0.008208 + 0.00825 + 0.01125 + 0.009792 + 0.009958 + 0.009583 + 0.0095 + 0.007958 + 0.00775 + 0.007667 + 0.007833 + 0.007084 + 0.007375 + 0.007709 + 0.009375 + 0.010917 + 0.013209 + 0.011459 + 0.009375 + 0.009 + 0.008875 + 0.008209 + 0.007417 + 0.010958 + 0.009708 + 0.00975 + 0.008625 + 0.007708 + 0.008084 + 0.010958 + 0.007792 + 0.00775 + 0.007625 + 0.006791 + 0.00575 + 0.006125 + 0.00625 + 0.006084 + 0.006542 + 0.006875 + 0.00775 + 0.005167 + 0.005291 + 0.006084 + 0.007 + 0.00675 + 0.006792 + 0.006417 + 0.006541 + 0.006875 + 0.007083 + 0.00725 + 0.006458 + 0.006833 + 0.006542 + 0.006458 + 0.006875 + 0.006709 + 0.007084 + 0.006875 + 0.006708 + 0.0065 + 0.007666 + 0.006458 + 0.007042 + 0.007208 + 0.00775 + 0.007041 + 0.006917 + 0.006833 + 0.006541 + 0.006542 + 0.007042 + 0.007417 + 0.007167 + 0.006666 + 0.007042 + 0.00675 + 0.007042 + 0.006792 + 0.006833 + 0.007541 + 0.007334 + 0.007375 + 0.007 + 0.007042 + 0.006959 + 0.007458 + 0.007375 + 0.007209 + 0.00775 + 0.006959 + 0.007334 + 0.00675 + 0.007375 + 0.00725 + 0.007167 + 0.006416 + 0.007667 + 0.007125 + 0.007084 + 0.007209 + 0.006709 + 0.006584 + 0.006625 + 0.006875 + 0.007167 + 0.009375 + 0.008833 + 0.008875 + 0.008 + 0.007166 + 0.007208 + 0.007667 + 0.00725 + 0.007875 + 0.007834 + 0.00825 + 0.009958 + 0.009792 + 0.009792 + 0.008666 + 0.007625 + 0.007917 + 0.008125 + 0.010083 + 0.009458 + 0.009625 + 0.008792 + 0.008125 + 0.009125 + 0.007833 + 0.0075 + 0.006708 + 0.006375 + 0.006333 + 0.006792 + 0.0065 + 0.006333 + 0.006333 + 0.00725 + 0.007625 + 0.007166 + 0.006584 + 0.005291 + 0.008125 + 0.006959 + 0.007208 + 0.006708 + 0.007041 + 0.006792 + 0.007542 + 0.006542 + 0.007458 + 0.006875 + 0.00725 + 0.006667 + 0.007 + 0.007459 + 0.006791 + 0.007125 + 0.008958 + 0.008416 + 0.007542 + 0.007667 + 0.007916 + 0.007417 + 0.007 + 0.008083 + 0.008917 + 0.009125 + 0.009333 + 0.009542 + 0.009084 + 0.009625 + 0.008417 + 0.008833 + 0.009 + 0.008875 + 0.008541 + 0.00875 + 0.009 + 0.008291 + 0.008209 + 0.007542 + 0.00875 + 0.008042 + 0.007625 + 0.00775 + 0.01025 + 0.011083 + 0.011209 + 0.010208 + 0.009 + 0.007334 + 0.006875 + 0.007 + 0.007458 + 0.007417 + 0.006875 + 0.007292 + 0.007667 + 0.007833 + 0.007208 + 0.006875 + 0.007791 + 0.008 + 0.007125 + 0.007375 + 0.010125 + 0.009833 + 0.009042 + 0.010417 + 0.009 + 0.007292 + 0.008042 + 0.008334 + 0.011291 + 0.008333 + 0.010583 + 0.010666 + 0.010458 + 0.009666 + 0.008666 + 0.008375 + 0.007958 + 0.007625 + 0.010959 + 0.010333 + 0.010083 + 0.009375 + 0.007917 + 0.006875 + 0.006291 + 0.007209 + 0.0075 + 0.007958 + 0.007916 + 0.006667 + 0.006459 + 0.006875 + 0.008208 + 0.008 + 0.005625 + 0.007125 + 0.009208 + 0.009542 + 0.008167 + 0.008208 + 0.00925 + 0.008833 + 0.009208 + 0.008542 + 0.007667 + 0.008125 + 0.009125 + 0.009375 + 0.008333 + 0.007291 + 0.007042 + 0.007042 + 0.007958 + 0.0075 + 0.008334 + 0.007375 + 0.007209 + 0.00675 + 0.006875 + 0.006584 + 0.007041 + 0.006333 + 0.006375 + 0.006666 + 0.006875 + 0.006375 + 0.0065 + 0.006958 + 0.006708 + 0.006833 + 0.006875 + 0.006458 + 0.006959 + 0.007125 + 0.006959 + 0.007209 + 0.007291 + 0.006959 + 0.006667 + 0.007083 + 0.006875 + 0.006542 + 0.007292 + 0.00675 + 0.007 + 0.00925 + 0.011917 + 0.009541 + 0.00875 + 0.009542 + 0.007583 + 0.007 + 0.007125 + 0.007166 + 0.006833 + 0.006958 + 0.007 + 0.007375 + 0.00725 + 0.007208 + 0.006958 + 0.006833 + 0.011333 + 0.009083 + 0.010125 + 0.009708 + 0.009792 + 0.007584 + 0.007375 + 0.007959 + 0.009459 + 0.010209 + 0.009834 + 0.009792 + 0.009416 + 0.008625 + 0.007791 + 0.010417 + 0.010125 + 0.010167 + 0.009792 + 0.010042 + 0.006125 + 0.005791 + 0.00625 + 0.006042 + 0.005792 + 0.005708 + 0.006584 + 0.007917 + 0.007708 + 0.009708 + 0.010875 + 0.008125 + 0.0075 + 0.007 + 0.010167 + 0.010334 + 0.008542 + 0.00825 + 0.008791 + 0.00725 + 0.0075 + 0.007584 + 0.008125 + 0.008291 + 0.00825 + 0.009458 + 0.008458 + 0.026625 + 0.01925 + 0.008375 + 0.00775 + 0.007042 + 0.006459 + 0.006709 + 0.007042 + 0.006958 + 0.006584 + 0.006875 + 0.0075 + 0.0075 + 0.008875 + 0.009166 + 0.007708 + 0.007542 + 0.007541 + 0.007375 + 0.008125 + 0.007417 + 0.007625 + 0.007166 + 0.008125 + 0.007916 + 0.007959 + 0.007709 + 0.0075 + 0.007792 + 0.008583 + 0.010083 + 0.00875 + 0.007416 + 0.008125 + 0.00975 + 0.008875 + 0.007917 + 0.007584 + 0.0085 + 0.007833 + 0.008542 + 0.007917 + 0.008125 + 0.007667 + 0.008166 + 0.008083 + 0.008042 + 0.008333 + 0.007792 + 0.007875 + 0.007792 + 0.008083 + 0.007791 + 0.007917 + 0.010459 + 0.010209 + 0.007917 + 0.010541 + 0.012458 + 0.010458 + 0.010083 + 0.0095 + 0.010584 + 0.009083 + 0.010208 + 0.010125 + 0.010916 + 0.008625 + 0.009334 + 0.011709 + 0.01125 + 0.010917 + 0.01125 + 0.009792 + 0.008791 + 0.008833 + 0.007583 + 0.007708 + 0.007292 + 0.008959 + 0.008834 + 0.008209 + 0.008334 + 0.010042 + 0.01025 + 0.009667 + 0.008084 + 0.009625 + 0.009208 + 0.008333 + 0.068625 + 0.013959 + 0.00975 + 0.010334 + 0.008709 + 0.008292 + 0.008375 + 0.008833 + 0.0085 + 0.00925 + 0.031125 + 0.0145 + 0.008375 + 0.008375 + 0.010375 + 0.008917 + 0.007375 + 0.007959 + 0.008375 + 0.008833 + 0.008208 + 0.008875 + 0.007917 + 0.007959 + 0.007292 + 0.0075 + 0.007334 + 0.007709 + 0.007584 + 0.00925 + 0.01125 + 0.010708 + 0.010667 + 0.011209 + 0.010792 + 0.010833 + 0.010042 + 0.009833 + 0.009458 + 0.009208 + 0.010917 + 0.010292 + 0.01175 + 0.011209 + 0.013834 + 0.010125 + 0.009125 + 0.009375 + 0.007958 + 0.009917 + 0.009917 + 0.009416 + 0.008667 + 0.008833 + 0.00875 + 0.008542 + 0.008583 + 0.008541 + 0.009125 + 0.009541 + 0.008667 + 0.008542 + 0.009042 + 0.008542 + 0.010291 + 0.008875 + 0.009375 + 0.008833 + 0.014083 + 0.009458 + 0.009791 + 0.01075 + 0.010541 + 0.013875 + 0.020125 + 0.013459 + 0.016958 + 0.01 + 0.008792 + 0.008041 + 0.008625 + 0.013375 + 0.011334 + 0.009917 + 0.007375 + 0.0075 + 0.006917 + 0.006958 + 0.007458 + 0.007167 + 0.006833 + 0.006792 + 0.006458 + 0.007416 + 0.008667 + 0.006791 + 0.005875 + 0.006 + 0.008625 + 0.00725 + 0.007208 + 0.009208 + 0.010875 + 0.009291 + 0.007917 + 0.008208 + 0.008125 + 0.007958 + 0.007333 + 0.00725 + 0.00725 + 0.006916 + 0.007 + 0.007042 + 0.007292 + 0.007541 + 0.006542 + 0.007208 + 0.006792 + 0.006708 + 0.006833 + 0.007375 + 0.0075 + 0.006792 + 0.007 + 0.00675 + 0.00725 + 0.007291 + 0.006875 + 0.007584 + 0.007334 + 0.006916 + 0.006459 + 0.007084 + 0.007792 + 0.007333 + 0.007791 + 0.007167 + 0.007541 + 0.007458 + 0.007334 + 0.007709 + 0.007458 + 0.007333 + 0.007167 + 0.008 + 0.007625 + 0.00825 + 0.007292 + 0.00725 + 0.007625 + 0.008541 + 0.009542 + 0.01125 + 0.01 + 0.009333 + 0.00825 + 0.008625 + 0.008791 + 0.008792 + 0.008291 + 0.007333 + 0.007708 + 0.007542 + 0.008 + 0.00925 + 0.009458 + 0.009333 + 0.014583 + 0.012542 + 0.0125 + 0.010459 + 0.008917 + 0.008875 + 0.008625 + 0.009208 + 0.008541 + 0.009208 + 0.007875 + 0.007833 + 0.0075 + 0.007875 + 0.01075 + 0.009708 + 0.007791 + 0.007584 + 0.007042 + 0.00625 + 0.006042 + 0.005875 + 0.0065 + 0.006417 + 0.006666 + 0.006167 + 0.007834 + 0.006959 + 0.005625 + 0.005 + 0.007166 + 0.006709 + 0.006584 + 0.00825 + 0.008083 + 0.007083 + 0.008209 + 0.009125 + 0.008709 + 0.008709 + 0.008416 + 0.009208 + 0.008667 + 0.006625 + 0.006709 + 0.006541 + 0.006708 + 0.00675 + 0.007375 + 0.008041 + 0.008208 + 0.008708 + 0.0075 + 0.006833 + 0.006875 + 0.008875 + 0.007083 + 0.008166 + 0.008167 + 0.006667 + 0.006792 + 0.007125 + 0.007167 + 0.006791 + 0.007334 + 0.006875 + 0.007667 + 0.00725 + 0.00775 + 0.007541 + 0.006959 + 0.007167 + 0.007083 + 0.008208 + 0.007417 + 0.007459 + 0.006958 + 0.006833 + 0.0075 + 0.007042 + 0.007375 + 0.007375 + 0.007708 + 0.00875 + 0.009125 + 0.011083 + 0.008458 + 0.009083 + 0.007917 + 0.0085 + 0.008333 + 0.008042 + 0.00725 + 0.007542 + 0.007125 + 0.007375 + 0.007375 + 0.007458 + 0.007208 + 0.007417 + 0.008375 + 0.010584 + 0.010583 + 0.009833 + 0.010708 + 0.01025 + 0.008333 + 0.010208 + 0.00975 + 0.01025 + 0.010209 + 0.009834 + 0.008375 + 0.010458 + 0.011292 + 0.009375 + 0.007084 + 0.006709 + 0.006875 + 0.005709 + 0.006375 + 0.007458 + 0.006375 + 0.006083 + 0.006208 + 0.006083 + 0.007917 + 0.0055 + 0.005291 + 0.005542 + 0.006791 + 0.006667 + 0.006459 + 0.007167 + 0.0065 + 0.006625 + 0.006458 + 0.006667 + 0.00675 + 0.006792 + 0.006542 + 0.006625 + 0.006541 + 0.006583 + 0.006334 + 0.006542 + 0.006958 + 0.006708 + 0.006792 + 0.006709 + 0.006667 + 0.007459 + 0.006292 + 0.007125 + 0.007375 + 0.006917 + 0.006583 + 0.007 + 0.006417 + 0.00625 + 0.006833 + 0.007042 + 0.006792 + 0.006833 + 0.006833 + 0.006458 + 0.006792 + 0.006959 + 0.007291 + 0.007375 + 0.007417 + 0.007125 + 0.007375 + 0.007125 + 0.007208 + 0.007083 + 0.008125 + 0.009166 + 0.009042 + 0.009625 + 0.009375 + 0.008125 + 0.007375 + 0.007791 + 0.007416 + 0.007 + 0.007 + 0.007167 + 0.009208 + 0.009167 + 0.009542 + 0.008791 + 0.009333 + 0.007791 + 0.007 + 0.007583 + 0.007209 + 0.007083 + 0.0075 + 0.007542 + 0.006708 + 0.007959 + 0.008 + 0.010333 + 0.009875 + 0.010416 + 0.01 + 0.009791 + 0.008458 + 0.008958 + 0.010792 + 0.010042 + 0.009875 + 0.0105 + 0.010208 + 1.575417 + 0.020292 + 0.010041 + 0.009 + 0.009916 + 0.010083 + 0.008417 + 0.007958 + 0.007166 + 0.008292 + 0.010417 + 0.011 + 0.007125 + 0.007417 + 0.008125 + 0.011458 + 0.010958 + 0.010958 + 0.011042 + 0.010458 + 0.01 + 0.01075 + 0.01025 + 0.009416 + 0.009667 + 0.010041 + 0.007959 + 0.008625 + 0.007875 + 0.00775 + 0.016709 + 0.009709 + 0.009417 + 0.009791 + 0.025625 + 0.023666 + 0.010542 + 0.009583 + 0.009792 + 0.009667 + 0.0095 + 0.00975 + 0.0095 + 0.008917 + 0.0095 + 0.008834 + 0.008042 + 0.008917 + 0.010292 + 0.009375 + 0.00925 + 0.009708 + 0.010666 + 0.008167 + 0.008166 + 0.0095 + 0.00975 + 0.009959 + 0.009334 + 0.011583 + 0.011125 + 0.010083 + 0.009916 + 0.009542 + 0.009916 + 0.009834 + 0.010125 + 0.011167 + 0.008917 + 0.010042 + 0.010125 + 0.009791 + 0.009958 + 0.010125 + 0.009291 + 0.008375 + 0.00975 + 0.009958 + 0.009792 + 0.009709 + 0.008375 + 0.008625 + 0.009667 + 0.010167 + 0.010875 + 0.01025 + 0.01475 + 0.011583 + 0.009542 + 0.013833 + 0.011458 + 0.0105 + 0.011209 + 0.011 + 0.011208 + 0.010958 + 0.011 + 0.012625 + 0.013166 + 0.011542 + 0.02375 + 0.015125 + 0.007542 + 0.006916 + 0.008667 + 0.008834 + 0.008167 + 0.007625 + 0.00775 + 0.009083 + 0.00875 + 0.008625 + 0.007792 + 0.008542 + 0.00825 + 0.008583 + 0.010416 + 0.007459 + 0.00775 + 0.008 + 0.007792 + 0.008541 + 0.009125 + 0.008666 + 0.008125 + 0.007166 + 0.007375 + 0.00775 + 0.008875 + 0.008875 + 0.008792 + 0.009166 + 0.008958 + 0.00775 + 0.0075 + 0.00875 + 0.009125 + 0.009041 + 0.009333 + 0.009 + 0.008667 + 0.0075 + 0.008417 + 0.009 + 0.008875 + 0.008959 + 0.008417 + 0.007541 + 0.007208 + 0.007083 + 0.007875 + 0.007583 + 0.009708 + 0.009083 + 0.009417 + 0.009584 + 0.009417 + 0.010208 + 0.009333 + 0.010042 + 0.008417 + 0.008834 + 0.008709 + 0.01025 + 0.01025 + 0.010125 + 0.009791 + 0.009875 + 0.008292 + 0.009208 + 0.00875 + 0.008625 + 0.00875 + 0.009084 + 0.009542 + 0.008666 + 0.008083 + 0.008458 + 0.008292 + 0.007916 + 0.007708 + 0.007375 + 0.007125 + 0.009125 + 0.00925 + 0.007667 + 0.009 + 0.011042 + 0.010584 + 0.009916 + 0.008917 + 0.012 + 0.010708 + 0.010417 + 0.009625 + 0.011083 + 0.008708 + 0.009667 + 0.012667 + 0.011125 + 0.056959 + 0.032667 + 0.00875 + 0.007 + 0.007667 + 0.007959 + 0.007375 + 0.006708 + 0.008583 + 0.008333 + 0.008458 + 0.007708 + 0.009166 + 0.007292 + 0.005958 + 0.00575 + 0.009125 + 0.00675 + 0.007542 + 0.007333 + 0.007167 + 0.007333 + 0.0075 + 0.007 + 0.007583 + 0.006708 + 0.007167 + 0.007167 + 0.007541 + 0.006958 + 0.007083 + 0.006917 + 0.007167 + 0.007167 + 0.007417 + 0.007208 + 0.007333 + 0.007083 + 0.006916 + 0.007958 + 0.006875 + 0.00725 + 0.007333 + 0.0075 + 0.007208 + 0.007458 + 0.007208 + 0.007 + 0.007833 + 0.00825 + 0.008375 + 0.008375 + 0.010125 + 0.007625 + 0.0075 + 0.007709 + 0.007458 + 0.007375 + 0.007209 + 0.009042 + 0.007375 + 0.007292 + 0.007375 + 0.007416 + 0.007667 + 0.007375 + 0.007417 + 0.008083 + 0.007709 + 0.008833 + 0.011375 + 0.008625 + 0.007792 + 0.007416 + 0.007542 + 0.007458 + 0.0085 + 0.008625 + 0.007458 + 0.007292 + 0.0095 + 0.00775 + 0.007417 + 0.007541 + 0.0075 + 0.007792 + 0.012333 + 0.008917 + 0.007708 + 0.008458 + 0.008125 + 0.008875 + 0.011208 + 0.009167 + 0.008166 + 0.008542 + 0.008 + 0.00825 + 0.00875 + 0.012125 + 0.010042 + 0.009 + 0.006583 + 0.006417 + 0.006333 + 0.00625 + 0.006416 + 0.006542 + 0.006542 + 0.011 + 0.009792 + 0.009791 + 0.010416 + 0.007084 + 0.007375 + 0.006167 + 0.008125 + 0.007792 + 0.00725 + 0.007292 + 0.008042 + 0.011041 + 0.010208 + 0.009291 + 0.009083 + 0.00825 + 0.007625 + 0.007125 + 0.007958 + 0.008833 + 0.009667 + 0.009584 + 0.009834 + 0.008709 + 0.008666 + 0.008458 + 0.008209 + 0.00825 + 0.008375 + 0.009834 + 0.010625 + 0.008916 + 0.008542 + 0.007166 + 0.009875 + 0.009667 + 0.008125 + 0.0085 + 0.00825 + 0.009208 + 0.008083 + 0.007792 + 0.008 + 0.008958 + 0.008667 + 0.008458 + 0.007792 + 0.008584 + 0.008625 + 0.009875 + 0.009375 + 0.009333 + 0.008125 + 0.00825 + 0.00875 + 0.008 + 0.012958 + 0.009375 + 0.010291 + 0.009208 + 0.010708 + 0.010292 + 0.008958 + 0.008208 + 0.008166 + 0.008584 + 0.009792 + 0.0075 + 0.007708 + 0.007541 + 0.006792 + 0.0075 + 0.007208 + 0.007166 + 0.006917 + 0.007584 + 0.006959 + 0.011583 + 0.010792 + 0.009917 + 0.009291 + 0.008417 + 0.008791 + 0.010875 + 0.009 + 0.0085 + 0.008375 + 0.00775 + 0.008375 + 0.008208 + 0.011166 + 0.008 + 0.006875 + 0.006 + 0.006 + 0.005958 + 0.006083 + 0.005709 + 0.005916 + 0.005917 + 0.00625 + 0.008917 + 0.014958 + 0.007583 + 0.008792 + 0.009708 + 0.010084 + 0.008834 + 0.008459 + 0.009166 + 0.008625 + 0.006958 + 0.007 + 0.007 + 0.0075 + 0.008333 + 0.009166 + 0.010417 + 0.010292 + 0.012084 + 0.009875 + 0.008667 + 0.008792 + 0.008667 + 0.009041 + 0.008083 + 0.007917 + 0.007917 + 0.008 + 0.006833 + 0.008041 + 0.00875 + 0.00925 + 0.009 + 0.009 + 0.008334 + 0.008459 + 0.008 + 0.008 + 0.006917 + 0.007041 + 0.006833 + 0.006833 + 0.00775 + 0.006875 + 0.007083 + 0.007125 + 0.007083 + 0.007125 + 0.006875 + 0.007583 + 0.007125 + 0.006833 + 0.006917 + 0.006625 + 0.007125 + 0.007458 + 0.006625 + 0.007208 + 0.006792 + 0.006833 + 0.007375 + 0.007125 + 0.00925 + 0.007667 + 0.007125 + 0.006541 + 0.00725 + 0.007125 + 0.007042 + 0.007208 + 0.007167 + 0.007166 + 0.007333 + 0.006875 + 0.00725 + 0.007125 + 0.00825 + 0.00775 + 0.010917 + 0.008584 + 0.008375 + 0.007584 + 0.007541 + 0.007375 + 0.008208 + 0.01 + 0.008333 + 0.008 + 0.00825 + 0.007875 + 0.007541 + 0.010542 + 0.006458 + 0.005625 + 0.006125 + 0.005959 + 0.006459 + 0.006 + 0.006375 + 0.006084 + 0.005834 + 0.006833 + 0.005208 + 0.006666 + 0.004667 + 0.00575 + 0.007125 + 0.006458 + 0.00675 + 0.006375 + 0.006625 + 0.007041 + 0.006417 + 0.0065 + 0.006458 + 0.006208 + 0.006542 + 0.00675 + 0.006375 + 0.006542 + 0.006917 + 0.007042 + 0.006375 + 0.006584 + 0.006625 + 0.006625 + 0.006333 + 0.00675 + 0.006791 + 0.006875 + 0.006542 + 0.006625 + 0.00675 + 0.007 + 0.006708 + 0.006833 + 0.006916 + 0.006625 + 0.006333 + 0.006792 + 0.006333 + 0.006416 + 0.006959 + 0.006584 + 0.007083 + 0.00675 + 0.007 + 0.006791 + 0.006875 + 0.007625 + 0.007125 + 0.007041 + 0.0075 + 0.006833 + 0.006792 + 0.006958 + 0.006917 + 0.008125 + 0.008292 + 0.008584 + 0.008542 + 0.008 + 0.008125 + 0.008708 + 0.009208 + 0.009583 + 0.009125 + 0.009542 + 0.012125 + 0.0095 + 0.008334 + 0.008542 + 0.008166 + 0.006875 + 0.006291 + 0.007375 + 0.008 + 0.007542 + 0.00775 + 0.010333 + 0.008333 + 0.007208 + 0.007625 + 0.00725 + 0.006792 + 0.007666 + 0.00975 + 0.008084 + 0.00775 + 0.007167 + 0.007666 + 0.007542 + 0.005958 + 0.005916 + 0.005667 + 0.005667 + 0.005875 + 0.006208 + 0.006042 + 0.00625 + 0.007042 + 0.007375 + 0.007417 + 0.00525 + 0.005625 + 0.005208 + 0.006792 + 0.006209 + 0.006416 + 0.006958 + 0.010875 + 0.006667 + 0.006459 + 0.006625 + 0.006209 + 0.006208 + 0.006666 + 0.006375 + 0.006167 + 0.006333 + 0.00625 + 0.006708 + 0.006166 + 0.006167 + 0.006625 + 0.006667 + 0.006291 + 0.006542 + 0.006541 + 0.006625 + 0.006125 + 0.006333 + 0.006708 + 0.006417 + 0.0065 + 0.0065 + 0.0065 + 0.006416 + 0.006334 + 0.006542 + 0.006166 + 0.006334 + 0.006875 + 0.006792 + 0.00675 + 0.006875 + 0.006958 + 0.006541 + 0.006875 + 0.007 + 0.009291 + 0.007209 + 0.007292 + 0.006833 + 0.007 + 0.007375 + 0.007 + 0.006709 + 0.0065 + 0.007375 + 0.006917 + 0.006917 + 0.006583 + 0.0065 + 0.00725 + 0.006542 + 0.006833 + 0.006958 + 0.006792 + 0.007 + 0.006667 + 0.009042 + 0.006708 + 0.006666 + 0.00675 + 0.006833 + 0.008291 + 0.009292 + 0.007625 + 0.009459 + 0.008042 + 0.007958 + 0.007083 + 0.007625 + 0.007292 + 0.007667 + 0.007666 + 0.010042 + 0.007958 + 0.008125 + 0.009667 + 0.008833 + 0.007833 + 0.00775 + 0.007084 + 0.006791 + 0.007125 + 0.007916 + 0.00825 + 0.007875 + 0.007459 + 0.006417 + 0.008583 + 0.005458 + 0.004542 + 0.005208 + 0.007458 + 0.006792 + 0.006541 + 0.006625 + 0.006542 + 0.006333 + 0.006333 + 0.006083 + 0.006208 + 0.005917 + 0.006542 + 0.006375 + 0.007084 + 0.006625 + 0.006209 + 0.006209 + 0.006125 + 0.0065 + 0.006416 + 0.006625 + 0.006333 + 0.006083 + 0.006084 + 0.006167 + 0.006416 + 0.006334 + 0.00625 + 0.006417 + 0.006166 + 0.006417 + 0.006417 + 0.006916 + 0.006292 + 0.006292 + 0.006166 + 0.006167 + 0.006625 + 0.006916 + 0.006667 + 0.00675 + 0.006625 + 0.006708 + 0.006708 + 0.007 + 0.010833 + 0.007208 + 0.006917 + 0.006833 + 0.00675 + 0.00675 + 0.006958 + 0.007125 + 0.006625 + 0.006583 + 0.006833 + 0.006916 + 0.006875 + 0.006625 + 0.006792 + 0.00675 + 0.006666 + 0.006667 + 0.006791 + 0.006583 + 0.007042 + 0.006584 + 0.00675 + 0.009 + 0.006792 + 0.0065 + 0.006709 + 0.007209 + 0.007125 + 0.007625 + 0.007417 + 0.007292 + 0.009708 + 0.008125 + 0.007334 + 0.007709 + 0.007625 + 0.00775 + 0.00725 + 0.009667 + 0.007792 + 0.007541 + 0.007333 + 0.005333 + 0.005375 + 0.005708 + 0.005375 + 0.006125 + 0.005625 + 0.005583 + 0.005541 + 0.005833 + 0.007 + 0.004792 + 0.004959 + 0.004542 + 0.005958 + 0.007167 + 0.00625 + 0.006292 + 0.006 + 0.006625 + 0.005792 + 0.005916 + 0.0065 + 0.005875 + 0.006083 + 0.00575 + 0.005625 + 0.005708 + 0.006708 + 0.006 + 0.005958 + 0.006083 + 0.00625 + 0.006291 + 0.006375 + 0.00625 + 0.006125 + 0.0065 + 0.006417 + 0.006292 + 0.00625 + 0.006167 + 0.006375 + 0.00625 + 0.006375 + 0.006333 + 0.006417 + 0.006666 + 0.00675 + 0.00725 + 0.0075 + 0.00925 + 0.008708 + 0.008875 + 0.008041 + 0.007833 + 0.008458 + 0.008208 + 0.00775 + 0.008709 + 0.008917 + 0.009291 + 0.009209 + 0.00875 + 0.008875 + 0.0095 + 0.006791 + 0.006625 + 0.008542 + 0.007833 + 0.007208 + 0.006792 + 0.006625 + 0.006708 + 0.007 + 0.006584 + 0.006959 + 0.00675 + 0.006625 + 0.006792 + 0.007041 + 0.006709 + 0.006167 + 0.006791 + 0.00675 + 0.009625 + 0.007791 + 0.007417 + 0.00725 + 0.0075 + 0.007125 + 0.009833 + 0.007458 + 0.007333 + 0.007542 + 0.007458 + 0.007375 + 0.007625 + 0.009708 + 0.007875 + 0.007666 + 0.00575 + 0.005375 + 0.005583 + 0.005667 + 0.00575 + 0.005333 + 0.006167 + 0.006041 + 0.005333 + 0.007917 + 0.007292 + 0.006833 + 0.004375 + 0.004833 + 0.006541 + 0.006375 + 0.006375 + 0.006583 + 0.006416 + 0.0065 + 0.006375 + 0.007709 + 0.006167 + 0.00625 + 0.006375 + 0.006208 + 0.005875 + 0.007375 + 0.006583 + 0.0065 + 0.006916 + 0.006833 + 0.006459 + 0.006125 + 0.006625 + 0.007375 + 0.006833 + 0.00625 + 0.006208 + 0.006875 + 0.006291 + 0.007042 + 0.0065 + 0.007084 + 0.007 + 0.006416 + 0.007 + 0.006917 + 0.00625 + 0.006542 + 0.008333 + 0.007208 + 0.007042 + 0.018791 + 0.008666 + 0.008083 + 0.007458 + 0.007875 + 0.006958 + 0.007 + 0.013917 + 0.007375 + 0.007416 + 0.008042 + 0.007 + 0.007209 + 0.007542 + 0.007667 + 0.007042 + 0.013458 + 0.007375 + 0.007666 + 0.006875 + 0.00725 + 0.006791 + 0.00825 + 0.007417 + 0.018916 + 0.008292 + 0.0075 + 0.007459 + 0.006792 + 0.007292 + 0.007083 + 0.01425 + 0.010375 + 0.008667 + 0.007833 + 0.007292 + 0.007917 + 0.0085 + 0.015042 + 0.007167 + 0.008375 + 0.008667 + 0.007583 + 0.008167 + 0.006917 + 0.01075 + 0.007958 + 0.006166 + 0.005458 + 0.005834 + 0.016542 + 0.007333 + 0.0075 + 0.007 + 0.007584 + 0.006917 + 0.008167 + 0.009 + 0.006166 + 0.005959 + 0.005334 + 0.008292 + 0.007375 + 0.007625 + 0.007667 + 0.007417 + 0.009542 + 0.009333 + 0.008833 + 0.007833 + 0.008167 + 0.00775 + 0.007917 + 0.007708 + 0.008375 + 0.007542 + 0.008 + 0.007625 + 0.008292 + 0.008625 + 0.007875 + 0.00825 + 0.008 + 0.007959 + 0.007833 + 0.007459 + 0.007458 + 0.007167 + 0.007834 + 0.00725 + 0.007125 + 0.008 + 0.007208 + 0.00725 + 0.007084 + 0.007334 + 0.007709 + 0.009625 + 0.008584 + 0.007458 + 0.007875 + 0.007166 + 0.007667 + 0.007541 + 0.011375 + 0.008042 + 0.007541 + 0.007917 + 0.007667 + 0.007625 + 0.01 + 0.010042 + 0.01225 + 0.009875 + 0.013583 + 0.007792 + 0.00775 + 0.007667 + 0.007625 + 0.007709 + 0.007584 + 0.009041 + 0.010458 + 0.007625 + 0.007583 + 0.007708 + 0.008208 + 0.007667 + 0.008208 + 0.009916 + 0.011708 + 0.0175 + 0.0125 + 0.009916 + 0.010417 + 0.009583 + 0.009167 + 0.011333 + 0.011125 + 0.012709 + 0.01225 + 0.008916 + 0.010375 + 0.01 + 0.011875 + 0.01225 + 0.010375 + 0.008667 + 0.008291 + 0.007792 + 0.008459 + 0.006833 + 0.006125 + 0.006375 + 0.00675 + 0.005958 + 0.006875 + 0.008875 + 0.008209 + 0.008584 + 0.008 + 0.008792 + 0.008791 + 0.008792 + 0.009292 + 0.01 + 0.008542 + 0.009083 + 0.008417 + 0.007959 + 0.008792 + 0.009583 + 0.010083 + 0.0095 + 0.008834 + 0.00875 + 0.008583 + 0.007167 + 0.009375 + 0.008625 + 0.009666 + 0.007959 + 0.00825 + 0.008333 + 0.008416 + 0.007334 + 0.009 + 0.0085 + 0.009875 + 0.008917 + 0.009166 + 0.012625 + 0.02 + 0.010666 + 0.011083 + 0.009084 + 0.009042 + 0.009 + 0.009541 + 0.008583 + 0.009 + 0.009541 + 0.008542 + 0.009334 + 0.010083 + 0.008541 + 0.009917 + 0.008666 + 0.008792 + 0.007125 + 0.006958 + 0.009666 + 0.009958 + 0.008959 + 0.007417 + 0.007875 + 0.008125 + 0.008541 + 0.007417 + 0.007167 + 0.007708 + 0.007125 + 0.00725 + 0.007333 + 0.007375 + 0.007333 + 0.007292 + 0.006875 + 0.007834 + 0.007583 + 0.007875 + 0.006958 + 0.010125 + 0.009666 + 0.0095 + 0.008666 + 0.008459 + 0.008208 + 0.010208 + 0.009375 + 0.009916 + 0.008542 + 0.007833 + 0.007958 + 0.008583 + 0.010542 + 0.009917 + 0.009166 + 0.00675 + 0.006291 + 0.006209 + 0.006708 + 0.006292 + 0.006791 + 0.00625 + 0.006292 + 0.005917 + 0.006875 + 0.007416 + 0.007208 + 0.004959 + 0.004959 + 0.007625 + 0.007208 + 0.007125 + 0.006625 + 0.007 + 0.006791 + 0.007 + 0.006458 + 0.006917 + 0.007083 + 0.007 + 0.006709 + 0.006542 + 0.006666 + 0.00725 + 0.006958 + 0.006833 + 0.007125 + 0.006167 + 0.006708 + 0.006708 + 0.006625 + 0.006458 + 0.0075 + 0.006583 + 0.006833 + 0.006625 + 0.006833 + 0.006583 + 0.006917 + 0.006583 + 0.007291 + 0.007167 + 0.006958 + 0.006791 + 0.006458 + 0.007333 + 0.007417 + 0.007375 + 0.006667 + 0.007042 + 0.007542 + 0.006791 + 0.007292 + 0.00725 + 0.006667 + 0.007 + 0.006708 + 0.007583 + 0.007875 + 0.007042 + 0.007291 + 0.007 + 0.007167 + 0.007167 + 0.013 + 0.009375 + 0.00875 + 0.007958 + 0.006959 + 0.007208 + 0.006958 + 0.007125 + 0.007292 + 0.007208 + 0.007625 + 0.007083 + 0.007333 + 0.007166 + 0.007167 + 0.00775 + 0.009875 + 0.010417 + 0.010416 + 0.007875 + 0.007583 + 0.00775 + 0.007416 + 0.00625 + 0.01025 + 0.009833 + 0.0095 + 0.008 + 0.007833 + 0.008 + 0.009667 + 0.007541 + 0.007625 + 0.0075 + 0.006333 + 0.005833 + 0.005792 + 0.005916 + 0.006042 + 0.005709 + 0.007167 + 0.007458 + 0.007084 + 0.005916 + 0.005167 + 0.006791 + 0.00675 + 0.006292 + 0.006458 + 0.006875 + 0.006541 + 0.006667 + 0.0065 + 0.006708 + 0.0065 + 0.006834 + 0.006459 + 0.006959 + 0.006709 + 0.007167 + 0.0065 + 0.006625 + 0.006667 + 0.006709 + 0.006917 + 0.006583 + 0.007208 + 0.006375 + 0.006958 + 0.007333 + 0.008334 + 0.007458 + 0.007041 + 0.007 + 0.006916 + 0.006584 + 0.006709 + 0.006791 + 0.006583 + 0.006292 + 0.006625 + 0.007042 + 0.007083 + 0.006917 + 0.006958 + 0.007125 + 0.007166 + 0.007125 + 0.007167 + 0.007125 + 0.007083 + 0.006875 + 0.007083 + 0.007083 + 0.006916 + 0.00725 + 0.007042 + 0.006917 + 0.007375 + 0.006709 + 0.006833 + 0.006833 + 0.006667 + 0.007 + 0.006917 + 0.007375 + 0.007291 + 0.009167 + 0.008875 + 0.008542 + 0.008 + 0.006834 + 0.00725 + 0.007625 + 0.008667 + 0.0085 + 0.007666 + 0.0085 + 0.011959 + 0.010792 + 0.010042 + 0.010167 + 0.008458 + 0.006875 + 0.007792 + 0.009708 + 0.009333 + 0.007916 + 0.007833 + 0.007417 + 0.007375 + 0.006666 + 0.006417 + 0.006166 + 0.0065 + 0.006792 + 0.006916 + 0.0065 + 0.006125 + 0.005542 + 0.006625 + 0.007667 + 0.004833 + 0.009958 + 0.0045 + 0.00675 + 0.007 + 0.006542 + 0.006166 + 0.006292 + 0.006625 + 0.006792 + 0.00625 + 0.006542 + 0.007 + 0.007083 + 0.0065 + 0.0075 + 0.006417 + 0.007208 + 0.006042 + 0.006375 + 0.006333 + 0.006917 + 0.006166 + 0.006583 + 0.006667 + 0.006667 + 0.006375 + 0.006958 + 0.006958 + 0.006375 + 0.006875 + 0.006916 + 0.006625 + 0.006083 + 0.0065 + 0.007 + 0.006708 + 0.006542 + 0.006375 + 0.007209 + 0.00725 + 0.007167 + 0.006917 + 0.007 + 0.007625 + 0.006959 + 0.007417 + 0.01 + 0.009667 + 0.00875 + 0.008875 + 0.008792 + 0.007708 + 0.006458 + 0.006875 + 0.007 + 0.006917 + 0.006458 + 0.007208 + 0.007291 + 0.006834 + 0.00725 + 0.006542 + 0.007959 + 0.00925 + 0.008709 + 0.009084 + 0.010542 + 0.008166 + 0.006959 + 0.007375 + 0.007208 + 0.006666 + 0.00725 + 0.008417 + 0.008375 + 0.00775 + 0.010542 + 0.010084 + 0.010375 + 0.01 + 0.008958 + 0.007875 + 0.006959 + 0.008958 + 0.010041 + 0.009584 + 0.00975 + 0.011917 + 0.009375 + 0.007875 + 0.007666 + 0.007542 + 0.007666 + 0.007792 + 0.006583 + 0.005875 + 0.005875 + 0.006208 + 0.007459 + 0.005209 + 0.006833 + 0.005208 + 0.0075 + 0.006542 + 0.006458 + 0.006667 + 0.006583 + 0.006625 + 0.006375 + 0.0065 + 0.006625 + 0.006459 + 0.006333 + 0.006542 + 0.006625 + 0.006875 + 0.006208 + 0.006291 + 0.006583 + 0.006625 + 0.006334 + 0.006167 + 0.007 + 0.006083 + 0.006333 + 0.0065 + 0.006834 + 0.005916 + 0.006958 + 0.006833 + 0.00825 + 0.008125 + 0.008459 + 0.008375 + 0.00825 + 0.007459 + 0.006583 + 0.006375 + 0.007042 + 0.00725 + 0.007167 + 0.006917 + 0.006375 + 0.006625 + 0.006542 + 0.006291 + 0.006709 + 0.006834 + 0.007209 + 0.006792 + 0.006958 + 0.006583 + 0.006875 + 0.006333 + 0.006708 + 0.006917 + 0.006792 + 0.0065 + 0.006458 + 0.006708 + 0.009 + 0.009084 + 0.00925 + 0.009542 + 0.009333 + 0.008708 + 0.007125 + 0.006791 + 0.007125 + 0.007208 + 0.006833 + 0.007 + 0.006375 + 0.007791 + 0.008 + 0.012625 + 0.010333 + 0.009791 + 0.010375 + 0.009791 + 0.007167 + 0.00725 + 0.0095 + 0.009792 + 0.009375 + 0.009708 + 0.009666 + 0.008083 + 0.009625 + 0.007791 + 0.00775 + 0.007583 + 0.0075 + 0.007542 + 0.006875 + 0.006 + 0.006208 + 0.005583 + 0.006292 + 0.007959 + 0.006209 + 0.0055 + 0.005167 + 0.00725 + 0.006709 + 0.00625 + 0.007042 + 0.006916 + 0.006625 + 0.006625 + 0.00625 + 0.006458 + 0.006292 + 0.006667 + 0.0065 + 0.006708 + 0.00675 + 0.006333 + 0.00625 + 0.006583 + 0.006667 + 0.006416 + 0.006333 + 0.006917 + 0.00725 + 0.006583 + 0.0065 + 0.006708 + 0.006375 + 0.012625 + 0.010208 + 0.006375 + 0.006208 + 0.007291 + 0.007625 + 0.013875 + 0.007375 + 0.006417 + 0.006667 + 0.0075 + 0.007334 + 0.007167 + 0.007666 + 0.008292 + 0.008791 + 0.009125 + 0.013208 + 0.010416 + 0.009041 + 0.009625 + 0.008292 + 0.007334 + 0.007584 + 0.007833 + 0.007583 + 0.007666 + 0.015542 + 0.008333 + 0.00725 + 0.007625 + 0.007209 + 0.007125 + 0.007416 + 0.007291 + 0.007458 + 0.010292 + 0.009416 + 0.008083 + 0.009167 + 0.009 + 0.008666 + 0.008 + 0.008041 + 0.008959 + 0.008625 + 0.008375 + 0.008 + 0.010541 + 0.009083 + 0.007708 + 0.007375 + 0.007666 + 0.008917 + 0.008 + 0.009875 + 0.009792 + 0.009167 + 0.009125 + 0.008 + 0.00625 + 0.007167 + 0.006208 + 0.006 + 0.006166 + 0.006375 + 0.006041 + 0.006333 + 0.005833 + 0.007167 + 0.008 + 0.00675 + 0.005167 + 0.005333 + 0.006958 + 0.006375 + 0.006625 + 0.006917 + 0.006875 + 0.007041 + 0.0065 + 0.008458 + 0.006625 + 0.006375 + 0.006208 + 0.0065 + 0.007 + 0.007 + 0.006584 + 0.0065 + 0.006334 + 0.006875 + 0.006542 + 0.007 + 0.007958 + 0.00775 + 0.006334 + 0.006584 + 0.006334 + 0.006917 + 0.006666 + 0.006792 + 0.00675 + 0.006333 + 0.006625 + 0.006459 + 0.006709 + 0.006417 + 0.006875 + 0.006417 + 0.008208 + 0.008375 + 0.00925 + 0.009334 + 0.009375 + 0.007708 + 0.008042 + 0.007875 + 0.007083 + 0.006875 + 0.007083 + 0.008416 + 0.008292 + 0.008417 + 0.007833 + 0.007083 + 0.00675 + 0.007416 + 0.006625 + 0.006708 + 0.007083 + 0.007 + 0.006625 + 0.006625 + 0.007375 + 0.006666 + 0.006792 + 0.007084 + 0.006833 + 0.007083 + 0.007208 + 0.009208 + 0.008708 + 0.0085 + 0.009 + 0.008 + 0.007875 + 0.007333 + 0.007833 + 0.010167 + 0.009208 + 0.009042 + 0.007792 + 0.007458 + 0.007625 + 0.007417 + 0.009541 + 0.009583 + 0.009083 + 0.008375 + 0.005916 + 0.005792 + 0.005834 + 0.005584 + 0.005709 + 0.006042 + 0.006291 + 0.006042 + 0.006208 + 0.006083 + 0.007583 + 0.005333 + 0.005292 + 0.005583 + 0.006583 + 0.006666 + 0.006375 + 0.006584 + 0.006625 + 0.006459 + 0.006709 + 0.006334 + 0.00675 + 0.006208 + 0.006041 + 0.006542 + 0.0065 + 0.006708 + 0.006458 + 0.006333 + 0.006417 + 0.006667 + 0.006542 + 0.006666 + 0.006583 + 0.00675 + 0.006375 + 0.006417 + 0.006333 + 0.0065 + 0.006292 + 0.006917 + 0.006708 + 0.006208 + 0.006208 + 0.006958 + 0.006542 + 0.006292 + 0.006917 + 0.007041 + 0.006709 + 0.006834 + 0.007 + 0.006542 + 0.006584 + 0.006542 + 0.007333 + 0.007333 + 0.007042 + 0.007167 + 0.00675 + 0.0075 + 0.007083 + 0.007083 + 0.012208 + 0.008416 + 0.008667 + 0.008 + 0.007083 + 0.014 + 0.007417 + 0.007 + 0.006917 + 0.007166 + 0.007584 + 0.00725 + 0.007208 + 0.007417 + 0.012292 + 0.007291 + 0.008583 + 0.007625 + 0.00725 + 0.00775 + 0.006833 + 0.010791 + 0.009417 + 0.009375 + 0.008417 + 0.007541 + 0.0075 + 0.009125 + 0.008417 + 0.009333 + 0.009083 + 0.008666 + 0.009833 + 0.011042 + 0.011125 + 0.009875 + 0.009417 + 0.00725 + 0.006416 + 0.00575 + 0.005791 + 0.006167 + 0.005875 + 0.005667 + 0.006208 + 0.006 + 0.007125 + 0.004833 + 0.005208 + 0.005167 + 0.00725 + 0.0065 + 0.006417 + 0.006833 + 0.00625 + 0.006334 + 0.006584 + 0.006458 + 0.006416 + 0.006292 + 0.006166 + 0.006292 + 0.006334 + 0.006209 + 0.006459 + 0.006458 + 0.006291 + 0.0065 + 0.006333 + 0.006083 + 0.006625 + 0.006708 + 0.006291 + 0.006167 + 0.006583 + 0.0065 + 0.005709 + 0.006292 + 0.005917 + 0.006542 + 0.006334 + 0.005875 + 0.006167 + 0.008875 + 0.006416 + 0.006417 + 0.007 + 0.00725 + 0.006708 + 0.006875 + 0.007 + 0.007 + 0.00675 + 0.006291 + 0.007375 + 0.006875 + 0.00675 + 0.007042 + 0.007084 + 0.006875 + 0.006667 + 0.011708 + 0.008167 + 0.008 + 0.008167 + 0.006834 + 0.007042 + 0.006125 + 0.006334 + 0.006709 + 0.006708 + 0.006875 + 0.00675 + 0.00675 + 0.006875 + 0.006917 + 0.007042 + 0.007042 + 0.006833 + 0.006916 + 0.006625 + 0.009458 + 0.009375 + 0.009291 + 0.008125 + 0.007083 + 0.0075 + 0.009083 + 0.008542 + 0.0095 + 0.008625 + 0.007042 + 0.007375 + 0.007916 + 0.009083 + 0.009 + 0.008791 + 0.007 + 0.006042 + 0.005416 + 0.005667 + 0.005791 + 0.005542 + 0.006 + 0.006292 + 0.005833 + 0.006417 + 0.005917 + 0.00625 + 0.004792 + 0.005208 + 0.006625 + 0.006791 + 0.006417 + 0.006125 + 0.006334 + 0.008 + 0.00825 + 0.008291 + 0.007833 + 0.00775 + 0.007042 + 0.007125 + 0.007542 + 0.009 + 0.009209 + 0.008709 + 0.009834 + 0.008042 + 0.007791 + 0.007375 + 0.00825 + 0.007583 + 0.006917 + 0.008083 + 0.007708 + 0.007834 + 0.00925 + 0.008959 + 0.009 + 0.007458 + 0.007625 + 0.007333 + 0.007208 + 0.007208 + 0.007333 + 0.006958 + 0.007875 + 0.006791 + 0.006708 + 0.006792 + 0.006958 + 0.006833 + 0.007125 + 0.007041 + 0.006875 + 0.007125 + 0.007542 + 0.006834 + 0.006875 + 0.007292 + 0.006625 + 0.006875 + 0.006875 + 0.007 + 0.011167 + 0.009209 + 0.00875 + 0.009 + 0.008583 + 0.007792 + 0.007167 + 0.0065 + 0.006666 + 0.007 + 0.006625 + 0.006959 + 0.006875 + 0.006958 + 0.007041 + 0.006708 + 0.008334 + 0.009584 + 0.01025 + 0.009875 + 0.010125 + 0.009667 + 0.007958 + 0.009625 + 0.009291 + 0.01 + 0.010333 + 0.0095 + 0.008042 + 0.007625 + 0.009459 + 0.009959 + 0.007334 + 0.007459 + 0.007584 + 0.007709 + 0.006417 + 0.005791 + 0.005833 + 0.005667 + 0.006 + 0.00675 + 0.007375 + 0.008458 + 0.00675 + 0.00675 + 0.008333 + 0.0085 + 0.007791 + 0.006542 + 0.0065 + 0.006416 + 0.006792 + 0.009667 + 0.007667 + 0.00625 + 0.006584 + 0.0065 + 0.006417 + 0.00625 + 0.006375 + 0.007125 + 0.005959 + 0.006709 + 0.006709 + 0.006625 + 0.006583 + 0.0065 + 0.006458 + 0.006792 + 0.006625 + 0.006333 + 0.006708 + 0.007125 + 0.006459 + 0.006042 + 0.0065 + 0.006584 + 0.006792 + 0.006458 + 0.006417 + 0.006084 + 0.007166 + 0.006792 + 0.006875 + 0.006875 + 0.007041 + 0.006834 + 0.00775 + 0.007708 + 0.007083 + 0.007042 + 0.00675 + 0.006708 + 0.006708 + 0.007042 + 0.00725 + 0.007458 + 0.007625 + 0.0095 + 0.01 + 0.009083 + 0.009833 + 0.009209 + 0.010417 + 0.009958 + 0.008791 + 0.006917 + 0.007 + 0.006834 + 0.0065 + 0.006584 + 0.007 + 0.007125 + 0.007625 + 0.009625 + 0.011208 + 0.009625 + 0.011875 + 0.010875 + 0.009583 + 0.009667 + 0.010042 + 0.011291 + 0.009875 + 0.009458 + 0.01025 + 0.009291 + 0.00975 + 0.009541 + 0.010208 + 0.009042 + 0.007 + 0.007209 + 0.007791 + 0.007792 + 0.007167 + 0.006917 + 0.007875 + 0.007458 + 0.008041 + 0.008 + 0.009791 + 0.008125 + 0.007875 + 0.007042 + 0.008375 + 0.008125 + 0.007708 + 0.007875 + 0.008 + 0.009084 + 0.008792 + 0.0075 + 0.008375 + 0.008875 + 0.008417 + 0.007334 + 0.00775 + 0.008709 + 0.009584 + 0.0085 + 0.008083 + 0.00725 + 0.009417 + 0.01325 + 0.011667 + 0.0125 + 0.010209 + 0.009959 + 0.010625 + 0.008625 + 0.008542 + 0.007791 + 0.008208 + 0.007875 + 0.0095 + 0.009333 + 0.009042 + 0.01 + 0.008458 + 0.00875 + 0.009125 + 0.007208 + 0.007667 + 0.00725 + 0.00725 + 0.007583 + 0.007333 + 0.008917 + 0.011459 + 0.010833 + 0.010167 + 0.009584 + 0.007417 + 0.006958 + 0.007083 + 0.007917 + 0.00725 + 0.007333 + 0.007375 + 0.007375 + 0.007458 + 0.0075 + 0.007375 + 0.007667 + 0.008042 + 0.006333 + 0.007625 + 0.007541 + 0.007333 + 0.009375 + 0.0095 + 0.009 + 0.009 + 0.009667 + 0.007916 + 0.008958 + 0.00775 + 0.007917 + 0.007916 + 0.00975 + 0.010791 + 0.010583 + 0.01025 + 0.010875 + 0.0085 + 0.007917 + 0.0105 + 0.010625 + 0.01 + 0.01 + 0.009459 + 0.007 + 0.006042 + 0.005917 + 0.006542 + 0.006041 + 0.0065 + 0.006833 + 0.006583 + 0.006625 + 0.007958 + 0.005417 + 0.005666 + 0.005542 + 0.007542 + 0.007125 + 0.00725 + 0.007125 + 0.006917 + 0.007208 + 0.00675 + 0.006875 + 0.007 + 0.006125 + 0.006875 + 0.007041 + 0.007 + 0.006292 + 0.006875 + 0.0065 + 0.006666 + 0.006667 + 0.007041 + 0.006875 + 0.007083 + 0.006791 + 0.006584 + 0.007042 + 0.006625 + 0.006541 + 0.007042 + 0.006875 + 0.00875 + 0.006959 + 0.006792 + 0.007083 + 0.006791 + 0.006583 + 0.007042 + 0.007291 + 0.006667 + 0.008167 + 0.01 + 0.008292 + 0.009584 + 0.023959 + 0.020459 + 0.009834 + 0.010042 + 0.007541 + 0.007083 + 0.007792 + 0.010292 + 0.009166 + 0.009208 + 0.008542 + 0.008375 + 0.008084 + 0.008959 + 0.009417 + 0.009416 + 0.009792 + 0.009042 + 0.009333 + 0.007125 + 0.007417 + 0.007041 + 0.007292 + 0.007708 + 0.008833 + 0.008583 + 0.007291 + 0.010167 + 0.009917 + 0.009291 + 0.012167 + 0.011 + 0.00825 + 0.007791 + 0.008125 + 0.01025 + 0.009709 + 0.00875 + 0.0085 + 0.007583 + 0.008167 + 0.007708 + 0.010208 + 0.0095 + 0.009708 + 0.008125 + 0.006334 + 0.006375 + 0.006333 + 0.006333 + 0.006334 + 0.00575 + 0.006375 + 0.007333 + 0.0065 + 0.006125 + 0.007875 + 0.006958 + 0.005416 + 0.005958 + 0.007833 + 0.006833 + 0.007042 + 0.006791 + 0.006875 + 0.006625 + 0.007125 + 0.006667 + 0.006583 + 0.006291 + 0.006542 + 0.006916 + 0.007917 + 0.007292 + 0.006583 + 0.006667 + 0.006792 + 0.006666 + 0.006375 + 0.007708 + 0.008208 + 0.007083 + 0.00725 + 0.00675 + 0.006875 + 0.006833 + 0.00675 + 0.006584 + 0.006625 + 0.007334 + 0.00875 + 0.009625 + 0.0085 + 0.008666 + 0.00725 + 0.00725 + 0.00775 + 0.006708 + 0.007375 + 0.007041 + 0.006917 + 0.007208 + 0.006875 + 0.007459 + 0.007375 + 0.00725 + 0.007167 + 0.009334 + 0.008625 + 0.008542 + 0.008166 + 0.007375 + 0.007042 + 0.007375 + 0.00725 + 0.007167 + 0.007083 + 0.007292 + 0.00775 + 0.008584 + 0.007125 + 0.007042 + 0.008875 + 0.009666 + 0.009208 + 0.00875 + 0.009125 + 0.012291 + 0.008042 + 0.008292 + 0.009125 + 0.00775 + 0.008625 + 0.009125 + 0.00875 + 0.009083 + 0.00925 + 0.009125 + 0.008625 + 0.008125 + 0.007458 + 0.009333 + 0.011125 + 0.010208 + 0.010333 + 0.010166 + 0.008042 + 0.006416 + 0.00625 + 0.006042 + 0.006209 + 0.007375 + 0.006417 + 0.006208 + 0.006167 + 0.006792 + 0.007458 + 0.004583 + 0.006083 + 0.005 + 0.006834 + 0.006459 + 0.006792 + 0.007041 + 0.007 + 0.006875 + 0.006584 + 0.006708 + 0.006541 + 0.0065 + 0.006875 + 0.006958 + 0.006375 + 0.006833 + 0.00675 + 0.006958 + 0.006333 + 0.007 + 0.007125 + 0.006541 + 0.00625 + 0.006584 + 0.006834 + 0.006584 + 0.006792 + 0.006417 + 0.00675 + 0.006917 + 0.006292 + 0.006666 + 0.006958 + 0.00675 + 0.006292 + 0.008875 + 0.008583 + 0.008792 + 0.009917 + 0.008875 + 0.00775 + 0.007208 + 0.007 + 0.006958 + 0.006708 + 0.0075 + 0.007542 + 0.0075 + 0.006792 + 0.009292 + 0.009208 + 0.009458 + 0.008917 + 0.0095 + 0.00775 + 0.007416 + 0.007292 + 0.006833 + 0.007 + 0.007125 + 0.007083 + 0.006792 + 0.007416 + 0.006833 + 0.00675 + 0.007 + 0.006917 + 0.006666 + 0.006958 + 0.0095 + 0.00925 + 0.009292 + 0.010375 + 0.010042 + 0.008042 + 0.007375 + 0.007625 + 0.009458 + 0.009875 + 0.009792 + 0.009334 + 0.010084 + 0.008584 + 0.007417 + 0.009667 + 0.010542 + 0.00975 + 0.010375 + 0.007583 + 0.006333 + 0.0055 + 0.005625 + 0.006958 + 0.007583 + 0.00775 + 0.007709 + 0.007542 + 0.007916 + 0.008208 + 0.004792 + 0.005125 + 0.005459 + 0.006625 + 0.006916 + 0.006542 + 0.006291 + 0.006333 + 0.006375 + 0.006375 + 0.006667 + 0.006792 + 0.006458 + 0.006208 + 0.006 + 0.006125 + 0.006542 + 0.006209 + 0.006792 + 0.006625 + 0.006292 + 0.007 + 0.006417 + 0.007208 + 0.007625 + 0.007708 + 0.007542 + 0.007959 + 0.006459 + 0.007375 + 0.007042 + 0.006792 + 0.006667 + 0.006875 + 0.006791 + 0.006667 + 0.006917 + 0.008166 + 0.008375 + 0.008 + 0.015041 + 0.008708 + 0.007167 + 0.006792 + 0.007583 + 0.007166 + 0.007542 + 0.007208 + 0.007041 + 0.007333 + 0.009958 + 0.01 + 0.009583 + 0.00975 + 0.009334 + 0.007542 + 0.007583 + 0.008084 + 0.007791 + 0.008667 + 0.009291 + 0.010042 + 0.027834 + 0.015167 + 0.007708 + 0.007542 + 0.007875 + 0.008 + 0.008209 + 0.008 + 0.007958 + 0.00775 + 0.007375 + 0.007625 + 0.013167 + 0.010084 + 0.010042 + 0.00925 + 0.00775 + 0.008292 + 0.010667 + 0.009375 + 0.009417 + 0.009959 + 0.009834 + 0.008125 + 0.007917 + 0.011125 + 0.011709 + 0.0105 + 0.008792 + 0.008292 + 0.007958 + 0.006833 + 0.005625 + 0.005833 + 0.00575 + 0.00625 + 0.006334 + 0.007416 + 0.005917 + 0.005833 + 0.005375 + 0.007625 + 0.007709 + 0.006584 + 0.006834 + 0.006458 + 0.00675 + 0.00675 + 0.006792 + 0.006542 + 0.006667 + 0.006625 + 0.007042 + 0.007042 + 0.006583 + 0.00675 + 0.006458 + 0.006042 + 0.006625 + 0.006792 + 0.006459 + 0.006583 + 0.006583 + 0.00675 + 0.006791 + 0.006417 + 0.0065 + 0.006625 + 0.006583 + 0.006416 + 0.006667 + 0.006834 + 0.00675 + 0.00675 + 0.007125 + 0.007875 + 0.007791 + 0.007417 + 0.007333 + 0.006917 + 0.00725 + 0.006959 + 0.0065 + 0.00725 + 0.006875 + 0.00725 + 0.007458 + 0.006833 + 0.007208 + 0.008333 + 0.009666 + 0.009542 + 0.009292 + 0.009208 + 0.008791 + 0.008333 + 0.00725 + 0.0095 + 0.008666 + 0.008375 + 0.008292 + 0.006958 + 0.006875 + 0.007125 + 0.007166 + 0.007292 + 0.007083 + 0.007375 + 0.00725 + 0.006875 + 0.006917 + 0.007167 + 0.008 + 0.00975 + 0.009667 + 0.009125 + 0.008583 + 0.007375 + 0.007583 + 0.007083 + 0.010708 + 0.009458 + 0.009042 + 0.008083 + 0.007666 + 0.008167 + 0.009625 + 0.00925 + 0.0075 + 0.00675 + 0.006542 + 0.006 + 0.006166 + 0.00575 + 0.005792 + 0.006125 + 0.005125 + 0.007 + 0.007375 + 0.007292 + 0.004833 + 0.005375 + 0.006792 + 0.006375 + 0.006542 + 0.006708 + 0.006916 + 0.006792 + 0.006709 + 0.006958 + 0.006458 + 0.006375 + 0.006333 + 0.006916 + 0.006458 + 0.006833 + 0.006333 + 0.006708 + 0.006292 + 0.006209 + 0.006709 + 0.006625 + 0.006667 + 0.006666 + 0.006542 + 0.006125 + 0.006417 + 0.006958 + 0.006667 + 0.007125 + 0.006541 + 0.006417 + 0.007125 + 0.006542 + 0.006667 + 0.006459 + 0.006667 + 0.006667 + 0.007334 + 0.006875 + 0.006792 + 0.007291 + 0.007042 + 0.007459 + 0.007291 + 0.007167 + 0.00725 + 0.006916 + 0.006709 + 0.006875 + 0.007375 + 0.007125 + 0.006958 + 0.007041 + 0.006792 + 0.007416 + 0.00725 + 0.006875 + 0.006833 + 0.009 + 0.009 + 0.008792 + 0.008291 + 0.007667 + 0.007208 + 0.007416 + 0.007083 + 0.008333 + 0.007959 + 0.007375 + 0.007 + 0.007791 + 0.009084 + 0.008167 + 0.010208 + 0.009209 + 0.009042 + 0.007833 + 0.007458 + 0.00775 + 0.007125 + 0.00975 + 0.009416 + 0.008792 + 0.009875 + 0.01 + 0.010333 + 0.011167 + 0.007 + 0.006 + 0.006083 + 0.006375 + 0.005834 + 0.005458 + 0.005708 + 0.006 + 0.005625 + 0.006875 + 0.007333 + 0.005083 + 0.005167 + 0.005125 + 0.006709 + 0.006541 + 0.006584 + 0.006167 + 0.006458 + 0.006416 + 0.006292 + 0.018209 + 0.006709 + 0.0065 + 0.006542 + 0.006917 + 0.00675 + 0.006709 + 0.006416 + 0.006667 + 0.00625 + 0.005917 + 0.006334 + 0.0065 + 0.006417 + 0.006458 + 0.006625 + 0.006459 + 0.00675 + 0.006417 + 0.006791 + 0.006542 + 0.006417 + 0.006542 + 0.006292 + 0.006458 + 0.0065 + 0.006584 + 0.006459 + 0.006167 + 0.006875 + 0.006709 + 0.006959 + 0.007042 + 0.006583 + 0.006541 + 0.006875 + 0.007625 + 0.007 + 0.006833 + 0.008916 + 0.006667 + 0.0065 + 0.006375 + 0.006875 + 0.007584 + 0.006916 + 0.006958 + 0.007084 + 0.006834 + 0.006792 + 0.006708 + 0.006667 + 0.006625 + 0.007209 + 0.006834 + 0.009 + 0.008084 + 0.008459 + 0.008292 + 0.006667 + 0.006459 + 0.00675 + 0.007333 + 0.007917 + 0.008292 + 0.008625 + 0.010584 + 0.009584 + 0.009209 + 0.008916 + 0.008125 + 0.007 + 0.00775 + 0.009042 + 0.009458 + 0.008917 + 0.008042 + 0.015541 + 0.010125 + 0.008125 + 0.006167 + 0.006375 + 0.006625 + 0.007 + 0.006375 + 0.006417 + 0.006208 + 0.007125 + 0.006083 + 0.008792 + 0.006125 + 0.005208 + 0.005875 + 0.007791 + 0.00725 + 0.007208 + 0.006875 + 0.007208 + 0.006917 + 0.006708 + 0.0065 + 0.006709 + 0.007209 + 0.006458 + 0.00725 + 0.006917 + 0.006916 + 0.007 + 0.007 + 0.006541 + 0.007166 + 0.007458 + 0.007041 + 0.006708 + 0.007334 + 0.007459 + 0.007167 + 0.007041 + 0.007167 + 0.007167 + 0.007291 + 0.007125 + 0.007167 + 0.006708 + 0.006791 + 0.006958 + 0.006875 + 0.007083 + 0.007083 + 0.007709 + 0.008209 + 0.007542 + 0.007 + 0.007334 + 0.007334 + 0.007084 + 0.010334 + 0.007416 + 0.007167 + 0.010042 + 0.00875 + 0.008625 + 0.007875 + 0.007292 + 0.009042 + 0.006917 + 0.007542 + 0.008583 + 0.007333 + 0.007291 + 0.00725 + 0.007416 + 0.007292 + 0.007709 + 0.008083 + 0.008625 + 0.007334 + 0.007583 + 0.007125 + 0.007709 + 0.008916 + 0.009042 + 0.010208 + 0.007916 + 0.0095 + 0.007959 + 0.008292 + 0.00825 + 0.007666 + 0.009958 + 0.009792 + 0.010416 + 0.008542 + 0.007959 + 0.008042 + 0.007667 + 0.009833 + 0.009833 + 0.0095 + 0.00825 + 0.006333 + 0.006125 + 0.00575 + 0.006292 + 0.006166 + 0.006083 + 0.006083 + 0.006167 + 0.005792 + 0.006167 + 0.006917 + 0.006375 + 0.005875 + 0.005625 + 0.007334 + 0.006875 + 0.006667 + 0.006416 + 0.006667 + 0.006625 + 0.007041 + 0.006833 + 0.00675 + 0.006625 + 0.007292 + 0.006458 + 0.0065 + 0.006458 + 0.006667 + 0.00675 + 0.006833 + 0.00675 + 0.007083 + 0.006667 + 0.006333 + 0.006583 + 0.006208 + 0.006875 + 0.006458 + 0.006917 + 0.006625 + 0.006541 + 0.0065 + 0.006667 + 0.006792 + 0.007 + 0.006416 + 0.006709 + 0.006667 + 0.006625 + 0.007125 + 0.007 + 0.006959 + 0.00675 + 0.006708 + 0.007583 + 0.00675 + 0.008333 + 0.008417 + 0.008416 + 0.007917 + 0.007 + 0.007208 + 0.007375 + 0.007958 + 0.0085 + 0.008542 + 0.007958 + 0.006541 + 0.0075 + 0.007583 + 0.0075 + 0.00675 + 0.006708 + 0.007375 + 0.007041 + 0.007334 + 0.007084 + 0.007375 + 0.007292 + 0.006916 + 0.006958 + 0.006792 + 0.006792 + 0.00975 + 0.009584 + 0.010125 + 0.00875 + 0.007708 + 0.007625 + 0.009167 + 0.009 + 0.007958 + 0.009625 + 0.009666 + 0.008333 + 0.007709 + 0.009 + 0.012542 + 0.010542 + 0.0075 + 0.007875 + 0.007542 + 0.006583 + 0.006 + 0.005875 + 0.005958 + 0.005625 + 0.0055 + 0.005959 + 0.007209 + 0.006625 + 0.005375 + 0.005417 + 0.006667 + 0.006791 + 0.006084 + 0.006125 + 0.00675 + 0.007083 + 0.006667 + 0.006875 + 0.007708 + 0.008166 + 0.0085 + 0.008583 + 0.009125 + 0.008875 + 0.0095 + 0.00725 + 0.007125 + 0.00675 + 0.006875 + 0.0065 + 0.007208 + 0.006959 + 0.007167 + 0.006666 + 0.00675 + 0.006667 + 0.007916 + 0.007667 + 0.006834 + 0.006834 + 0.007 + 0.00675 + 0.007 + 0.006833 + 0.006375 + 0.007083 + 0.007375 + 0.007584 + 0.007459 + 0.007542 + 0.007166 + 0.006667 + 0.006542 + 0.0075 + 0.0075 + 0.007292 + 0.0075 + 0.007375 + 0.007042 + 0.006667 + 0.0075 + 0.007208 + 0.007166 + 0.007834 + 0.010083 + 0.009583 + 0.009083 + 0.009125 + 0.008833 + 0.008375 + 0.007042 + 0.00775 + 0.006791 + 0.007208 + 0.007042 + 0.007292 + 0.007208 + 0.0075 + 0.006542 + 0.007458 + 0.008166 + 0.009958 + 0.010541 + 0.01 + 0.009708 + 0.009791 + 0.008375 + 0.009708 + 0.009708 + 0.01 + 0.010541 + 0.009833 + 0.008083 + 0.007833 + 0.01 + 0.00975 + 0.007833 + 0.007958 + 0.007583 + 0.007458 + 0.006 + 0.005917 + 0.006166 + 0.005959 + 0.006125 + 0.006292 + 0.010291 + 0.005958 + 0.006292 + 0.00475 + 0.006458 + 0.006292 + 0.006875 + 0.0065 + 0.006375 + 0.0065 + 0.006791 + 0.006459 + 0.006208 + 0.006208 + 0.006667 + 0.006334 + 0.006625 + 0.006958 + 0.0065 + 0.006375 + 0.006708 + 0.006541 + 0.007042 + 0.006542 + 0.006583 + 0.006541 + 0.007 + 0.006375 + 0.006541 + 0.006542 + 0.006625 + 0.006625 + 0.006292 + 0.006916 + 0.006167 + 0.006834 + 0.006625 + 0.006417 + 0.006625 + 0.006583 + 0.006792 + 0.007417 + 0.006916 + 0.007084 + 0.006542 + 0.006791 + 0.007042 + 0.007083 + 0.007375 + 0.007291 + 0.007167 + 0.006625 + 0.006875 + 0.007042 + 0.007042 + 0.006958 + 0.006833 + 0.007 + 0.007167 + 0.007084 + 0.008834 + 0.009125 + 0.009208 + 0.007333 + 0.007875 + 0.007916 + 0.006792 + 0.007458 + 0.006958 + 0.007 + 0.006833 + 0.007208 + 0.007375 + 0.007167 + 0.0075 + 0.008 + 0.0105 + 0.01075 + 0.009708 + 0.01 + 0.009709 + 0.008416 + 0.0075 + 0.010875 + 0.010042 + 0.009958 + 0.009625 + 0.009875 + 0.007625 + 0.00925 + 0.01 + 0.007333 + 0.007333 + 0.007417 + 0.007667 + 0.006041 + 0.006042 + 0.005625 + 0.005792 + 0.005875 + 0.00775 + 0.005792 + 0.005958 + 0.005125 + 0.007084 + 0.006834 + 0.006334 + 0.006375 + 0.006167 + 0.006417 + 0.006333 + 0.006083 + 0.006333 + 0.006208 + 0.005833 + 0.006583 + 0.006375 + 0.006333 + 0.006292 + 0.006042 + 0.00625 + 0.006292 + 0.0065 + 0.006584 + 0.00625 + 0.006375 + 0.006292 + 0.006208 + 0.006541 + 0.006292 + 0.006083 + 0.006125 + 0.005833 + 0.006208 + 0.006333 + 0.006083 + 0.006375 + 0.006416 + 0.00675 + 0.00625 + 0.006958 + 0.00725 + 0.007 + 0.006625 + 0.006916 + 0.006917 + 0.006917 + 0.006583 + 0.006958 + 0.007042 + 0.006791 + 0.007958 + 0.007917 + 0.006917 + 0.006708 + 0.0065 + 0.006417 + 0.007042 + 0.007042 + 0.006417 + 0.006875 + 0.00775 + 0.008792 + 0.0075 + 0.0065 + 0.007 + 0.007 + 0.006875 + 0.007916 + 0.007959 + 0.008166 + 0.00675 + 0.006875 + 0.007042 + 0.007541 + 0.009 + 0.009083 + 0.0105 + 0.010125 + 0.00975 + 0.008875 + 0.010917 + 0.009959 + 0.01 + 0.011917 + 0.00975 + 0.007375 + 0.00725 + 0.007375 + 0.006875 + 0.009209 + 0.006834 + 0.007125 + 0.006792 + 0.006167 + 0.005416 + 0.006334 + 0.005834 + 0.0065 + 0.006 + 0.006833 + 0.008209 + 0.007417 + 0.005792 + 0.006292 + 0.007208 + 0.007375 + 0.006625 + 0.00625 + 0.006583 + 0.007667 + 0.006625 + 0.006458 + 0.006708 + 0.006542 + 0.006541 + 0.00675 + 0.007084 + 0.006917 + 0.0065 + 0.00625 + 0.00675 + 0.006958 + 0.006542 + 0.006792 + 0.0075 + 0.00725 + 0.006625 + 0.007208 + 0.006458 + 0.006667 + 0.006291 + 0.006958 + 0.007292 + 0.006791 + 0.007167 + 0.008417 + 0.007042 + 0.007667 + 0.006958 + 0.007375 + 0.008125 + 0.007959 + 0.007084 + 0.007334 + 0.00725 + 0.007625 + 0.006875 + 0.007416 + 0.007125 + 0.007166 + 0.00775 + 0.007667 + 0.007708 + 0.006834 + 0.006917 + 0.007417 + 0.008792 + 0.008416 + 0.008417 + 0.007458 + 0.007666 + 0.006667 + 0.007292 + 0.006916 + 0.007834 + 0.006917 + 0.01075 + 0.010333 + 0.009875 + 0.009583 + 0.009458 + 0.00825 + 0.0085 + 0.009375 + 0.01125 + 0.01125 + 0.009125 + 0.010583 + 0.009875 + 0.009083 + 0.008125 + 0.007459 + 0.006584 + 0.008167 + 0.008 + 0.00925 + 0.009458 + 0.009667 + 0.008084 + 0.007583 + 0.006416 + 0.006084 + 0.006333 + 0.006291 + 0.006208 + 0.005958 + 0.006083 + 0.005875 + 0.006417 + 0.006541 + 0.008084 + 0.006667 + 0.004583 + 0.005 + 0.006958 + 0.006084 + 0.006625 + 0.006292 + 0.00725 + 0.007041 + 0.006375 + 0.008125 + 0.00675 + 0.006208 + 0.006375 + 0.006583 + 0.006833 + 0.006292 + 0.006334 + 0.00675 + 0.0065 + 0.00675 + 0.006708 + 0.006416 + 0.006917 + 0.006708 + 0.006833 + 0.006792 + 0.006459 + 0.006709 + 0.006292 + 0.007208 + 0.006584 + 0.006959 + 0.005959 + 0.006125 + 0.006667 + 0.006625 + 0.006792 + 0.006042 + 0.007 + 0.007 + 0.007042 + 0.007125 + 0.007 + 0.006709 + 0.0065 + 0.007375 + 0.006958 + 0.007125 + 0.008875 + 0.008 + 0.00825 + 0.008 + 0.007 + 0.007291 + 0.006667 + 0.00775 + 0.006709 + 0.006709 + 0.006958 + 0.006458 + 0.006583 + 0.006792 + 0.007084 + 0.006958 + 0.006916 + 0.006709 + 0.007084 + 0.006834 + 0.008584 + 0.0085 + 0.00825 + 0.007917 + 0.007666 + 0.007834 + 0.0075 + 0.00725 + 0.00925 + 0.009542 + 0.008833 + 0.008291 + 0.006792 + 0.007542 + 0.007708 + 0.008958 + 0.008917 + 0.009209 + 0.008125 + 0.007542 + 0.006209 + 0.006334 + 0.005625 + 0.005333 + 0.005666 + 0.006583 + 0.006167 + 0.006041 + 0.006167 + 0.006291 + 0.007167 + 0.005209 + 0.004666 + 0.00525 + 0.0065 + 0.006416 + 0.006625 + 0.006208 + 0.00625 + 0.006709 + 0.006166 + 0.006459 + 0.006459 + 0.007166 + 0.007542 + 0.0075 + 0.007125 + 0.006458 + 0.006416 + 0.006167 + 0.006625 + 0.007125 + 0.006708 + 0.006916 + 0.006542 + 0.006416 + 0.0065 + 0.007084 + 0.007375 + 0.006541 + 0.006458 + 0.006583 + 0.006375 + 0.006791 + 0.006458 + 0.007 + 0.0065 + 0.006292 + 0.006625 + 0.006541 + 0.006458 + 0.007375 + 0.006541 + 0.006542 + 0.007042 + 0.007583 + 0.007417 + 0.007625 + 0.008084 + 0.0075 + 0.007084 + 0.009042 + 0.008583 + 0.008458 + 0.008375 + 0.006958 + 0.007083 + 0.007042 + 0.006833 + 0.00725 + 0.007209 + 0.007584 + 0.007 + 0.006792 + 0.007042 + 0.006542 + 0.006209 + 0.007 + 0.006667 + 0.006791 + 0.006875 + 0.009708 + 0.009042 + 0.009209 + 0.008709 + 0.010334 + 0.010042 + 0.009375 + 0.008959 + 0.007375 + 0.0095 + 0.009333 + 0.008167 + 0.008584 + 0.0075 + 0.00775 + 0.007542 + 0.0095 + 0.009167 + 0.009 + 0.008084 + 0.005917 + 0.006 + 0.005625 + 0.006 + 0.0065 + 0.006042 + 0.005875 + 0.005375 + 0.005917 + 0.007458 + 0.005083 + 0.005708 + 0.005083 + 0.007 + 0.006167 + 0.006166 + 0.006042 + 0.006458 + 0.006125 + 0.006041 + 0.006333 + 0.0065 + 0.006333 + 0.006042 + 0.006125 + 0.006458 + 0.006333 + 0.006292 + 0.006083 + 0.00625 + 0.006042 + 0.006584 + 0.0065 + 0.006375 + 0.006458 + 0.006625 + 0.006291 + 0.006417 + 0.006291 + 0.0065 + 0.006 + 0.006416 + 0.006333 + 0.007 + 0.0075 + 0.007792 + 0.007916 + 0.008208 + 0.006792 + 0.007667 + 0.007958 + 0.008667 + 0.010542 + 0.01 + 0.009292 + 0.009583 + 0.009916 + 0.00975 + 0.008416 + 0.008083 + 0.008458 + 0.008209 + 0.009375 + 0.010167 + 0.010833 + 0.008625 + 0.007375 + 0.007916 + 0.008 + 0.007584 + 0.007333 + 0.00725 + 0.007 + 0.007375 + 0.008166 + 0.008792 + 0.008667 + 0.009542 + 0.00975 + 0.010375 + 0.008792 + 0.00925 + 0.008167 + 0.006875 + 0.01075 + 0.010542 + 0.009875 + 0.008625 + 0.008958 + 0.008584 + 0.011458 + 0.011 + 0.010208 + 0.009167 + 0.008959 + 0.00925 + 0.010667 + 0.011917 + 0.010708 + 0.010208 + 0.007333 + 0.007583 + 0.006708 + 0.006833 + 0.006541 + 0.006375 + 0.006625 + 0.007209 + 0.008 + 0.008333 + 0.006459 + 0.006125 + 0.005084 + 0.005209 + 0.0085 + 0.009125 + 0.008542 + 0.00825 + 0.008083 + 0.007125 + 0.006333 + 0.00825 + 0.007667 + 0.007583 + 0.008292 + 0.00875 + 0.0085 + 0.012459 + 0.01175 + 0.010958 + 0.01025 + 0.009333 + 0.018417 + 0.010708 + 0.0095 + 0.007333 + 0.007167 + 0.007541 + 0.00725 + 0.007584 + 0.008 + 0.007667 + 0.007917 + 0.007875 + 0.007833 + 0.00825 + 0.008 + 0.006208 + 0.0065 + 0.006625 + 0.007166 + 0.006833 + 0.007125 + 0.007084 + 0.006917 + 0.008875 + 0.00825 + 0.007667 + 0.00725 + 0.007 + 0.006625 + 0.007375 + 0.007667 + 0.00725 + 0.006833 + 0.006333 + 0.00675 + 0.007375 + 0.007041 + 0.008792 + 0.00825 + 0.008375 + 0.00775 + 0.007333 + 0.006833 + 0.007083 + 0.007083 + 0.006875 + 0.007042 + 0.006917 + 0.006667 + 0.007542 + 0.006875 + 0.007084 + 0.009 + 0.010708 + 0.010458 + 0.010291 + 0.009 + 0.008334 + 0.008417 + 0.0095 + 0.008125 + 0.007792 + 0.00775 + 0.0075 + 0.007291 + 0.007417 + 0.009709 + 0.00925 + 0.006042 + 0.00675 + 0.006 + 0.006083 + 0.005958 + 0.006083 + 0.006375 + 0.005917 + 0.006125 + 0.006458 + 0.007334 + 0.006084 + 0.004916 + 0.005708 + 0.006959 + 0.006459 + 0.006458 + 0.006708 + 0.006666 + 0.006709 + 0.006958 + 0.006167 + 0.00625 + 0.006792 + 0.006583 + 0.006916 + 0.006792 + 0.006416 + 0.0065 + 0.00675 + 0.006166 + 0.006625 + 0.007083 + 0.006541 + 0.006834 + 0.006667 + 0.006458 + 0.006708 + 0.006709 + 0.00675 + 0.0065 + 0.007125 + 0.006625 + 0.00675 + 0.006417 + 0.006459 + 0.006709 + 0.006458 + 0.006916 + 0.006792 + 0.007791 + 0.009 + 0.008875 + 0.009 + 0.008917 + 0.008083 + 0.007291 + 0.007542 + 0.0075 + 0.007625 + 0.007041 + 0.006625 + 0.006834 + 0.006833 + 0.007083 + 0.007458 + 0.00725 + 0.007375 + 0.010292 + 0.0075 + 0.007209 + 0.007 + 0.0095 + 0.009791 + 0.009458 + 0.009292 + 0.009375 + 0.00775 + 0.006458 + 0.006833 + 0.006917 + 0.007125 + 0.006917 + 0.007375 + 0.00825 + 0.007458 + 0.010042 + 0.010334 + 0.01025 + 0.010041 + 0.009875 + 0.008 + 0.007375 + 0.009916 + 0.010125 + 0.009708 + 0.010084 + 0.009625 + 0.007792 + 0.01075 + 0.007834 + 0.007209 + 0.007209 + 0.0075 + 0.007875 + 0.006333 + 0.006083 + 0.005875 + 0.005958 + 0.005792 + 0.0075 + 0.005208 + 0.004917 + 0.004791 + 0.006917 + 0.006417 + 0.006833 + 0.006458 + 0.006583 + 0.006625 + 0.0065 + 0.00625 + 0.006667 + 0.006833 + 0.006542 + 0.00625 + 0.006542 + 0.006583 + 0.006959 + 0.006542 + 0.0065 + 0.006375 + 0.0065 + 0.006667 + 0.007125 + 0.006625 + 0.006375 + 0.006209 + 0.006666 + 0.006792 + 0.006166 + 0.006875 + 0.006583 + 0.006583 + 0.006542 + 0.006709 + 0.006709 + 0.007041 + 0.0065 + 0.006666 + 0.006667 + 0.006833 + 0.006958 + 0.006583 + 0.006917 + 0.007208 + 0.007375 + 0.007083 + 0.006958 + 0.009417 + 0.008584 + 0.009209 + 0.018667 + 0.011541 + 0.009 + 0.009334 + 0.008917 + 0.00675 + 0.006792 + 0.007208 + 0.006667 + 0.006791 + 0.006542 + 0.007166 + 0.007208 + 0.006292 + 0.009 + 0.008542 + 0.009041 + 0.0095 + 0.009166 + 0.009125 + 0.009041 + 0.007667 + 0.006792 + 0.008458 + 0.008166 + 0.00875 + 0.011083 + 0.010375 + 0.010042 + 0.00975 + 0.008917 + 0.00975 + 0.008334 + 0.00875 + 0.009083 + 0.009125 + 0.00825 + 0.0075 + 0.0075 + 0.005708 + 0.005833 + 0.00525 + 0.005875 + 0.006042 + 0.005709 + 0.005917 + 0.005917 + 0.006209 + 0.007667 + 0.004625 + 0.005 + 0.004666 + 0.007042 + 0.006209 + 0.006834 + 0.006417 + 0.006375 + 0.0065 + 0.006333 + 0.006 + 0.006083 + 0.00625 + 0.006084 + 0.006208 + 0.006416 + 0.006042 + 0.006041 + 0.00625 + 0.006625 + 0.00625 + 0.006416 + 0.006709 + 0.006375 + 0.006625 + 0.006459 + 0.006459 + 0.006334 + 0.006417 + 0.006333 + 0.0065 + 0.00625 + 0.007666 + 0.006333 + 0.0065 + 0.006875 + 0.0065 + 0.007125 + 0.007875 + 0.006583 + 0.007333 + 0.006708 + 0.007083 + 0.006792 + 0.006959 + 0.008292 + 0.008708 + 0.010791 + 0.009417 + 0.007834 + 0.007625 + 0.007083 + 0.006667 + 0.007083 + 0.006708 + 0.007 + 0.006208 + 0.006708 + 0.006875 + 0.006542 + 0.006958 + 0.006667 + 0.006916 + 0.006542 + 0.007333 + 0.0085 + 0.00825 + 0.008084 + 0.009667 + 0.008708 + 0.008416 + 0.007542 + 0.007041 + 0.006917 + 0.007333 + 0.007541 + 0.007292 + 0.0075 + 0.00975 + 0.009125 + 0.009 + 0.007625 + 0.007958 + 0.007458 + 0.007625 + 0.008834 + 0.009375 + 0.00925 + 0.007833 + 0.007291 + 0.005417 + 0.006 + 0.005791 + 0.005833 + 0.0055 + 0.005583 + 0.005583 + 0.005833 + 0.00575 + 0.006041 + 0.005083 + 0.006125 + 0.005792 + 0.0055 + 0.006458 + 0.0065 + 0.00725 + 0.007666 + 0.007333 + 0.008708 + 0.008583 + 0.008542 + 0.008041 + 0.007708 + 0.007125 + 0.006 + 0.0065 + 0.006084 + 0.005959 + 0.005959 + 0.006375 + 0.006625 + 0.006167 + 0.00625 + 0.006709 + 0.006292 + 0.006666 + 0.006334 + 0.006542 + 0.006042 + 0.0065 + 0.006625 + 0.006167 + 0.006292 + 0.006833 + 0.007791 + 0.007958 + 0.009417 + 0.008125 + 0.008542 + 0.008792 + 0.009291 + 0.00725 + 0.006708 + 0.006333 + 0.006625 + 0.007 + 0.006834 + 0.007083 + 0.0065 + 0.009084 + 0.009125 + 0.00875 + 0.008583 + 0.008833 + 0.008125 + 0.007084 + 0.006917 + 0.006875 + 0.006791 + 0.006917 + 0.006875 + 0.007 + 0.006792 + 0.007667 + 0.007 + 0.007333 + 0.00675 + 0.006833 + 0.006833 + 0.009334 + 0.009167 + 0.008042 + 0.008958 + 0.01025 + 0.008375 + 0.007833 + 0.007459 + 0.009333 + 0.009541 + 0.009709 + 0.0105 + 0.009417 + 0.00825 + 0.008 + 0.009708 + 0.009916 + 0.009667 + 0.009583 + 0.009916 + 0.006875 + 0.006083 + 0.005541 + 0.005958 + 0.005625 + 0.005458 + 0.005542 + 0.005666 + 0.005917 + 0.006583 + 0.007375 + 0.005 + 0.004917 + 0.004917 + 0.006666 + 0.00625 + 0.006333 + 0.006458 + 0.006208 + 0.006583 + 0.006458 + 0.006792 + 0.00675 + 0.00625 + 0.006458 + 0.006333 + 0.006292 + 0.006291 + 0.006125 + 0.006167 + 0.006125 + 0.006334 + 0.006875 + 0.006875 + 0.006666 + 0.006416 + 0.006417 + 0.00625 + 0.006417 + 0.006375 + 0.006458 + 0.006625 + 0.006584 + 0.006375 + 0.007042 + 0.006542 + 0.006416 + 0.008209 + 0.008584 + 0.00875 + 0.009209 + 0.008875 + 0.008583 + 0.006917 + 0.006541 + 0.006708 + 0.007084 + 0.007 + 0.007 + 0.007 + 0.00675 + 0.009375 + 0.009542 + 0.009291 + 0.008917 + 0.009334 + 0.007709 + 0.007209 + 0.00675 + 0.007208 + 0.008209 + 0.008208 + 0.008375 + 0.007292 + 0.007125 + 0.007 + 0.006709 + 0.00725 + 0.006459 + 0.007458 + 0.007333 + 0.009583 + 0.009833 + 0.009333 + 0.010667 + 0.01 + 0.008125 + 0.007542 + 0.007958 + 0.012125 + 0.010416 + 0.009792 + 0.00975 + 0.01 + 0.007917 + 0.008042 + 0.011083 + 0.010792 + 0.021125 + 0.023834 + 0.007375 + 0.008209 + 0.007375 + 0.006334 + 0.006417 + 0.006792 + 0.007375 + 0.00725 + 0.006459 + 0.006583 + 0.010166 + 0.006 + 0.005875 + 0.005708 + 0.007666 + 0.00725 + 0.007125 + 0.007584 + 0.006791 + 0.006667 + 0.006792 + 0.008708 + 0.008666 + 0.00725 + 0.007583 + 0.006875 + 0.006916 + 0.006458 + 0.007458 + 0.007 + 0.007375 + 0.006625 + 0.006792 + 0.006625 + 0.007125 + 0.006958 + 0.006958 + 0.006958 + 0.007041 + 0.007625 + 0.007333 + 0.006917 + 0.00675 + 0.006875 + 0.006959 + 0.007333 + 0.007166 + 0.006833 + 0.006833 + 0.006917 + 0.006584 + 0.007 + 0.006875 + 0.0065 + 0.011875 + 0.007166 + 0.007459 + 0.0075 + 0.007083 + 0.006917 + 0.00725 + 0.007042 + 0.007125 + 0.007292 + 0.0095 + 0.009166 + 0.007958 + 0.007584 + 0.007541 + 0.007417 + 0.007 + 0.006708 + 0.008208 + 0.008791 + 0.007167 + 0.0065 + 0.007417 + 0.006958 + 0.007166 + 0.00725 + 0.007458 + 0.007125 + 0.007291 + 0.007334 + 0.007958 + 0.011 + 0.011583 + 0.010666 + 0.010542 + 0.009083 + 0.008791 + 0.007833 + 0.007375 + 0.009708 + 0.009416 + 0.009333 + 0.008166 + 0.007417 + 0.00775 + 0.00925 + 0.009167 + 0.007584 + 0.007292 + 0.005916 + 0.005792 + 0.005709 + 0.006042 + 0.005791 + 0.00625 + 0.006416 + 0.007625 + 0.005208 + 0.00575 + 0.005041 + 0.00675 + 0.006916 + 0.0065 + 0.007208 + 0.006167 + 0.006666 + 0.006708 + 0.006708 + 0.006667 + 0.006458 + 0.0065 + 0.006666 + 0.007084 + 0.006833 + 0.006625 + 0.00625 + 0.006 + 0.006375 + 0.006667 + 0.006667 + 0.006209 + 0.006209 + 0.006417 + 0.006583 + 0.006041 + 0.006708 + 0.006708 + 0.006375 + 0.006292 + 0.00625 + 0.006584 + 0.00625 + 0.006584 + 0.006834 + 0.006875 + 0.006334 + 0.0065 + 0.006959 + 0.006625 + 0.007375 + 0.007083 + 0.006333 + 0.006958 + 0.007042 + 0.007416 + 0.007667 + 0.006667 + 0.007042 + 0.006709 + 0.006667 + 0.00675 + 0.006833 + 0.007125 + 0.006916 + 0.007 + 0.007166 + 0.006875 + 0.007042 + 0.00675 + 0.009291 + 0.008458 + 0.008417 + 0.008209 + 0.006917 + 0.007208 + 0.006834 + 0.007125 + 0.006833 + 0.006833 + 0.006708 + 0.007167 + 0.007791 + 0.007584 + 0.009791 + 0.009667 + 0.009792 + 0.008416 + 0.00775 + 0.007916 + 0.007458 + 0.009959 + 0.009625 + 0.009333 + 0.008583 + 0.007875 + 0.007917 + 0.010292 + 0.007583 + 0.00725 + 0.007375 + 0.006833 + 0.006666 + 0.006125 + 0.006083 + 0.006125 + 0.00575 + 0.008 + 0.007583 + 0.007875 + 0.005458 + 0.006041 + 0.007125 + 0.006667 + 0.007375 + 0.006708 + 0.007458 + 0.00675 + 0.007125 + 0.006584 + 0.006542 + 0.006792 + 0.00675 + 0.006709 + 0.006666 + 0.007375 + 0.00725 + 0.006541 + 0.006709 + 0.006625 + 0.006625 + 0.006542 + 0.006333 + 0.00625 + 0.006875 + 0.006667 + 0.006625 + 0.006709 + 0.00775 + 0.008042 + 0.008667 + 0.008667 + 0.008584 + 0.008708 + 0.008375 + 0.00725 + 0.006667 + 0.006708 + 0.007292 + 0.006584 + 0.007459 + 0.007 + 0.006667 + 0.006791 + 0.007 + 0.007125 + 0.006875 + 0.006875 + 0.006584 + 0.006625 + 0.006542 + 0.006709 + 0.006834 + 0.007042 + 0.006875 + 0.00675 + 0.007125 + 0.006708 + 0.006458 + 0.007042 + 0.00675 + 0.00625 + 0.007125 + 0.007292 + 0.009208 + 0.008458 + 0.008208 + 0.008292 + 0.006584 + 0.006625 + 0.006875 + 0.006917 + 0.007625 + 0.007417 + 0.007625 + 0.009 + 0.009375 + 0.00875 + 0.008333 + 0.007375 + 0.006875 + 0.007291 + 0.009125 + 0.009416 + 0.008792 + 0.008416 + 0.007792 + 0.007334 + 0.005792 + 0.006 + 0.005666 + 0.006 + 0.005834 + 0.006084 + 0.005958 + 0.005958 + 0.005541 + 0.006083 + 0.007375 + 0.004875 + 0.005167 + 0.005166 + 0.006875 + 0.006417 + 0.0065 + 0.006 + 0.006583 + 0.006458 + 0.006583 + 0.006667 + 0.006334 + 0.006291 + 0.006708 + 0.006459 + 0.00625 + 0.006584 + 0.006459 + 0.006417 + 0.006166 + 0.006042 + 0.006375 + 0.006167 + 0.006459 + 0.006 + 0.005917 + 0.006125 + 0.006167 + 0.006916 + 0.007042 + 0.00775 + 0.007083 + 0.006541 + 0.006167 + 0.006375 + 0.006375 + 0.006292 + 0.006167 + 0.006375 + 0.006709 + 0.006792 + 0.006458 + 0.006375 + 0.006833 + 0.006041 + 0.006458 + 0.007167 + 0.008791 + 0.008542 + 0.00825 + 0.00775 + 0.006541 + 0.006458 + 0.007542 + 0.006917 + 0.006791 + 0.006875 + 0.006833 + 0.006459 + 0.0065 + 0.007083 + 0.006542 + 0.006958 + 0.006958 + 0.00675 + 0.006458 + 0.007 + 0.008 + 0.00825 + 0.008458 + 0.007916 + 0.006833 + 0.006792 + 0.0075 + 0.0075 + 0.00725 + 0.007458 + 0.009417 + 0.009167 + 0.00875 + 0.008375 + 0.007208 + 0.007292 + 0.007667 + 0.008542 + 0.009041 + 0.0085 + 0.008167 + 0.007708 + 0.006292 + 0.005625 + 0.005459 + 0.005917 + 0.005583 + 0.005625 + 0.005834 + 0.00575 + 0.005459 + 0.005542 + 0.007291 + 0.004667 + 0.004625 + 0.004917 + 0.006333 + 0.006583 + 0.006375 + 0.006041 + 0.006542 + 0.006041 + 0.006459 + 0.006292 + 0.006167 + 0.006166 + 0.006375 + 0.006042 + 0.006333 + 0.006292 + 0.006208 + 0.006291 + 0.006042 + 0.006167 + 0.006666 + 0.006333 + 0.006292 + 0.006709 + 0.006083 + 0.006083 + 0.006125 + 0.005833 + 0.006625 + 0.006209 + 0.006334 + 0.006292 + 0.0065 + 0.006375 + 0.00625 + 0.006792 + 0.006417 + 0.006333 + 0.006458 + 0.006792 + 0.006833 + 0.006375 + 0.006292 + 0.006334 + 0.0065 + 0.007 + 0.007083 + 0.006708 + 0.006875 + 0.008459 + 0.008459 + 0.007959 + 0.008166 + 0.007583 + 0.007834 + 0.007375 + 0.006792 + 0.006625 + 0.006583 + 0.006625 + 0.008625 + 0.009292 + 0.009583 + 0.008375 + 0.00825 + 0.008583 + 0.007959 + 0.008584 + 0.008666 + 0.008375 + 0.009041 + 0.00875 + 0.008417 + 0.009708 + 0.008958 + 0.009416 + 0.010375 + 0.011333 + 0.009916 + 0.009125 + 0.008333 + 0.01675 + 0.008833 + 0.00875 + 0.016208 + 0.011541 + 0.00875 + 0.008375 + 0.008 + 0.007583 + 0.006584 + 0.00675 + 0.006583 + 0.006792 + 0.006791 + 0.006625 + 0.006792 + 0.006833 + 0.007916 + 0.006833 + 0.00775 + 0.005833 + 0.005833 + 0.008208 + 0.007 + 0.007208 + 0.007041 + 0.007208 + 0.007 + 0.00725 + 0.007125 + 0.006958 + 0.007083 + 0.007125 + 0.00725 + 0.051958 + 0.009291 + 0.007834 + 0.007583 + 0.007584 + 0.007584 + 0.008 + 0.0075 + 0.007709 + 0.007458 + 0.007375 + 0.00775 + 0.007666 + 0.007417 + 0.007417 + 0.007416 + 0.00725 + 0.0075 + 0.007291 + 0.0075 + 0.007542 + 0.007209 + 0.0075 + 0.007666 + 0.009 + 0.00775 + 0.008 + 0.007916 + 0.007625 + 0.007791 + 0.008292 + 0.011583 + 0.008334 + 0.007916 + 0.008292 + 0.008083 + 0.008041 + 0.008042 + 0.013916 + 0.012167 + 0.010292 + 0.01225 + 0.009209 + 0.008375 + 0.007792 + 0.007792 + 0.008 + 0.008458 + 0.009541 + 0.010042 + 0.007833 + 0.007708 + 0.008416 + 0.008208 + 0.008333 + 0.010375 + 0.013041 + 0.010125 + 0.017416 + 0.010792 + 0.009709 + 0.010167 + 0.009208 + 0.008583 + 0.012125 + 0.01375 + 0.02975 + 0.010291 + 0.009458 + 0.008833 + 0.009333 + 0.010292 + 0.011208 + 0.011875 + 0.0105 + 0.0085 + 0.009 + 0.007667 + 0.007708 + 0.008292 + 0.00725 + 0.009959 + 0.009292 + 0.012584 + 0.009416 + 0.011833 + 0.007833 + 0.006833 + 0.017417 + 0.010583 + 0.023417 + 0.011083 + 0.010917 + 0.009292 + 0.0085 + 0.007875 + 0.007875 + 0.008125 + 0.00825 + 0.00825 + 0.008625 + 0.007833 + 0.011083 + 0.007417 + 0.008208 + 0.007 + 0.007708 + 0.008166 + 0.007167 + 0.01825 + 0.016 + 0.008833 + 0.007167 + 0.007584 + 0.006917 + 0.008625 + 0.008042 + 0.008042 + 0.007583 + 0.006625 + 0.00725 + 0.009875 + 0.007459 + 0.007667 + 0.007958 + 0.010667 + 0.008 + 0.007542 + 0.007166 + 0.011667 + 0.00725 + 0.008792 + 0.007917 + 0.011125 + 0.00825 + 0.007292 + 0.007916 + 0.007208 + 0.011292 + 0.009417 + 0.007416 + 0.007792 + 0.007875 + 0.010584 + 0.008833 + 0.008417 + 0.007959 + 0.007875 + 0.008125 + 0.007875 + 0.007292 + 0.007042 + 0.007291 + 0.00725 + 0.018333 + 0.008042 + 0.008667 + 0.008833 + 0.008958 + 0.010583 + 0.01075 + 0.01125 + 0.01025 + 0.010708 + 0.010666 + 0.0115 + 0.008541 + 0.009167 + 0.008875 + 0.008292 + 0.011625 + 0.009125 + 0.011208 + 0.011083 + 0.008583 + 0.008333 + 0.007792 + 0.007 + 0.006959 + 0.007 + 0.007417 + 0.0085 + 0.00825 + 0.008125 + 0.008125 + 0.006125 + 0.006708 + 0.0065 + 0.00925 + 0.00875 + 0.0085 + 0.007166 + 0.007167 + 0.007291 + 0.007375 + 0.007292 + 0.008292 + 0.009208 + 0.009375 + 0.008958 + 0.00875 + 0.00875 + 0.011 + 0.016333 + 0.01425 + 0.009 + 0.008791 + 0.007583 + 0.007708 + 0.006917 + 0.007875 + 0.007958 + 0.008708 + 0.008334 + 0.010292 + 0.007542 + 0.0065 + 0.006833 + 0.006584 + 0.007459 + 0.007291 + 0.007042 + 0.007084 + 0.006625 + 0.007417 + 0.007625 + 0.006959 + 0.007417 + 0.007083 + 0.007458 + 0.009958 + 0.007375 + 0.007541 + 0.007042 + 0.007542 + 0.006958 + 0.007291 + 0.007792 + 0.007334 + 0.007042 + 0.007417 + 0.00775 + 0.007459 + 0.007334 + 0.007458 + 0.009791 + 0.013625 + 0.009333 + 0.00925 + 0.007917 + 0.007125 + 0.007875 + 0.008084 + 0.007583 + 0.007791 + 0.007208 + 0.007458 + 0.007667 + 0.008125 + 0.008792 + 0.01075 + 0.010041 + 0.009917 + 0.009292 + 0.008875 + 0.008208 + 0.007958 + 0.01025 + 0.010542 + 0.009791 + 0.00925 + 0.008333 + 0.00825 + 0.010958 + 0.009208 + 0.008583 + 0.008 + 0.006708 + 0.006084 + 0.006041 + 0.006792 + 0.006334 + 0.006209 + 0.006667 + 0.008958 + 0.005458 + 0.007542 + 0.006416 + 0.008167 + 0.008959 + 0.009167 + 0.00775 + 0.009334 + 0.008709 + 0.0085 + 0.006792 + 0.006667 + 0.006458 + 0.006792 + 0.006583 + 0.007083 + 0.006708 + 0.008084 + 0.007875 + 0.007334 + 0.008667 + 0.00925 + 0.008417 + 0.008875 + 0.009 + 0.007791 + 0.008459 + 0.00925 + 0.009042 + 0.007459 + 0.006959 + 0.007459 + 0.008 + 0.008666 + 0.008791 + 0.00925 + 0.008625 + 0.008625 + 0.007667 + 0.008416 + 0.008334 + 0.007916 + 0.00825 + 0.007083 + 0.007292 + 0.006958 + 0.006583 + 0.007417 + 0.007375 + 0.007583 + 0.007084 + 0.007625 + 0.006916 + 0.007417 + 0.009083 + 0.008125 + 0.006625 + 0.0075 + 0.007625 + 0.007167 + 0.0075 + 0.007125 + 0.009208 + 0.008583 + 0.009042 + 0.008709 + 0.008125 + 0.007458 + 0.007583 + 0.006959 + 0.007208 + 0.007167 + 0.007084 + 0.007375 + 0.007667 + 0.008291 + 0.009709 + 0.009459 + 0.009584 + 0.008542 + 0.007916 + 0.007292 + 0.008083 + 0.009208 + 0.009917 + 0.0095 + 0.008292 + 0.008042 + 0.009333 + 0.010125 + 0.006416 + 0.0065 + 0.006167 + 0.005791 + 0.006125 + 0.006166 + 0.006 + 0.005917 + 0.005791 + 0.006875 + 0.008042 + 0.006833 + 0.005833 + 0.005042 + 0.007084 + 0.006958 + 0.006541 + 0.00675 + 0.006667 + 0.007042 + 0.006125 + 0.006916 + 0.006375 + 0.007 + 0.006375 + 0.006584 + 0.006667 + 0.006333 + 0.006542 + 0.0065 + 0.006834 + 0.006542 + 0.006542 + 0.006584 + 0.006875 + 0.0065 + 0.00675 + 0.006375 + 0.006625 + 0.006875 + 0.006333 + 0.006416 + 0.006334 + 0.0065 + 0.006583 + 0.006375 + 0.006625 + 0.006292 + 0.006459 + 0.006334 + 0.007208 + 0.006708 + 0.015833 + 0.006875 + 0.006959 + 0.006834 + 0.006875 + 0.007 + 0.007541 + 0.007209 + 0.006958 + 0.008083 + 0.008166 + 0.008667 + 0.008834 + 0.008833 + 0.009042 + 0.0095 + 0.008667 + 0.008416 + 0.009167 + 0.009 + 0.009042 + 0.012417 + 0.009416 + 0.009 + 0.01075 + 0.010417 + 0.007541 + 0.007959 + 0.009125 + 0.009 + 0.008625 + 0.00825 + 0.009375 + 0.009667 + 0.009125 + 0.010459 + 0.00975 + 0.010375 + 0.009625 + 0.008834 + 0.008291 + 0.008292 + 0.007375 + 0.012792 + 0.009458 + 0.009125 + 0.008459 + 0.007375 + 0.006208 + 0.005833 + 0.005667 + 0.006083 + 0.005541 + 0.00575 + 0.005917 + 0.006 + 0.005708 + 0.005833 + 0.007459 + 0.006375 + 0.005084 + 0.0045 + 0.006375 + 0.006125 + 0.006208 + 0.006083 + 0.010625 + 0.008375 + 0.00875 + 0.009917 + 0.0095 + 0.008792 + 0.007667 + 0.007917 + 0.008042 + 0.007708 + 0.007416 + 0.007042 + 0.008 + 0.007667 + 0.009791 + 0.009667 + 0.009834 + 0.007917 + 0.007583 + 0.007375 + 0.007041 + 0.007292 + 0.007542 + 0.007167 + 0.007292 + 0.006875 + 0.006708 + 0.006125 + 0.006084 + 0.006209 + 0.006959 + 0.00625 + 0.006792 + 0.006708 + 0.006583 + 0.007 + 0.007 + 0.006875 + 0.006833 + 0.008208 + 0.010833 + 0.009667 + 0.00925 + 0.009042 + 0.008584 + 0.007792 + 0.0075 + 0.007459 + 0.006625 + 0.007417 + 0.006792 + 0.006875 + 0.006916 + 0.008959 + 0.009833 + 0.009125 + 0.008167 + 0.008 + 0.007625 + 0.011958 + 0.008208 + 0.007208 + 0.006583 + 0.009292 + 0.0095 + 0.0095 + 0.010833 + 0.010542 + 0.010833 + 0.011083 + 0.010541 + 0.013208 + 0.010458 + 0.010833 + 0.01 + 0.05225 + 0.023375 + 0.01075 + 0.01125 + 0.01025 + 0.009709 + 0.009375 + 0.007333 + 0.007208 + 0.006417 + 0.007125 + 0.0075 + 0.008375 + 0.006958 + 0.006833 + 0.006583 + 0.006958 + 0.008375 + 0.006625 + 0.005917 + 0.006083 + 0.031 + 0.013542 + 0.00875 + 0.007416 + 0.007334 + 0.007 + 0.013833 + 0.012291 + 0.00925 + 0.00975 + 0.008833 + 0.008583 + 0.008875 + 0.009209 + 0.007917 + 0.007041 + 0.007125 + 0.007583 + 0.007375 + 0.008083 + 0.008083 + 0.007416 + 0.0075 + 0.008291 + 0.008042 + 0.008292 + 0.008375 + 0.008167 + 0.009541 + 0.008292 + 0.007959 + 0.007709 + 0.007291 + 0.007375 + 0.009125 + 0.008708 + 0.008833 + 0.008084 + 0.007959 + 0.007708 + 0.0075 + 0.007166 + 0.007083 + 0.008333 + 0.007208 + 0.006917 + 0.009 + 0.01 + 0.00925 + 0.009875 + 0.013167 + 0.010667 + 0.009125 + 0.010084 + 0.009292 + 0.007958 + 0.0075 + 0.00825 + 0.009042 + 0.01 + 0.011209 + 0.010916 + 0.008583 + 0.008333 + 0.007417 + 0.007417 + 0.007583 + 0.012666 + 0.007708 + 0.009875 + 0.010291 + 0.014208 + 0.012083 + 0.011375 + 0.017375 + 0.011291 + 0.011334 + 0.013166 + 0.011375 + 0.012166 + 0.011667 + 0.009667 + 0.010208 + 0.021875 + 0.019417 + 0.01475 + 0.013417 + 0.009 + 0.007958 + 0.010458 + 0.007792 + 0.007 + 0.007917 + 0.008125 + 0.0075 + 0.008542 + 0.010041 + 0.015792 + 0.011709 + 0.009 + 0.010958 + 0.009791 + 0.008542 + 0.008666 + 0.007958 + 0.007542 + 0.008292 + 0.007708 + 0.008042 + 0.007625 + 0.007834 + 0.008584 + 0.009125 + 0.009292 + 0.009292 + 0.007834 + 0.007 + 0.00725 + 0.006916 + 0.007333 + 0.007125 + 0.007125 + 0.007083 + 0.008291 + 0.008167 + 0.007 + 0.007292 + 0.007958 + 0.008208 + 0.00725 + 0.007083 + 0.006917 + 0.006958 + 0.007166 + 0.007 + 0.006875 + 0.007291 + 0.007625 + 0.007458 + 0.007042 + 0.006959 + 0.007417 + 0.006875 + 0.006708 + 0.007458 + 0.00775 + 0.007041 + 0.007458 + 0.007083 + 0.007292 + 0.007333 + 0.006917 + 0.007541 + 0.006833 + 0.007334 + 0.007334 + 0.010375 + 0.009125 + 0.008833 + 0.0085 + 0.006792 + 0.007292 + 0.007042 + 0.006666 + 0.007125 + 0.007292 + 0.008 + 0.008583 + 0.008333 + 0.007291 + 0.007375 + 0.015708 + 0.011291 + 0.011 + 0.010916 + 0.011333 + 0.010417 + 0.007958 + 0.007333 + 0.010333 + 0.010708 + 0.010708 + 0.010542 + 0.010125 + 0.008125 + 0.010584 + 0.010208 + 0.008167 + 0.008 + 0.008084 + 0.008459 + 0.007375 + 0.008292 + 0.008708 + 0.009042 + 0.008041 + 0.009292 + 0.009791 + 0.007875 + 0.005708 + 0.006416 + 0.008583 + 0.008875 + 0.009666 + 0.00875 + 0.008458 + 0.007666 + 0.00775 + 0.007541 + 0.00925 + 0.009833 + 0.0195 + 0.010959 + 0.008917 + 0.009458 + 0.008333 + 0.007791 + 0.008125 + 0.008916 + 0.008708 + 0.008958 + 0.009125 + 0.007916 + 0.007833 + 0.007833 + 0.007625 + 0.007542 + 0.007125 + 0.006834 + 0.007375 + 0.006625 + 0.006667 + 0.00775 + 0.007542 + 0.006584 + 0.007041 + 0.006834 + 0.007291 + 0.007458 + 0.007375 + 0.007333 + 0.007584 + 0.00775 + 0.007333 + 0.008291 + 0.007417 + 0.007125 + 0.007958 + 0.007666 + 0.007917 + 0.007209 + 0.007709 + 0.00725 + 0.00775 + 0.007583 + 0.007625 + 0.007709 + 0.00725 + 0.007375 + 0.01325 + 0.010042 + 0.009625 + 0.009375 + 0.009875 + 0.007791 + 0.00775 + 0.007208 + 0.008083 + 0.007166 + 0.007458 + 0.007 + 0.009125 + 0.008417 + 0.009875 + 0.010208 + 0.009417 + 0.008959 + 0.007458 + 0.0075 + 0.00725 + 0.01075 + 0.009417 + 0.009459 + 0.00825 + 0.007792 + 0.007833 + 0.010667 + 0.008125 + 0.007708 + 0.007375 + 0.00625 + 0.005917 + 0.005833 + 0.006083 + 0.006167 + 0.006542 + 0.006291 + 0.008083 + 0.012084 + 0.004791 + 0.005209 + 0.007125 + 0.006958 + 0.006625 + 0.007166 + 0.006875 + 0.008458 + 0.009458 + 0.009083 + 0.023625 + 0.009083 + 0.008375 + 0.0085 + 0.007125 + 0.00775 + 0.008667 + 0.008333 + 0.008041 + 0.007042 + 0.008167 + 0.007375 + 0.008 + 0.008792 + 0.009042 + 0.008458 + 0.009125 + 0.009292 + 0.009208 + 0.008875 + 0.009084 + 0.008292 + 0.008458 + 0.008625 + 0.00875 + 0.008417 + 0.007834 + 0.007875 + 0.007875 + 0.007291 + 0.007292 + 0.007375 + 0.007875 + 0.007375 + 0.007 + 0.00775 + 0.008 + 0.007375 + 0.007125 + 0.007 + 0.007333 + 0.006875 + 0.007 + 0.007625 + 0.007583 + 0.00725 + 0.007375 + 0.007208 + 0.007 + 0.00675 + 0.007292 + 0.007291 + 0.007542 + 0.006875 + 0.010375 + 0.010083 + 0.009125 + 0.009042 + 0.009291 + 0.007708 + 0.007167 + 0.007333 + 0.008458 + 0.007958 + 0.007916 + 0.009875 + 0.01025 + 0.010542 + 0.010125 + 0.010291 + 0.007875 + 0.008292 + 0.010209 + 0.009959 + 0.01025 + 0.010125 + 0.009916 + 0.008 + 0.006083 + 0.006375 + 0.006083 + 0.006 + 0.006208 + 0.006333 + 0.006125 + 0.006458 + 0.00625 + 0.006 + 0.007125 + 0.004916 + 0.004708 + 0.005583 + 0.006958 + 0.0065 + 0.006625 + 0.006667 + 0.0065 + 0.006834 + 0.008625 + 0.0085 + 0.008875 + 0.008209 + 0.00825 + 0.007417 + 0.006459 + 0.006209 + 0.006459 + 0.006834 + 0.006791 + 0.006792 + 0.006459 + 0.006417 + 0.006833 + 0.007625 + 0.006834 + 0.011375 + 0.006917 + 0.006417 + 0.006417 + 0.007208 + 0.006583 + 0.006666 + 0.006958 + 0.006625 + 0.007084 + 0.006834 + 0.007042 + 0.006583 + 0.0065 + 0.00725 + 0.011583 + 0.010625 + 0.011125 + 0.009542 + 0.009958 + 0.00775 + 0.007791 + 0.007042 + 0.007042 + 0.007125 + 0.006667 + 0.006917 + 0.006959 + 0.007209 + 0.007041 + 0.006333 + 0.007084 + 0.007209 + 0.007 + 0.007167 + 0.006625 + 0.007208 + 0.006875 + 0.007875 + 0.010167 + 0.009459 + 0.007542 + 0.007709 + 0.007709 + 0.007833 + 0.007708 + 0.007708 + 0.00725 + 0.007958 + 0.007625 + 0.007375 + 0.009625 + 0.010125 + 0.009917 + 0.012875 + 0.008834 + 0.008125 + 0.008042 + 0.009958 + 0.0105 + 0.009667 + 0.010125 + 0.009917 + 0.007833 + 0.006458 + 0.006 + 0.005625 + 0.00575 + 0.005667 + 0.005667 + 0.005458 + 0.005833 + 0.006584 + 0.007375 + 0.004458 + 0.005125 + 0.004958 + 0.007083 + 0.00675 + 0.006667 + 0.0065 + 0.006917 + 0.006792 + 0.006625 + 0.006666 + 0.006917 + 0.006666 + 0.0065 + 0.006458 + 0.006417 + 0.005875 + 0.006334 + 0.006083 + 0.006125 + 0.006125 + 0.006209 + 0.00675 + 0.006792 + 0.007125 + 0.017209 + 0.007333 + 0.006417 + 0.006667 + 0.006708 + 0.006666 + 0.0065 + 0.006917 + 0.007125 + 0.006667 + 0.00675 + 0.006959 + 0.006416 + 0.0065 + 0.006458 + 0.007 + 0.015083 + 0.00725 + 0.007042 + 0.007333 + 0.007042 + 0.007 + 0.007458 + 0.011833 + 0.008833 + 0.009 + 0.008625 + 0.01 + 0.007792 + 0.007166 + 0.007125 + 0.006708 + 0.00725 + 0.013042 + 0.007 + 0.006792 + 0.007041 + 0.006917 + 0.007541 + 0.007125 + 0.007125 + 0.00725 + 0.009291 + 0.009375 + 0.009375 + 0.009375 + 0.009625 + 0.007916 + 0.00825 + 0.0115 + 0.011583 + 0.010583 + 0.010875 + 0.011209 + 0.010333 + 0.007792 + 0.007125 + 0.008167 + 0.007542 + 0.008333 + 0.010708 + 0.011083 + 0.010208 + 0.009917 + 0.009625 + 0.006291 + 0.006292 + 0.005875 + 0.005875 + 0.00575 + 0.006292 + 0.006333 + 0.0075 + 0.005583 + 0.013541 + 0.00775 + 0.006417 + 0.005375 + 0.005125 + 0.007542 + 0.012916 + 0.007167 + 0.006708 + 0.007292 + 0.0085 + 0.006958 + 0.014083 + 0.013 + 0.007667 + 0.008958 + 0.010042 + 0.01775 + 0.026125 + 0.009208 + 0.008791 + 0.007708 + 0.007375 + 0.007125 + 0.007167 + 0.00725 + 0.007458 + 0.007458 + 0.007583 + 0.007 + 0.007125 + 0.007 + 0.007417 + 0.007083 + 0.007541 + 0.007167 + 0.007333 + 0.015125 + 0.006792 + 0.008042 + 0.008667 + 0.00825 + 0.007125 + 0.007917 + 0.008167 + 0.008041 + 0.007042 + 0.007959 + 0.008417 + 0.007708 + 0.007375 + 0.007708 + 0.007167 + 0.007459 + 0.007375 + 0.011042 + 0.009375 + 0.008667 + 0.007833 + 0.007 + 0.007 + 0.006709 + 0.006875 + 0.006875 + 0.007375 + 0.006958 + 0.007208 + 0.007417 + 0.007166 + 0.006542 + 0.007167 + 0.008125 + 0.007083 + 0.006958 + 0.00775 + 0.011 + 0.010417 + 0.018334 + 0.008084 + 0.007333 + 0.00775 + 0.010042 + 0.009125 + 0.0085 + 0.00925 + 0.008208 + 0.007958 + 0.008541 + 0.011042 + 0.010625 + 0.01075 + 0.008667 + 0.008208 + 0.008917 + 0.008333 + 0.007875 + 0.007625 + 0.007625 + 0.007875 + 0.008041 + 0.007333 + 0.007459 + 0.004917 + 0.005416 + 0.005375 + 0.007042 + 0.006875 + 0.007042 + 0.007791 + 0.008458 + 0.008917 + 0.009417 + 0.009166 + 0.008208 + 0.007 + 0.00725 + 0.007083 + 0.008042 + 0.009 + 0.008375 + 0.007625 + 0.007125 + 0.007458 + 0.007333 + 0.007583 + 0.006458 + 0.007084 + 0.006834 + 0.006666 + 0.006542 + 0.00725 + 0.007084 + 0.007167 + 0.006458 + 0.008917 + 0.008792 + 0.008666 + 0.007333 + 0.007542 + 0.007125 + 0.007167 + 0.008375 + 0.019667 + 0.011 + 0.008833 + 0.009333 + 0.009208 + 0.007792 + 0.008125 + 0.007542 + 0.007125 + 0.00725 + 0.006959 + 0.007959 + 0.008875 + 0.007583 + 0.007292 + 0.007 + 0.007083 + 0.007416 + 0.012417 + 0.0085 + 0.00875 + 0.008208 + 0.007166 + 0.007292 + 0.006917 + 0.007 + 0.00725 + 0.007292 + 0.007166 + 0.007333 + 0.006833 + 0.007125 + 0.007208 + 0.008166 + 0.009542 + 0.009208 + 0.009375 + 0.008542 + 0.007458 + 0.0075 + 0.009625 + 0.009333 + 0.009792 + 0.008541 + 0.008292 + 0.00825 + 0.013167 + 0.010583 + 0.009833 + 0.007667 + 0.006916 + 0.006209 + 0.006167 + 0.006708 + 0.006959 + 0.007541 + 0.007875 + 0.007417 + 0.007459 + 0.008625 + 0.004708 + 0.005 + 0.007125 + 0.006792 + 0.006542 + 0.006833 + 0.007042 + 0.006875 + 0.007583 + 0.008042 + 0.009 + 0.008542 + 0.008583 + 0.009208 + 0.008833 + 0.009667 + 0.00775 + 0.007958 + 0.008833 + 0.015333 + 0.007666 + 0.007625 + 0.008333 + 0.008875 + 0.010125 + 0.025208 + 0.011459 + 0.008167 + 0.008083 + 0.006958 + 0.00725 + 0.008083 + 0.008458 + 0.007667 + 0.006833 + 0.006667 + 0.006833 + 0.006791 + 0.006917 + 0.006208 + 0.006625 + 0.019 + 0.007 + 0.006833 + 0.006666 + 0.006583 + 0.007542 + 0.007875 + 0.00725 + 0.007208 + 0.006583 + 0.006958 + 0.007333 + 0.007125 + 0.007375 + 0.006792 + 0.006333 + 0.007417 + 0.007042 + 0.007375 + 0.006917 + 0.007041 + 0.012667 + 0.009 + 0.007917 + 0.008125 + 0.007167 + 0.006875 + 0.007 + 0.006958 + 0.007208 + 0.00775 + 0.008292 + 0.007292 + 0.008916 + 0.008042 + 0.007833 + 0.010583 + 0.010875 + 0.010041 + 0.010417 + 0.0095 + 0.008625 + 0.007708 + 0.009375 + 0.009375 + 0.008708 + 0.008458 + 0.008042 + 0.007584 + 0.006041 + 0.006375 + 0.006125 + 0.006 + 0.006083 + 0.005542 + 0.005916 + 0.005875 + 0.006208 + 0.0075 + 0.005083 + 0.005958 + 0.005958 + 0.008416 + 0.009709 + 0.008291 + 0.00875 + 0.007167 + 0.006917 + 0.007208 + 0.008167 + 0.009084 + 0.009334 + 0.008 + 0.008042 + 0.008375 + 0.008 + 0.007291 + 0.008208 + 0.008125 + 0.007917 + 0.0085 + 0.009959 + 0.01 + 0.008708 + 0.008167 + 0.007459 + 0.007667 + 0.007583 + 0.007375 + 0.007333 + 0.007208 + 0.007042 + 0.007375 + 0.00675 + 0.006417 + 0.006916 + 0.006417 + 0.007541 + 0.008708 + 0.009667 + 0.009625 + 0.007833 + 0.007333 + 0.007417 + 0.007292 + 0.007125 + 0.008917 + 0.01125 + 0.009625 + 0.010792 + 0.017542 + 0.019959 + 0.008917 + 0.009459 + 0.009584 + 0.0085 + 0.007708 + 0.007375 + 0.010292 + 0.008541 + 0.009708 + 0.009208 + 0.009375 + 0.008208 + 0.009375 + 0.008833 + 0.009875 + 0.010292 + 0.010375 + 0.00925 + 0.008375 + 0.009208 + 0.008041 + 0.009667 + 0.0095 + 0.009334 + 0.011542 + 0.011916 + 0.010667 + 0.009166 + 0.009125 + 0.010459 + 0.011042 + 0.012375 + 0.011459 + 0.010667 + 0.01 + 0.010125 + 0.011125 + 0.008833 + 0.006458 + 0.007125 + 0.007708 + 0.00825 + 0.008959 + 0.006917 + 0.008625 + 0.007666 + 0.009083 + 0.009542 + 0.008958 + 0.007958 + 0.007416 + 0.065167 + 0.0475 + 0.008083 + 0.007375 + 0.007166 + 0.00725 + 0.006667 + 0.00675 + 0.006583 + 0.006459 + 0.006959 + 0.006791 + 0.006583 + 0.00675 + 0.006666 + 0.006708 + 0.006917 + 0.006875 + 0.021833 + 0.009667 + 0.022958 + 0.006792 + 0.006666 + 0.006667 + 0.007875 + 0.008292 + 0.008625 + 0.008875 + 0.010292 + 0.009834 + 0.008959 + 0.008791 + 0.008 + 0.006875 + 0.007 + 0.007125 + 0.008583 + 0.008916 + 0.009542 + 0.009625 + 0.009458 + 0.009125 + 0.008041 + 0.009292 + 0.010833 + 0.010958 + 0.010916 + 0.007916 + 0.0075 + 0.009 + 0.008958 + 0.008458 + 0.008042 + 0.009 + 0.009708 + 0.010125 + 0.030125 + 0.0155 + 0.009417 + 0.009166 + 0.012417 + 0.011209 + 0.010667 + 0.011 + 0.01325 + 0.0095 + 0.009166 + 0.0105 + 0.01075 + 0.009917 + 0.011958 + 0.009958 + 0.009667 + 0.010209 + 0.013459 + 0.009833 + 0.009375 + 0.0095 + 0.009291 + 0.00975 + 0.010542 + 0.010875 + 0.009209 + 0.009167 + 0.010833 + 0.01075 + 0.007792 + 0.007208 + 0.008708 + 0.009208 + 0.009125 + 0.0105 + 0.007834 + 0.00775 + 0.007584 + 0.008875 + 0.010584 + 0.007084 + 0.007584 + 0.006458 + 0.009291 + 0.009709 + 0.008625 + 0.008083 + 0.009417 + 0.00925 + 0.0095 + 0.008416 + 0.007708 + 0.0085 + 0.009625 + 0.009667 + 0.008416 + 0.008084 + 0.007833 + 0.007542 + 0.00925 + 0.009667 + 0.010166 + 0.007833 + 0.007958 + 0.007208 + 0.006417 + 0.008334 + 0.009083 + 0.016 + 0.009791 + 0.009667 + 0.008291 + 0.0095 + 0.01 + 0.01 + 0.008834 + 0.008541 + 0.009959 + 0.009208 + 0.011417 + 0.010209 + 0.011959 + 0.010625 + 0.009542 + 0.009625 + 0.0105 + 0.0105 + 0.008042 + 0.008084 + 0.008333 + 0.011625 + 0.010208 + 0.009542 + 0.009625 + 0.010917 + 0.009084 + 0.00875 + 0.008042 + 0.007709 + 0.007625 + 0.00775 + 0.007667 + 0.007375 + 0.007958 + 0.007625 + 0.007042 + 0.007875 + 0.007667 + 0.007834 + 0.00775 + 0.012417 + 0.009875 + 0.010375 + 0.01125 + 0.010333 + 0.008958 + 0.008666 + 0.008458 + 0.010125 + 0.010666 + 0.01025 + 0.010166 + 0.010667 + 0.0085 + 0.008167 + 0.010542 + 0.010541 + 0.010167 + 0.010083 + 0.00825 + 0.007292 + 0.006166 + 0.007167 + 0.008208 + 0.006208 + 0.006166 + 0.006875 + 0.007 + 0.008792 + 0.00875 + 0.007459 + 0.010875 + 0.008458 + 0.013042 + 0.009459 + 0.008667 + 0.01175 + 0.007292 + 0.006958 + 0.007166 + 0.007083 + 0.006625 + 0.00675 + 0.010708 + 0.007083 + 0.007 + 0.008083 + 0.008583 + 0.009292 + 0.008917 + 0.008917 + 0.008958 + 0.008583 + 0.009 + 0.007667 + 0.0075 + 0.007375 + 0.006667 + 0.007417 + 0.007791 + 0.007667 + 0.00725 + 0.007541 + 0.007 + 0.006708 + 0.006791 + 0.006792 + 0.006666 + 0.006834 + 0.006917 + 0.007375 + 0.007541 + 0.007417 + 0.007709 + 0.00725 + 0.007041 + 0.00725 + 0.007416 + 0.007833 + 0.007667 + 0.007084 + 0.007084 + 0.006792 + 0.009833 + 0.009083 + 0.008875 + 0.007916 + 0.00925 + 0.009125 + 0.009334 + 0.0135 + 0.008458 + 0.007708 + 0.006833 + 0.007084 + 0.007625 + 0.007125 + 0.007 + 0.007166 + 0.007042 + 0.007459 + 0.007208 + 0.0075 + 0.007167 + 0.01375 + 0.009792 + 0.009458 + 0.00875 + 0.008 + 0.008541 + 0.02325 + 0.009791 + 0.008333 + 0.008292 + 0.008167 + 0.008084 + 0.007833 + 0.011666 + 0.013 + 0.0095 + 0.007042 + 0.007708 + 0.00775 + 0.007125 + 0.00675 + 0.007417 + 0.006709 + 0.007333 + 0.007 + 0.00725 + 0.008375 + 0.006584 + 0.005042 + 0.005541 + 0.007833 + 0.007125 + 0.007458 + 0.008 + 0.008833 + 0.008833 + 0.009167 + 0.008959 + 0.008667 + 0.007916 + 0.007667 + 0.007875 + 0.009417 + 0.00925 + 0.008459 + 0.008709 + 0.008125 + 0.007875 + 0.008041 + 0.007583 + 0.007917 + 0.007 + 0.007084 + 0.006792 + 0.006958 + 0.00725 + 0.007084 + 0.00675 + 0.00725 + 0.00675 + 0.006625 + 0.007208 + 0.007542 + 0.007041 + 0.0075 + 0.006875 + 0.007625 + 0.007125 + 0.00725 + 0.007 + 0.006875 + 0.007625 + 0.007125 + 0.007209 + 0.007709 + 0.007875 + 0.007542 + 0.0075 + 0.007542 + 0.007583 + 0.007125 + 0.007334 + 0.007125 + 0.007334 + 0.0075 + 0.013459 + 0.010125 + 0.009292 + 0.009333 + 0.009208 + 0.007792 + 0.0075 + 0.008334 + 0.009917 + 0.009333 + 0.00975 + 0.009041 + 0.009417 + 0.009167 + 0.009958 + 0.010333 + 0.010583 + 0.009208 + 0.017375 + 0.012917 + 0.009458 + 0.008542 + 0.019125 + 0.010375 + 0.0085 + 0.008708 + 0.011291 + 0.008917 + 0.008041 + 0.008209 + 0.013833 + 0.0075 + 0.006416 + 0.006834 + 0.006875 + 0.006709 + 0.006334 + 0.006584 + 0.006625 + 0.006542 + 0.009042 + 0.007833 + 0.0085 + 0.00525 + 0.005916 + 0.008375 + 0.007333 + 0.007333 + 0.007916 + 0.007125 + 0.007208 + 0.007208 + 0.00725 + 0.007333 + 0.007542 + 0.007292 + 0.007 + 0.007 + 0.006958 + 0.007292 + 0.007167 + 0.007833 + 0.006875 + 0.007167 + 0.007292 + 0.007458 + 0.007 + 0.00725 + 0.007292 + 0.007167 + 0.0075 + 0.007542 + 0.007209 + 0.007458 + 0.007167 + 0.007334 + 0.007 + 0.006875 + 0.007083 + 0.006875 + 0.007333 + 0.009625 + 0.007792 + 0.007458 + 0.007333 + 0.007417 + 0.008041 + 0.007333 + 0.010083 + 0.007417 + 0.007167 + 0.010417 + 0.00775 + 0.00775 + 0.0075 + 0.007667 + 0.008833 + 0.011208 + 0.010041 + 0.0075 + 0.007791 + 0.007459 + 0.007333 + 0.007375 + 0.00725 + 0.009333 + 0.02125 + 0.007917 + 0.007583 + 0.007625 + 0.007458 + 0.007 + 0.012875 + 0.007458 + 0.008208 + 0.008584 + 0.008292 + 0.008208 + 0.007958 + 0.007959 + 0.010541 + 0.008333 + 0.007625 + 0.007 + 0.008125 + 0.007667 + 0.007875 + 0.010583 + 0.008625 + 0.007917 + 0.008333 + 0.006416 + 0.006167 + 0.006 + 0.005959 + 0.006541 + 0.006334 + 0.006292 + 0.006958 + 0.006292 + 0.006334 + 0.00775 + 0.005709 + 0.005458 + 0.004833 + 0.006958 + 0.006833 + 0.006875 + 0.006666 + 0.0065 + 0.006625 + 0.006333 + 0.006833 + 0.006792 + 0.007083 + 0.007084 + 0.006709 + 0.006584 + 0.00675 + 0.006917 + 0.006958 + 0.007125 + 0.012417 + 0.007833 + 0.006792 + 0.006916 + 0.006583 + 0.006875 + 0.006708 + 0.006875 + 0.006792 + 0.007042 + 0.00675 + 0.006459 + 0.0065 + 0.006834 + 0.007583 + 0.007083 + 0.007542 + 0.006708 + 0.006791 + 0.006959 + 0.007125 + 0.007 + 0.006875 + 0.007208 + 0.007666 + 0.007125 + 0.007625 + 0.007292 + 0.007334 + 0.00725 + 0.007209 + 0.007459 + 0.007709 + 0.01075 + 0.007917 + 0.007 + 0.007375 + 0.007167 + 0.00725 + 0.007458 + 0.007334 + 0.00725 + 0.006667 + 0.007042 + 0.007291 + 0.006917 + 0.007375 + 0.00725 + 0.007917 + 0.007333 + 0.007333 + 0.00725 + 0.007417 + 0.010458 + 0.0165 + 0.008833 + 0.008208 + 0.007875 + 0.017167 + 0.016042 + 0.014333 + 0.006917 + 0.008583 + 0.008208 + 0.00875 + 0.008625 + 0.022625 + 0.008167 + 0.008125 + 0.006958 + 0.007458 + 0.006958 + 0.014167 + 0.006125 + 0.007167 + 0.006875 + 0.00825 + 0.00675 + 0.008333 + 0.009166 + 0.00575 + 0.006833 + 0.006541 + 0.007375 + 0.007666 + 0.007459 + 0.007042 + 0.007625 + 0.007625 + 0.049458 + 0.013167 + 0.007958 + 0.007583 + 0.007416 + 0.007917 + 0.008167 + 0.009292 + 0.007708 + 0.007166 + 0.007584 + 0.007625 + 0.025208 + 0.009 + 0.009291 + 0.008375 + 0.007583 + 0.0075 + 0.007709 + 0.0075 + 0.007708 + 0.007458 + 0.007667 + 0.007375 + 0.008042 + 0.007166 + 0.006917 + 0.010709 + 0.007458 + 0.007209 + 0.00775 + 0.008 + 0.007583 + 0.007459 + 0.007666 + 0.007292 + 0.007792 + 0.010583 + 0.009083 + 0.009375 + 0.009666 + 0.009917 + 0.009459 + 0.009292 + 0.01425 + 0.010625 + 0.008708 + 0.008667 + 0.008667 + 0.007583 + 0.007833 + 0.007583 + 0.007125 + 0.007834 + 0.007166 + 0.007917 + 0.0085 + 0.014 + 0.008083 + 0.008167 + 0.008709 + 0.008792 + 0.007958 + 0.008625 + 0.009084 + 0.016292 + 0.011375 + 0.011042 + 0.010584 + 0.008917 + 0.008083 + 0.010791 + 0.011875 + 0.01275 + 0.010834 + 0.009542 + 0.007875 + 0.008 + 0.010125 + 0.011125 + 0.010458 + 0.009542 + 0.00825 + 0.007417 + 0.006041 + 0.007042 + 0.007 + 0.007542 + 0.008167 + 0.00775 + 0.009708 + 0.008708 + 0.007833 + 0.00775 + 0.007416 + 0.010875 + 0.0085 + 0.007459 + 0.008 + 0.008875 + 0.009083 + 0.0075 + 0.015042 + 0.00825 + 0.008125 + 0.007542 + 0.006458 + 0.006458 + 0.007833 + 0.008208 + 0.007167 + 0.006625 + 0.007291 + 0.006625 + 0.006583 + 0.006625 + 0.006792 + 0.006834 + 0.007 + 0.006791 + 0.006875 + 0.006625 + 0.006916 + 0.006708 + 0.007167 + 0.0065 + 0.006792 + 0.007167 + 0.007083 + 0.006792 + 0.006625 + 0.007416 + 0.007167 + 0.006875 + 0.006917 + 0.00675 + 0.006959 + 0.006666 + 0.00725 + 0.007166 + 0.007334 + 0.007333 + 0.007042 + 0.007083 + 0.007708 + 0.007 + 0.007791 + 0.016792 + 0.007667 + 0.007 + 0.010167 + 0.009833 + 0.00875 + 0.007792 + 0.008291 + 0.008833 + 0.009708 + 0.013041 + 0.009833 + 0.009416 + 0.00925 + 0.008958 + 0.009666 + 0.010042 + 0.009792 + 0.011291 + 0.0135 + 0.011458 + 0.011167 + 0.0105 + 0.010042 + 0.008667 + 0.008667 + 0.008 + 0.011583 + 0.009333 + 0.007958 + 0.007625 + 0.007833 + 0.015583 + 0.018125 + 0.007916 + 0.006458 + 0.008125 + 0.007792 + 0.007833 + 0.007541 + 0.007708 + 0.007917 + 0.007125 + 0.007583 + 0.00775 + 0.008084 + 0.005708 + 0.004917 + 0.007 + 0.007042 + 0.006792 + 0.006666 + 0.00675 + 0.007 + 0.006708 + 0.006708 + 0.006667 + 0.018625 + 0.00875 + 0.007333 + 0.007625 + 0.006625 + 0.0065 + 0.006833 + 0.006458 + 0.006833 + 0.006542 + 0.007125 + 0.006916 + 0.007084 + 0.0065 + 0.007209 + 0.00675 + 0.006666 + 0.006583 + 0.007167 + 0.007125 + 0.007125 + 0.006584 + 0.006375 + 0.00675 + 0.007916 + 0.007333 + 0.00775 + 0.007625 + 0.007417 + 0.007833 + 0.007042 + 0.007041 + 0.007625 + 0.006583 + 0.007667 + 0.007291 + 0.00725 + 0.007209 + 0.007084 + 0.007083 + 0.007208 + 0.007083 + 0.007125 + 0.007208 + 0.007583 + 0.007042 + 0.007042 + 0.007 + 0.007084 + 0.009292 + 0.009 + 0.008792 + 0.00925 + 0.007417 + 0.007291 + 0.007167 + 0.006708 + 0.006916 + 0.00725 + 0.00725 + 0.007125 + 0.008459 + 0.009291 + 0.0105 + 0.013708 + 0.007958 + 0.008333 + 0.008208 + 0.00775 + 0.0075 + 0.0105 + 0.009625 + 0.009375 + 0.008959 + 0.008333 + 0.009042 + 0.01025 + 0.008125 + 0.008042 + 0.007625 + 0.0075 + 0.007958 + 0.008167 + 0.00675 + 0.007792 + 0.008042 + 0.008125 + 0.008583 + 0.006042 + 0.006 + 0.005875 + 0.008958 + 0.009042 + 0.008792 + 0.008458 + 0.008542 + 0.007666 + 0.007375 + 0.007292 + 0.007167 + 0.007458 + 0.00775 + 0.008666 + 0.009042 + 0.00775 + 0.007 + 0.007375 + 0.007333 + 0.007958 + 0.008583 + 0.00825 + 0.006625 + 0.007125 + 0.006459 + 0.00725 + 0.007792 + 0.008666 + 0.008334 + 0.007917 + 0.007666 + 0.007625 + 0.007875 + 0.008625 + 0.008834 + 0.009084 + 0.008334 + 0.008542 + 0.008333 + 0.00725 + 0.007334 + 0.006791 + 0.006958 + 0.007 + 0.007084 + 0.006875 + 0.006958 + 0.007333 + 0.006667 + 0.007083 + 0.006833 + 0.007166 + 0.006959 + 0.007 + 0.006833 + 0.007292 + 0.006666 + 0.00725 + 0.007167 + 0.006958 + 0.006917 + 0.007458 + 0.007042 + 0.006959 + 0.008209 + 0.008792 + 0.008333 + 0.008333 + 0.007 + 0.007333 + 0.007292 + 0.00725 + 0.006708 + 0.008125 + 0.0075 + 0.008167 + 0.00975 + 0.009291 + 0.009167 + 0.008 + 0.007167 + 0.008083 + 0.007375 + 0.010334 + 0.009292 + 0.009375 + 0.008375 + 0.007583 + 0.0075 + 0.007458 + 0.007166 + 0.006292 + 0.006208 + 0.006583 + 0.005875 + 0.005792 + 0.005917 + 0.007458 + 0.009541 + 0.007125 + 0.00825 + 0.006042 + 0.008625 + 0.011875 + 0.006875 + 0.009 + 0.0095 + 0.008542 + 0.0085 + 0.009542 + 0.007042 + 0.006041 + 0.00625 + 0.006375 + 0.00625 + 0.006125 + 0.007 + 0.007333 + 0.009042 + 0.008959 + 0.017375 + 0.010125 + 0.008125 + 0.007625 + 0.007125 + 0.007292 + 0.008208 + 0.008708 + 0.00875 + 0.007 + 0.007541 + 0.008292 + 0.008709 + 0.018959 + 0.035334 + 0.014125 + 0.009083 + 0.008292 + 0.008709 + 0.009209 + 0.009041 + 0.00775 + 0.00725 + 0.007416 + 0.008375 + 0.008667 + 0.010667 + 0.01225 + 0.010166 + 0.008875 + 0.008292 + 0.008625 + 0.008708 + 0.008 + 0.008916 + 0.008791 + 0.0085 + 0.0085 + 0.008833 + 0.008458 + 0.008416 + 0.00875 + 0.010375 + 0.010083 + 0.0085 + 0.008542 + 0.008625 + 0.012791 + 0.011 + 0.009333 + 0.0085 + 0.009166 + 0.008792 + 0.010291 + 0.0145 + 0.010084 + 0.022667 + 0.017958 + 0.010166 + 0.009958 + 0.009333 + 0.009792 + 0.009541 + 0.008792 + 0.010125 + 0.010709 + 0.010333 + 0.010541 + 0.010459 + 0.007167 + 0.006542 + 0.006291 + 0.006042 + 0.006208 + 0.005917 + 0.006208 + 0.006709 + 0.005875 + 0.006709 + 0.007459 + 0.0065 + 0.005333 + 0.005208 + 0.007375 + 0.007 + 0.006542 + 0.006792 + 0.006708 + 0.006708 + 0.006875 + 0.006834 + 0.006292 + 0.014708 + 0.006667 + 0.00675 + 0.006542 + 0.00675 + 0.006291 + 0.0065 + 0.00675 + 0.007542 + 0.008375 + 0.008625 + 0.00975 + 0.009708 + 0.009084 + 0.007875 + 0.00775 + 0.007541 + 0.007375 + 0.007625 + 0.007625 + 0.008334 + 0.00775 + 0.007834 + 0.008125 + 0.007958 + 0.007875 + 0.008458 + 0.010583 + 0.009625 + 0.010375 + 0.009458 + 0.008 + 0.007875 + 0.010167 + 0.009584 + 0.009125 + 0.009292 + 0.010167 + 0.010708 + 0.011041 + 0.00975 + 0.007833 + 0.007917 + 0.007791 + 0.007417 + 0.007041 + 0.00825 + 0.009584 + 0.009625 + 0.00975 + 0.009625 + 0.009084 + 0.010375 + 0.01025 + 0.008917 + 0.008458 + 0.008875 + 0.009583 + 0.01025 + 0.010417 + 0.00975 + 0.012125 + 0.010167 + 0.009333 + 0.009167 + 0.008 + 0.010542 + 0.011584 + 0.009375 + 0.008125 + 0.008958 + 0.0145 + 0.008417 + 0.010042 + 0.010375 + 0.010125 + 0.008875 + 0.00675 + 0.0075 + 0.007625 + 0.007666 + 0.007625 + 0.007458 + 0.007375 + 0.007459 + 0.010042 + 0.0155 + 0.013083 + 0.011583 + 0.007625 + 0.007792 + 0.009042 + 0.009125 + 0.007958 + 0.007958 + 0.007625 + 0.009334 + 0.007542 + 0.008041 + 0.007875 + 0.008167 + 0.008667 + 0.009333 + 0.008708 + 0.007542 + 0.006541 + 0.006625 + 0.006667 + 0.0125 + 0.007875 + 0.007291 + 0.007292 + 0.008042 + 0.015459 + 0.006917 + 0.007416 + 0.007208 + 0.007083 + 0.007333 + 0.0075 + 0.006292 + 0.0065 + 0.006917 + 0.006791 + 0.007208 + 0.006875 + 0.006917 + 0.007417 + 0.007333 + 0.00725 + 0.008291 + 0.008208 + 0.00825 + 0.009917 + 0.01 + 0.008584 + 0.008209 + 0.007875 + 0.007875 + 0.009291 + 0.013333 + 0.008417 + 0.0085 + 0.008875 + 0.009875 + 0.009208 + 0.00875 + 0.008709 + 0.00975 + 0.009292 + 0.009792 + 0.010708 + 0.01225 + 0.00975 + 0.007708 + 0.006833 + 0.007458 + 0.007375 + 0.007542 + 0.006917 + 0.007333 + 0.010625 + 0.009625 + 0.009208 + 0.00825 + 0.007542 + 0.00825 + 0.013209 + 0.009584 + 0.00875 + 0.008541 + 0.007959 + 0.007709 + 0.008042 + 0.009875 + 0.009459 + 0.009917 + 0.006541 + 0.006041 + 0.00575 + 0.006125 + 0.006375 + 0.0065 + 0.006583 + 0.006458 + 0.005875 + 0.005875 + 0.0075 + 0.005334 + 0.00525 + 0.005334 + 0.006584 + 0.006958 + 0.007042 + 0.006417 + 0.0065 + 0.006625 + 0.006917 + 0.007125 + 0.007 + 0.006833 + 0.006458 + 0.006584 + 0.006583 + 0.00675 + 0.006708 + 0.00725 + 0.006791 + 0.006417 + 0.007 + 0.00675 + 0.007 + 0.007125 + 0.007 + 0.006833 + 0.006875 + 0.006583 + 0.006917 + 0.007084 + 0.00675 + 0.006583 + 0.007167 + 0.007041 + 0.0075 + 0.00675 + 0.006667 + 0.00675 + 0.006583 + 0.006916 + 0.006958 + 0.007 + 0.007459 + 0.008042 + 0.007125 + 0.007459 + 0.00725 + 0.0075 + 0.00675 + 0.007167 + 0.007334 + 0.006875 + 0.007334 + 0.00725 + 0.017917 + 0.006667 + 0.007208 + 0.009708 + 0.009042 + 0.00875 + 0.008167 + 0.008084 + 0.008375 + 0.006709 + 0.007167 + 0.007708 + 0.007833 + 0.007417 + 0.007083 + 0.008375 + 0.008875 + 0.008375 + 0.008917 + 0.010542 + 0.011083 + 0.010583 + 0.008833 + 0.009166 + 0.010042 + 0.010292 + 0.008333 + 0.015 + 0.010583 + 0.010167 + 0.008584 + 0.0095 + 0.01 + 0.011291 + 0.010042 + 0.008083 + 0.008083 + 0.008209 + 0.008084 + 0.007166 + 0.006209 + 0.006333 + 0.006667 + 0.008125 + 0.009042 + 0.0065 + 0.015125 + 0.006458 + 0.007875 + 0.00825 + 0.008 + 0.008541 + 0.008333 + 0.008875 + 0.008791 + 0.008584 + 0.008958 + 0.008833 + 0.006791 + 0.006959 + 0.006541 + 0.006834 + 0.006625 + 0.006792 + 0.006334 + 0.006625 + 0.006958 + 0.006667 + 0.006459 + 0.006625 + 0.006792 + 0.006708 + 0.006666 + 0.006333 + 0.007542 + 0.008125 + 0.008625 + 0.008292 + 0.008917 + 0.008791 + 0.009334 + 0.007834 + 0.00675 + 0.006708 + 0.006333 + 0.007166 + 0.007042 + 0.007042 + 0.006792 + 0.00675 + 0.007 + 0.007084 + 0.007583 + 0.007083 + 0.006833 + 0.007916 + 0.006917 + 0.006958 + 0.007041 + 0.007042 + 0.00725 + 0.006542 + 0.007209 + 0.007459 + 0.007166 + 0.006917 + 0.006584 + 0.012792 + 0.009167 + 0.009042 + 0.0095 + 0.009 + 0.007917 + 0.007 + 0.007458 + 0.0075 + 0.007292 + 0.007417 + 0.006917 + 0.008166 + 0.008208 + 0.009875 + 0.015333 + 0.010291 + 0.009958 + 0.00925 + 0.007417 + 0.00775 + 0.010292 + 0.010458 + 0.009583 + 0.008667 + 0.009583 + 0.0105 + 0.0115 + 0.007 + 0.006666 + 0.006125 + 0.006416 + 0.006084 + 0.007 + 0.007459 + 0.008417 + 0.00725 + 0.007959 + 0.006958 + 0.007416 + 0.005042 + 0.005584 + 0.007375 + 0.007042 + 0.006709 + 0.006667 + 0.007 + 0.007333 + 0.007833 + 0.006875 + 0.00625 + 0.006416 + 0.006792 + 0.006458 + 0.007083 + 0.0065 + 0.006375 + 0.006292 + 0.006458 + 0.007 + 0.006291 + 0.006625 + 0.006459 + 0.006375 + 0.00675 + 0.006583 + 0.006667 + 0.006625 + 0.006375 + 0.006667 + 0.006458 + 0.0065 + 0.006625 + 0.006666 + 0.006584 + 0.006333 + 0.00625 + 0.006792 + 0.007 + 0.006792 + 0.006708 + 0.006584 + 0.006625 + 0.00675 + 0.0065 + 0.007333 + 0.006917 + 0.007209 + 0.007042 + 0.006875 + 0.006916 + 0.006833 + 0.00675 + 0.007208 + 0.006917 + 0.00675 + 0.00725 + 0.006917 + 0.006625 + 0.006625 + 0.006916 + 0.007083 + 0.007083 + 0.007084 + 0.012375 + 0.009459 + 0.008834 + 0.008667 + 0.008916 + 0.008833 + 0.008959 + 0.0085 + 0.010625 + 0.010041 + 0.008583 + 0.009959 + 0.010167 + 0.009916 + 0.010375 + 0.009334 + 0.007584 + 0.010083 + 0.010583 + 0.009208 + 0.008625 + 0.008375 + 0.008084 + 0.008666 + 0.006583 + 0.006291 + 0.005875 + 0.006208 + 0.006125 + 0.006166 + 0.00625 + 0.005834 + 0.005834 + 0.006875 + 0.007417 + 0.005125 + 0.005042 + 0.00475 + 0.006834 + 0.006584 + 0.006291 + 0.006833 + 0.006917 + 0.006542 + 0.006375 + 0.012 + 0.007167 + 0.0065 + 0.006292 + 0.006959 + 0.006291 + 0.006584 + 0.0065 + 0.00675 + 0.018916 + 0.00675 + 0.006708 + 0.006542 + 0.006834 + 0.0065 + 0.007791 + 0.008417 + 0.013375 + 0.009333 + 0.008875 + 0.008667 + 0.008209 + 0.007667 + 0.006458 + 0.006375 + 0.006208 + 0.006833 + 0.006417 + 0.006625 + 0.007709 + 0.00675 + 0.006833 + 0.006584 + 0.00625 + 0.006417 + 0.00675 + 0.007208 + 0.007208 + 0.0075 + 0.007083 + 0.007083 + 0.006875 + 0.006833 + 0.006917 + 0.00725 + 0.006792 + 0.006791 + 0.006667 + 0.006958 + 0.007833 + 0.006791 + 0.007084 + 0.006834 + 0.00675 + 0.011834 + 0.009292 + 0.009166 + 0.009208 + 0.00925 + 0.00775 + 0.017042 + 0.010875 + 0.007459 + 0.008166 + 0.00775 + 0.008667 + 0.023792 + 0.011083 + 0.017291 + 0.008417 + 0.008209 + 0.010042 + 0.007542 + 0.011667 + 0.011625 + 0.009625 + 0.024625 + 0.027041 + 0.009209 + 0.008209 + 0.007125 + 0.00725 + 0.006917 + 0.006708 + 0.006959 + 0.006458 + 0.007375 + 0.007083 + 0.006917 + 0.0095 + 0.005333 + 0.005792 + 0.006375 + 0.008167 + 0.007375 + 0.007084 + 0.018792 + 0.006791 + 0.007292 + 0.009125 + 0.008042 + 0.006958 + 0.007 + 0.006791 + 0.006417 + 0.013958 + 0.008 + 0.007625 + 0.007667 + 0.00775 + 0.007166 + 0.007167 + 0.006916 + 0.006958 + 0.006917 + 0.00775 + 0.006625 + 0.015291 + 0.007083 + 0.007083 + 0.006584 + 0.006833 + 0.007 + 0.007167 + 0.006958 + 0.007125 + 0.007 + 0.006833 + 0.007042 + 0.006959 + 0.006875 + 0.006875 + 0.007708 + 0.008458 + 0.008416 + 0.008417 + 0.010625 + 0.011792 + 0.009417 + 0.009292 + 0.009167 + 0.007958 + 0.007083 + 0.007166 + 0.007625 + 0.007666 + 0.016792 + 0.007334 + 0.008459 + 0.008666 + 0.007375 + 0.007083 + 0.00725 + 0.00725 + 0.007209 + 0.008 + 0.007458 + 0.009209 + 0.009 + 0.01 + 0.008208 + 0.007541 + 0.007917 + 0.007125 + 0.008542 + 0.008125 + 0.0085 + 0.008125 + 0.011292 + 0.01075 + 0.010042 + 0.009791 + 0.009541 + 0.008542 + 0.008709 + 0.010583 + 0.009958 + 0.009625 + 0.009083 + 0.008708 + 0.007125 + 0.00675 + 0.006541 + 0.006375 + 0.006333 + 0.006458 + 0.005916 + 0.006958 + 0.006708 + 0.008208 + 0.00575 + 0.00525 + 0.005625 + 0.008666 + 0.009417 + 0.009125 + 0.008417 + 0.007542 + 0.007625 + 0.007708 + 0.008541 + 0.011125 + 0.01425 + 0.01225 + 0.007709 + 0.007666 + 0.0065 + 0.006833 + 0.006375 + 0.007167 + 0.007875 + 0.007708 + 0.00675 + 0.007125 + 0.006667 + 0.006208 + 0.006333 + 0.006708 + 0.006292 + 0.006416 + 0.006834 + 0.006834 + 0.006416 + 0.006542 + 0.006458 + 0.00625 + 0.007083 + 0.00675 + 0.006792 + 0.006333 + 0.00775 + 0.007083 + 0.006625 + 0.007084 + 0.007041 + 0.006792 + 0.007 + 0.007708 + 0.007667 + 0.007083 + 0.00725 + 0.009166 + 0.008792 + 0.013334 + 0.007583 + 0.007208 + 0.007167 + 0.007334 + 0.007084 + 0.007167 + 0.007375 + 0.008125 + 0.008667 + 0.009834 + 0.00975 + 0.009458 + 0.009292 + 0.008625 + 0.009292 + 0.01025 + 0.009541 + 0.010875 + 0.010083 + 0.009375 + 0.023 + 0.0145 + 0.011541 + 0.011542 + 0.009667 + 0.010791 + 0.009292 + 0.007916 + 0.008417 + 0.008916 + 0.009167 + 0.009583 + 0.011 + 0.009834 + 0.009334 + 0.016417 + 0.00675 + 0.006166 + 0.006917 + 0.0075 + 0.007792 + 0.007916 + 0.007916 + 0.008458 + 0.007542 + 0.009 + 0.007917 + 0.0075 + 0.005125 + 0.005542 + 0.007417 + 0.006792 + 0.006666 + 0.006542 + 0.006792 + 0.006625 + 0.007042 + 0.0065 + 0.006459 + 0.006209 + 0.013417 + 0.007167 + 0.006459 + 0.006666 + 0.006917 + 0.007208 + 0.00625 + 0.0065 + 0.006458 + 0.006917 + 0.006583 + 0.00675 + 0.007041 + 0.006583 + 0.006792 + 0.006834 + 0.006542 + 0.00625 + 0.006292 + 0.006583 + 0.00675 + 0.006458 + 0.006375 + 0.006375 + 0.0065 + 0.006125 + 0.008208 + 0.008083 + 0.007834 + 0.007417 + 0.006958 + 0.006916 + 0.007083 + 0.009292 + 0.009666 + 0.010417 + 0.009791 + 0.009292 + 0.010959 + 0.009292 + 0.009458 + 0.008583 + 0.007292 + 0.008542 + 0.008125 + 0.008417 + 0.01025 + 0.010083 + 0.009667 + 0.007917 + 0.008542 + 0.008083 + 0.008375 + 0.008166 + 0.007334 + 0.00825 + 0.009167 + 0.00925 + 0.009333 + 0.009834 + 0.010584 + 0.0085 + 0.008542 + 0.008625 + 0.00825 + 0.007708 + 0.010792 + 0.010292 + 0.0095 + 0.010542 + 0.010041 + 0.008208 + 0.008167 + 0.010917 + 0.019208 + 0.011875 + 0.0085 + 0.009083 + 0.008333 + 0.007292 + 0.006542 + 0.007917 + 0.008959 + 0.011167 + 0.007833 + 0.008916 + 0.010875 + 0.008375 + 0.011292 + 0.006125 + 0.007417 + 0.007208 + 0.009291 + 0.008625 + 0.008333 + 0.007792 + 0.007959 + 0.007875 + 0.00725 + 0.008 + 0.0085 + 0.008792 + 0.008166 + 0.009 + 0.008375 + 0.008584 + 0.008084 + 0.006792 + 0.006958 + 0.006667 + 0.0065 + 0.006584 + 0.006709 + 0.0065 + 0.006667 + 0.006792 + 0.00675 + 0.007042 + 0.006292 + 0.006541 + 0.006375 + 0.007708 + 0.00675 + 0.007 + 0.006666 + 0.006625 + 0.007208 + 0.007166 + 0.006958 + 0.006958 + 0.007083 + 0.007084 + 0.007125 + 0.0075 + 0.007375 + 0.007083 + 0.007125 + 0.006916 + 0.006833 + 0.007125 + 0.007333 + 0.007208 + 0.006875 + 0.007375 + 0.01 + 0.009375 + 0.009167 + 0.008166 + 0.007459 + 0.008166 + 0.0095 + 0.008 + 0.00825 + 0.011125 + 0.008 + 0.008083 + 0.007708 + 0.007875 + 0.008625 + 0.008625 + 0.012416 + 0.010667 + 0.010291 + 0.009625 + 0.01125 + 0.010916 + 0.012292 + 0.011416 + 0.009292 + 0.010875 + 0.011084 + 0.011167 + 0.010625 + 0.012083 + 0.011042 + 0.01 + 0.007667 + 0.007333 + 0.007375 + 0.008208 + 0.009583 + 0.02575 + 0.015 + 0.008709 + 0.007875 + 0.007542 + 0.009 + 0.008292 + 0.006791 + 0.007208 + 0.018792 + 0.009625 + 0.009042 + 0.035584 + 0.011083 + 0.007583 + 0.007917 + 0.007666 + 0.007667 + 0.006917 + 0.007125 + 0.007417 + 0.006917 + 0.00725 + 0.006875 + 0.007125 + 0.006916 + 0.007167 + 0.007708 + 0.007958 + 0.007875 + 0.009084 + 0.010125 + 0.010167 + 0.009708 + 0.050208 + 0.008166 + 0.00825 + 0.008 + 0.009167 + 0.010375 + 0.009208 + 0.007917 + 0.007375 + 0.007583 + 0.007208 + 0.007792 + 0.009708 + 0.010125 + 0.009292 + 0.009208 + 0.008709 + 0.008708 + 0.009625 + 0.010417 + 0.010584 + 0.010209 + 0.009042 + 0.008541 + 0.0085 + 0.013333 + 0.009916 + 0.008417 + 0.008541 + 0.008334 + 0.008458 + 0.00875 + 0.008791 + 0.008125 + 0.008625 + 0.008334 + 0.009 + 0.009916 + 0.009833 + 0.009167 + 0.0095 + 0.008125 + 0.009458 + 0.011625 + 0.013833 + 0.011333 + 0.017125 + 0.012833 + 0.012834 + 0.011542 + 0.008958 + 0.009333 + 0.011542 + 0.012417 + 0.014166 + 0.012959 + 0.011375 + 0.009042 + 0.008958 + 0.011375 + 0.010959 + 0.009625 + 0.007334 + 0.007125 + 0.006792 + 0.007416 + 0.007958 + 0.008542 + 0.007875 + 0.008458 + 0.008625 + 0.010834 + 0.0075 + 0.009625 + 0.007 + 0.009292 + 0.00975 + 0.009542 + 0.009208 + 0.008208 + 0.00675 + 0.006875 + 0.006917 + 0.00725 + 0.007209 + 0.006917 + 0.007083 + 0.006833 + 0.007 + 0.007208 + 0.007208 + 0.006958 + 0.007125 + 0.0075 + 0.007542 + 0.006833 + 0.006916 + 0.007125 + 0.006917 + 0.007208 + 0.007166 + 0.007125 + 0.007375 + 0.007042 + 0.00775 + 0.006791 + 0.006958 + 0.007208 + 0.007292 + 0.007041 + 0.007292 + 0.006834 + 0.007958 + 0.007208 + 0.007041 + 0.007083 + 0.007167 + 0.008 + 0.006917 + 0.007125 + 0.0075 + 0.007167 + 0.0075 + 0.0075 + 0.008084 + 0.007833 + 0.007083 + 0.007667 + 0.007459 + 0.00775 + 0.007334 + 0.01025 + 0.010375 + 0.009709 + 0.009584 + 0.00925 + 0.007917 + 0.007333 + 0.007042 + 0.007375 + 0.007709 + 0.008 + 0.007625 + 0.007166 + 0.007333 + 0.00725 + 0.008958 + 0.010208 + 0.010875 + 0.010958 + 0.009916 + 0.009625 + 0.008583 + 0.008583 + 0.011375 + 0.011416 + 0.010875 + 0.010416 + 0.010333 + 0.00875 + 0.013583 + 0.010375 + 0.00825 + 0.009875 + 0.008708 + 0.0085 + 0.006667 + 0.006167 + 0.006125 + 0.00675 + 0.006167 + 0.007292 + 0.008334 + 0.009417 + 0.017042 + 0.011292 + 0.009291 + 0.009 + 0.008792 + 0.013625 + 0.00775 + 0.008333 + 0.008125 + 0.008958 + 0.009166 + 0.008625 + 0.009208 + 0.009292 + 0.008334 + 0.007334 + 0.007291 + 0.008334 + 0.008084 + 0.008125 + 0.00875 + 0.009166 + 0.009167 + 0.009583 + 0.008917 + 0.008 + 0.008292 + 0.007125 + 0.006667 + 0.007208 + 0.00725 + 0.006917 + 0.007625 + 0.006625 + 0.007042 + 0.007 + 0.006375 + 0.00675 + 0.007417 + 0.007416 + 0.007375 + 0.00725 + 0.007125 + 0.007042 + 0.00725 + 0.00775 + 0.007417 + 0.007375 + 0.007959 + 0.007125 + 0.007209 + 0.007084 + 0.007166 + 0.007542 + 0.006958 + 0.007833 + 0.00775 + 0.007292 + 0.007083 + 0.007083 + 0.009542 + 0.008583 + 0.009125 + 0.008333 + 0.008875 + 0.008833 + 0.0085 + 0.007917 + 0.006958 + 0.007417 + 0.01575 + 0.009458 + 0.012667 + 0.008542 + 0.008542 + 0.008584 + 0.010708 + 0.00925 + 0.008542 + 0.008042 + 0.008625 + 0.010959 + 0.0085 + 0.010709 + 0.0085 + 0.008167 + 0.008125 + 0.008417 + 0.007625 + 0.006667 + 0.006791 + 0.0065 + 0.006292 + 0.006625 + 0.006666 + 0.0065 + 0.0065 + 0.006958 + 0.008708 + 0.037417 + 0.008917 + 0.0065 + 0.012041 + 0.007625 + 0.007583 + 0.007417 + 0.007959 + 0.00775 + 0.008083 + 0.007333 + 0.007542 + 0.007125 + 0.007166 + 0.007583 + 0.00775 + 0.008959 + 0.0085 + 0.008042 + 0.008709 + 0.007875 + 0.0085 + 0.007917 + 0.0075 + 0.007709 + 0.007708 + 0.009791 + 0.009125 + 0.009167 + 0.008708 + 0.007958 + 0.007583 + 0.008083 + 0.008083 + 0.007666 + 0.008625 + 0.0085 + 0.007875 + 0.008583 + 0.010625 + 0.009416 + 0.008958 + 0.008709 + 0.00825 + 0.007917 + 0.008375 + 0.014416 + 0.00925 + 0.008166 + 0.008875 + 0.012834 + 0.011542 + 0.01075 + 0.011167 + 0.027666 + 0.012667 + 0.015208 + 0.009 + 0.00825 + 0.00825 + 0.008875 + 0.00925 + 0.009042 + 0.0105 + 0.01175 + 0.00775 + 0.008458 + 0.007791 + 0.007875 + 0.008375 + 0.010875 + 0.010542 + 0.011833 + 0.018417 + 0.010875 + 0.011792 + 0.011916 + 0.013083 + 0.011166 + 0.011084 + 0.00925 + 0.010334 + 0.011709 + 0.009209 + 0.010584 + 0.010666 + 0.009542 + 0.009084 + 0.009042 + 0.006541 + 0.006959 + 0.007125 + 0.007042 + 0.007416 + 0.006417 + 0.007167 + 0.007291 + 0.007 + 0.006708 + 0.0085 + 0.00525 + 0.00625 + 0.006083 + 0.006834 + 0.00725 + 0.006959 + 0.0065 + 0.010292 + 0.00725 + 0.007042 + 0.007083 + 0.006708 + 0.006916 + 0.006625 + 0.010792 + 0.007292 + 0.007083 + 0.00775 + 0.007542 + 0.008542 + 0.007083 + 0.007 + 0.007291 + 0.007542 + 0.008209 + 0.012459 + 0.009 + 0.008958 + 0.008833 + 0.008833 + 0.010417 + 0.007292 + 0.007459 + 0.007375 + 0.006417 + 0.008708 + 0.009542 + 0.010375 + 0.009042 + 0.00875 + 0.008583 + 0.008708 + 0.010208 + 0.00975 + 0.008459 + 0.008375 + 0.010917 + 0.009959 + 0.009625 + 0.009875 + 0.009542 + 0.0095 + 0.00975 + 0.009667 + 0.008667 + 0.008417 + 0.007375 + 0.008209 + 0.009167 + 0.009458 + 0.009667 + 0.007667 + 0.008208 + 0.007416 + 0.007875 + 0.008792 + 0.008583 + 0.008958 + 0.011375 + 0.010292 + 0.01 + 0.009958 + 0.009375 + 0.008875 + 0.01 + 0.008166 + 0.008875 + 0.010208 + 0.011583 + 0.011375 + 0.015625 + 0.010167 + 0.009375 + 0.01025 + 0.010291 + 0.012417 + 0.010542 + 0.011083 + 0.010625 + 0.009625 + 0.008375 + 0.009208 + 0.007125 + 0.008041 + 0.007792 + 0.0075 + 0.006791 + 0.006583 + 0.006292 + 0.006541 + 0.008625 + 0.00675 + 0.005458 + 0.006041 + 0.009125 + 0.007583 + 0.00775 + 0.00675 + 0.007166 + 0.007333 + 0.007 + 0.011125 + 0.007417 + 0.007208 + 0.00675 + 0.00975 + 0.007541 + 0.006833 + 0.011708 + 0.0075 + 0.008083 + 0.007958 + 0.007083 + 0.007792 + 0.006875 + 0.007208 + 0.007333 + 0.010417 + 0.007709 + 0.007584 + 0.008792 + 0.006708 + 0.007083 + 0.007083 + 0.009417 + 0.007666 + 0.007125 + 0.007125 + 0.007583 + 0.007958 + 0.009458 + 0.009125 + 0.00975 + 0.009083 + 0.00925 + 0.012167 + 0.00825 + 0.008208 + 0.00725 + 0.007208 + 0.012292 + 0.009167 + 0.014208 + 0.007833 + 0.011 + 0.007042 + 0.007167 + 0.006833 + 0.006958 + 0.007584 + 0.007792 + 0.007416 + 0.007292 + 0.007125 + 0.007542 + 0.007583 + 0.007417 + 0.007708 + 0.007375 + 0.010833 + 0.009917 + 0.009167 + 0.008541 + 0.007667 + 0.008333 + 0.011958 + 0.009083 + 0.008458 + 0.009292 + 0.015042 + 0.010334 + 0.009917 + 0.009042 + 0.010834 + 0.008792 + 0.008208 + 0.007917 + 0.011708 + 0.008333 + 0.008 + 0.006083 + 0.006375 + 0.006709 + 0.006375 + 0.006208 + 0.006083 + 0.006292 + 0.005792 + 0.006667 + 0.007083 + 0.007917 + 0.007792 + 0.004958 + 0.005583 + 0.00675 + 0.011042 + 0.00675 + 0.0065 + 0.007334 + 0.006667 + 0.006583 + 0.006458 + 0.006667 + 0.009416 + 0.006667 + 0.00675 + 0.006625 + 0.011 + 0.007083 + 0.006458 + 0.006625 + 0.016583 + 0.008458 + 0.008958 + 0.00975 + 0.009125 + 0.008666 + 0.008667 + 0.008709 + 0.011917 + 0.007334 + 0.007709 + 0.007334 + 0.007334 + 0.016875 + 0.013125 + 0.008792 + 0.008625 + 0.0105 + 0.009334 + 0.009584 + 0.009958 + 0.009375 + 0.01 + 0.009541 + 0.00925 + 0.009458 + 0.009583 + 0.007792 + 0.007459 + 0.007458 + 0.0075 + 0.00725 + 0.009625 + 0.01 + 0.010125 + 0.009 + 0.008417 + 0.008083 + 0.013333 + 0.0095 + 0.009791 + 0.009042 + 0.009417 + 0.009875 + 0.007584 + 0.007084 + 0.0075 + 0.00775 + 0.009958 + 0.009291 + 0.009542 + 0.008791 + 0.00875 + 0.010333 + 0.010958 + 0.011416 + 0.011292 + 0.0105 + 0.010209 + 0.008334 + 0.0105 + 0.01 + 0.010375 + 0.010667 + 0.009666 + 0.008 + 0.008416 + 0.010917 + 0.01025 + 0.008333 + 0.008583 + 0.007792 + 0.0085 + 0.006167 + 0.006458 + 0.006333 + 0.0065 + 0.006375 + 0.006542 + 0.008584 + 0.00525 + 0.0055 + 0.0055 + 0.006792 + 0.0065 + 0.006834 + 0.006584 + 0.006667 + 0.006708 + 0.0065 + 0.006667 + 0.006625 + 0.006834 + 0.006542 + 0.006458 + 0.006542 + 0.0065 + 0.006125 + 0.006416 + 0.00675 + 0.00675 + 0.007125 + 0.006375 + 0.006541 + 0.006667 + 0.006041 + 0.006667 + 0.0065 + 0.006459 + 0.006625 + 0.006375 + 0.0065 + 0.006542 + 0.00625 + 0.006333 + 0.007042 + 0.006708 + 0.006792 + 0.006125 + 0.00725 + 0.006791 + 0.006833 + 0.006584 + 0.006917 + 0.007125 + 0.007 + 0.007166 + 0.007333 + 0.00675 + 0.007167 + 0.006625 + 0.007041 + 0.007209 + 0.007334 + 0.00725 + 0.007125 + 0.007084 + 0.006959 + 0.006833 + 0.007167 + 0.008292 + 0.009 + 0.009167 + 0.00925 + 0.00925 + 0.008958 + 0.007959 + 0.007792 + 0.007 + 0.007 + 0.006791 + 0.007292 + 0.007125 + 0.006334 + 0.007625 + 0.007667 + 0.01025 + 0.010375 + 0.009667 + 0.008292 + 0.009916 + 0.009833 + 0.011083 + 0.012 + 0.00925 + 0.009167 + 0.008625 + 0.009333 + 0.008458 + 0.009083 + 0.00675 + 0.006667 + 0.006625 + 0.0065 + 0.007625 + 0.007458 + 0.009167 + 0.009792 + 0.009125 + 0.056042 + 0.01625 + 0.009375 + 0.006584 + 0.011167 + 0.008541 + 0.009125 + 0.009625 + 0.008625 + 0.009916 + 0.009917 + 0.009792 + 0.010083 + 0.009208 + 0.008583 + 0.008583 + 0.02475 + 0.009833 + 0.008458 + 0.007833 + 0.008541 + 0.008625 + 0.012541 + 0.069292 + 0.010791 + 0.020875 + 0.010833 + 0.008917 + 0.009542 + 0.008583 + 0.056208 + 0.012375 + 0.008791 + 0.008042 + 0.00775 + 0.007167 + 0.008333 + 0.011416 + 0.010625 + 0.012125 + 0.013583 + 0.013458 + 0.011125 + 0.01175 + 0.01175 + 0.009 + 0.010084 + 0.008375 + 0.011208 + 0.010042 + 0.008834 + 0.009417 + 0.009708 + 0.01 + 0.009417 + 0.007625 + 0.007917 + 0.0075 + 0.008375 + 0.008542 + 0.009125 + 0.008167 + 0.010584 + 0.009791 + 0.008958 + 0.009125 + 0.008458 + 0.0075 + 0.007375 + 0.007625 + 0.007583 + 0.008 + 0.0075 + 0.0085 + 0.007542 + 0.010625 + 0.00825 + 0.010167 + 0.009958 + 0.009958 + 0.009 + 0.007667 + 0.008458 + 0.017792 + 0.01 + 0.009875 + 0.00975 + 0.009042 + 0.008709 + 0.007709 + 0.009416 + 0.008083 + 0.007667 + 0.007834 + 0.006459 + 0.006584 + 0.006833 + 0.006208 + 0.006291 + 0.006 + 0.007166 + 0.007875 + 0.007042 + 0.005625 + 0.006333 + 0.00825 + 0.00725 + 0.00675 + 0.0065 + 0.006791 + 0.007167 + 0.007625 + 0.006959 + 0.007208 + 0.006958 + 0.00675 + 0.006833 + 0.006542 + 0.007125 + 0.00675 + 0.007375 + 0.007042 + 0.006708 + 0.006958 + 0.006666 + 0.006417 + 0.006792 + 0.007167 + 0.00725 + 0.007084 + 0.006959 + 0.006666 + 0.006708 + 0.006708 + 0.006584 + 0.006875 + 0.007333 + 0.007333 + 0.00675 + 0.007083 + 0.006958 + 0.007583 + 0.007084 + 0.007166 + 0.00725 + 0.007916 + 0.008833 + 0.007625 + 0.00925 + 0.0095 + 0.009625 + 0.009666 + 0.009375 + 0.00875 + 0.009417 + 0.0095 + 0.008167 + 0.007958 + 0.008125 + 0.00825 + 0.007375 + 0.009875 + 0.009167 + 0.009 + 0.008709 + 0.010042 + 0.009667 + 0.011625 + 0.009959 + 0.010166 + 0.00925 + 0.009541 + 0.008625 + 0.007583 + 0.008083 + 0.011083 + 0.010541 + 0.0105 + 0.01225 + 0.009459 + 0.009375 + 0.008 + 0.007125 + 0.007 + 0.008375 + 0.009625 + 0.009583 + 0.009125 + 0.008958 + 0.008166 + 0.00775 + 0.006209 + 0.006416 + 0.006625 + 0.006334 + 0.006125 + 0.006292 + 0.006334 + 0.005834 + 0.006583 + 0.006042 + 0.008208 + 0.006916 + 0.005125 + 0.005083 + 0.007 + 0.006625 + 0.006583 + 0.006791 + 0.006917 + 0.007083 + 0.00675 + 0.006709 + 0.006875 + 0.007125 + 0.006459 + 0.007 + 0.007417 + 0.007083 + 0.006791 + 0.006833 + 0.006667 + 0.006833 + 0.006667 + 0.006541 + 0.006583 + 0.006625 + 0.006792 + 0.006292 + 0.006708 + 0.006375 + 0.006791 + 0.007167 + 0.006958 + 0.006542 + 0.006459 + 0.006416 + 0.006709 + 0.006833 + 0.006666 + 0.007 + 0.007041 + 0.007125 + 0.007167 + 0.007084 + 0.006875 + 0.006667 + 0.006334 + 0.007125 + 0.009417 + 0.009333 + 0.0085 + 0.00775 + 0.006959 + 0.006916 + 0.007125 + 0.007458 + 0.008 + 0.008541 + 0.007708 + 0.007 + 0.007125 + 0.006541 + 0.007167 + 0.0075 + 0.007417 + 0.007041 + 0.006958 + 0.007292 + 0.012292 + 0.008666 + 0.008834 + 0.008375 + 0.00725 + 0.007583 + 0.008167 + 0.007792 + 0.008 + 0.007458 + 0.01 + 0.009542 + 0.009708 + 0.008166 + 0.0075 + 0.007916 + 0.007417 + 0.009625 + 0.009416 + 0.00925 + 0.00825 + 0.008084 + 0.006542 + 0.006125 + 0.005917 + 0.005375 + 0.006208 + 0.0065 + 0.006333 + 0.006125 + 0.006333 + 0.005917 + 0.008083 + 0.006125 + 0.0055 + 0.005083 + 0.006583 + 0.00675 + 0.006791 + 0.006458 + 0.00675 + 0.007042 + 0.006792 + 0.007458 + 0.007875 + 0.008125 + 0.007625 + 0.006541 + 0.006458 + 0.006958 + 0.00675 + 0.006583 + 0.006875 + 0.006584 + 0.00675 + 0.006125 + 0.006542 + 0.006292 + 0.006292 + 0.006625 + 0.006459 + 0.006459 + 0.006708 + 0.007083 + 0.006709 + 0.005959 + 0.006542 + 0.006708 + 0.006291 + 0.006166 + 0.007709 + 0.007875 + 0.006625 + 0.006917 + 0.007292 + 0.006958 + 0.006833 + 0.00725 + 0.007125 + 0.007875 + 0.008833 + 0.0085 + 0.007041 + 0.009667 + 0.009416 + 0.009292 + 0.009209 + 0.009667 + 0.009083 + 0.008708 + 0.0075 + 0.008125 + 0.009292 + 0.00925 + 0.008958 + 0.009125 + 0.008708 + 0.006458 + 0.007042 + 0.007375 + 0.008459 + 0.00825 + 0.006959 + 0.009709 + 0.01 + 0.009292 + 0.00875 + 0.01025 + 0.00825 + 0.007792 + 0.007708 + 0.007666 + 0.010083 + 0.010625 + 0.009416 + 0.010125 + 0.009875 + 0.008084 + 0.007834 + 0.0105 + 0.011959 + 0.011458 + 0.010208 + 0.007625 + 0.005708 + 0.006083 + 0.006042 + 0.006375 + 0.006167 + 0.005916 + 0.006167 + 0.006334 + 0.008042 + 0.005041 + 0.00525 + 0.004875 + 0.006958 + 0.006875 + 0.006542 + 0.00675 + 0.006625 + 0.006541 + 0.006333 + 0.006875 + 0.006541 + 0.007083 + 0.006208 + 0.007 + 0.006833 + 0.006583 + 0.006125 + 0.006625 + 0.0065 + 0.006541 + 0.006459 + 0.006541 + 0.0065 + 0.006333 + 0.006625 + 0.006209 + 0.006209 + 0.007167 + 0.006291 + 0.006583 + 0.006375 + 0.006333 + 0.006666 + 0.006375 + 0.006583 + 0.00675 + 0.00625 + 0.006583 + 0.006875 + 0.0075 + 0.006916 + 0.007167 + 0.008459 + 0.008791 + 0.008708 + 0.00875 + 0.009458 + 0.008208 + 0.006833 + 0.009583 + 0.009292 + 0.008709 + 0.008708 + 0.008708 + 0.009541 + 0.007375 + 0.006625 + 0.007 + 0.006792 + 0.009041 + 0.00725 + 0.00675 + 0.006584 + 0.006584 + 0.006875 + 0.00725 + 0.008708 + 0.008792 + 0.009 + 0.009542 + 0.008917 + 0.008166 + 0.006375 + 0.007875 + 0.008 + 0.007541 + 0.008208 + 0.010542 + 0.010209 + 0.009875 + 0.009 + 0.009458 + 0.008041 + 0.007416 + 0.010042 + 0.009875 + 0.0095 + 0.009959 + 0.010042 + 0.006958 + 0.00625 + 0.006167 + 0.006125 + 0.006083 + 0.005666 + 0.005834 + 0.00675 + 0.005875 + 0.006375 + 0.006541 + 0.006958 + 0.0045 + 0.004917 + 0.006917 + 0.007167 + 0.006625 + 0.006667 + 0.006083 + 0.006667 + 0.00675 + 0.006542 + 0.006291 + 0.007292 + 0.008125 + 0.008334 + 0.00825 + 0.008375 + 0.008291 + 0.007083 + 0.006542 + 0.006833 + 0.006667 + 0.006625 + 0.006584 + 0.006208 + 0.006625 + 0.006333 + 0.006458 + 0.00625 + 0.006667 + 0.006459 + 0.006166 + 0.006417 + 0.006625 + 0.006583 + 0.006584 + 0.006584 + 0.006584 + 0.006833 + 0.006875 + 0.008291 + 0.009125 + 0.009125 + 0.008542 + 0.009042 + 0.008625 + 0.009459 + 0.009334 + 0.008917 + 0.009291 + 0.010292 + 0.009541 + 0.009208 + 0.008834 + 0.009167 + 0.0085 + 0.007375 + 0.006709 + 0.006667 + 0.006625 + 0.006792 + 0.007416 + 0.006917 + 0.007375 + 0.006875 + 0.006708 + 0.006917 + 0.007 + 0.006667 + 0.007416 + 0.009 + 0.009666 + 0.008667 + 0.01025 + 0.010292 + 0.007917 + 0.007791 + 0.007792 + 0.010166 + 0.010083 + 0.01 + 0.009208 + 0.009875 + 0.007959 + 0.0075 + 0.009625 + 0.009666 + 0.010083 + 0.009542 + 0.007791 + 0.007042 + 0.005916 + 0.005959 + 0.006042 + 0.006041 + 0.006 + 0.005625 + 0.006209 + 0.006917 + 0.007541 + 0.005208 + 0.0055 + 0.004625 + 0.006958 + 0.006541 + 0.0065 + 0.006458 + 0.006541 + 0.006583 + 0.0065 + 0.006458 + 0.0065 + 0.006333 + 0.006375 + 0.007 + 0.006667 + 0.006667 + 0.006375 + 0.006958 + 0.006583 + 0.0065 + 0.0065 + 0.006833 + 0.006541 + 0.006417 + 0.006417 + 0.006375 + 0.006792 + 0.006625 + 0.006625 + 0.006583 + 0.0065 + 0.008 + 0.0085 + 0.021334 + 0.013417 + 0.008958 + 0.008958 + 0.007125 + 0.007083 + 0.007 + 0.007166 + 0.00725 + 0.007041 + 0.006625 + 0.007125 + 0.008125 + 0.007125 + 0.007417 + 0.007167 + 0.006792 + 0.006583 + 0.007125 + 0.01 + 0.009125 + 0.008375 + 0.008292 + 0.00825 + 0.007542 + 0.007209 + 0.007083 + 0.007417 + 0.007125 + 0.007958 + 0.007042 + 0.007292 + 0.006875 + 0.007083 + 0.007333 + 0.007458 + 0.007458 + 0.007125 + 0.007125 + 0.011125 + 0.009958 + 0.009542 + 0.008709 + 0.008167 + 0.007791 + 0.00925 + 0.01025 + 0.010333 + 0.008875 + 0.0085 + 0.007709 + 0.0085 + 0.009875 + 0.010833 + 0.010417 + 0.007292 + 0.007583 + 0.007167 + 0.00725 + 0.00825 + 0.007333 + 0.007083 + 0.007208 + 0.014417 + 0.006542 + 0.009417 + 0.005958 + 0.006375 + 0.006333 + 0.007542 + 0.008 + 0.008375 + 0.0085 + 0.008458 + 0.00775 + 0.007959 + 0.007375 + 0.006917 + 0.006875 + 0.006792 + 0.007125 + 0.008083 + 0.009208 + 0.008833 + 0.013125 + 0.010083 + 0.007542 + 0.006792 + 0.007708 + 0.007792 + 0.007041 + 0.007458 + 0.007333 + 0.007625 + 0.0085 + 0.009125 + 0.008792 + 0.008375 + 0.008792 + 0.0075 + 0.007291 + 0.007708 + 0.006917 + 0.007708 + 0.009167 + 0.008541 + 0.008875 + 0.00825 + 0.007916 + 0.008542 + 0.009083 + 0.008916 + 0.010042 + 0.009334 + 0.007459 + 0.009417 + 0.009333 + 0.008333 + 0.007917 + 0.008416 + 0.008167 + 0.007333 + 0.006958 + 0.008708 + 0.008583 + 0.009875 + 0.008083 + 0.006958 + 0.007375 + 0.007541 + 0.006208 + 0.008125 + 0.008417 + 0.007584 + 0.007084 + 0.007584 + 0.00725 + 0.007417 + 0.007375 + 0.007208 + 0.008125 + 0.008625 + 0.010958 + 0.011 + 0.0105 + 0.010333 + 0.009958 + 0.008417 + 0.007708 + 0.009541 + 0.009708 + 0.009667 + 0.008291 + 0.008208 + 0.008333 + 0.009625 + 0.007667 + 0.007417 + 0.007666 + 0.006292 + 0.006166 + 0.005792 + 0.006125 + 0.006083 + 0.006542 + 0.007667 + 0.005125 + 0.0055 + 0.005458 + 0.007541 + 0.006042 + 0.006416 + 0.006417 + 0.006792 + 0.006708 + 0.006708 + 0.006583 + 0.006833 + 0.00625 + 0.006208 + 0.007 + 0.007 + 0.00625 + 0.0065 + 0.006458 + 0.006541 + 0.006417 + 0.00725 + 0.006958 + 0.006458 + 0.006542 + 0.007 + 0.00725 + 0.006791 + 0.006542 + 0.006875 + 0.006459 + 0.006542 + 0.006792 + 0.006792 + 0.006958 + 0.008583 + 0.006584 + 0.006667 + 0.006166 + 0.006625 + 0.007333 + 0.006833 + 0.007083 + 0.007166 + 0.006875 + 0.006875 + 0.006792 + 0.007334 + 0.007167 + 0.006666 + 0.006834 + 0.011667 + 0.008416 + 0.009125 + 0.009042 + 0.009459 + 0.008792 + 0.008958 + 0.008041 + 0.0075 + 0.00725 + 0.007 + 0.009459 + 0.009167 + 0.010208 + 0.010125 + 0.009292 + 0.00875 + 0.007375 + 0.007417 + 0.007167 + 0.007375 + 0.007958 + 0.007875 + 0.010583 + 0.011 + 0.012 + 0.009666 + 0.009083 + 0.009083 + 0.008542 + 0.0075 + 0.009083 + 0.011417 + 0.011167 + 0.009541 + 0.009125 + 0.009208 + 0.011791 + 0.02375 + 0.013833 + 0.007 + 0.008042 + 0.007291 + 0.007584 + 0.007167 + 0.007292 + 0.007875 + 0.007583 + 0.008959 + 0.010625 + 0.01075 + 0.008 + 0.00725 + 0.010416 + 0.00925 + 0.0085 + 0.007375 + 0.008583 + 0.007542 + 0.007458 + 0.007166 + 0.007167 + 0.006458 + 0.007083 + 0.006917 + 0.006334 + 0.006958 + 0.006625 + 0.006833 + 0.006917 + 0.006667 + 0.0065 + 0.006791 + 0.007209 + 0.008667 + 0.009708 + 0.009042 + 0.008875 + 0.010167 + 0.009375 + 0.008333 + 0.007291 + 0.007208 + 0.0075 + 0.007375 + 0.007333 + 0.007958 + 0.008541 + 0.008833 + 0.008542 + 0.00775 + 0.008209 + 0.009583 + 0.009125 + 0.009375 + 0.010292 + 0.009334 + 0.009167 + 0.00775 + 0.007959 + 0.008 + 0.007583 + 0.007708 + 0.007625 + 0.008667 + 0.009125 + 0.009792 + 0.009334 + 0.009084 + 0.008 + 0.007292 + 0.007167 + 0.009292 + 0.009083 + 0.011375 + 0.008208 + 0.007541 + 0.007917 + 0.00775 + 0.007709 + 0.007375 + 0.007042 + 0.00725 + 0.009542 + 0.007792 + 0.008166 + 0.011625 + 0.010208 + 0.009958 + 0.010583 + 0.009875 + 0.007375 + 0.008208 + 0.0105 + 0.011333 + 0.010959 + 0.010417 + 0.010125 + 0.010334 + 0.009417 + 0.009416 + 0.007667 + 0.00775 + 0.005792 + 0.005875 + 0.006083 + 0.006208 + 0.006042 + 0.0065 + 0.008292 + 0.005125 + 0.005167 + 0.005 + 0.007667 + 0.007 + 0.00775 + 0.008375 + 0.010667 + 0.009209 + 0.008583 + 0.008333 + 0.007416 + 0.007875 + 0.007333 + 0.006833 + 0.006792 + 0.006917 + 0.006833 + 0.007667 + 0.00825 + 0.009875 + 0.010458 + 0.009 + 0.008208 + 0.008125 + 0.008833 + 0.009167 + 0.009875 + 0.008709 + 0.008541 + 0.008583 + 0.00825 + 0.007708 + 0.007791 + 0.008125 + 0.009083 + 0.009583 + 0.039333 + 0.010333 + 0.009416 + 0.009042 + 0.020208 + 0.009459 + 0.011792 + 0.007875 + 0.009 + 0.010917 + 0.010959 + 0.010375 + 0.011625 + 0.048875 + 0.014875 + 0.013084 + 0.014459 + 0.016084 + 0.011875 + 0.014417 + 0.010916 + 0.011333 + 0.010917 + 0.011625 + 0.012167 + 0.012334 + 0.011834 + 0.012917 + 0.008083 + 0.007667 + 0.010542 + 0.010542 + 0.009417 + 0.008333 + 0.009125 + 0.010917 + 0.014583 + 0.011041 + 0.011042 + 0.010209 + 0.012375 + 0.010708 + 0.009917 + 0.009958 + 0.010042 + 0.009917 + 0.009083 + 0.010458 + 0.010208 + 0.009417 + 0.008959 + 0.009209 + 0.008041 + 0.007083 + 0.006292 + 0.008 + 0.009375 + 0.00825 + 0.008375 + 0.007791 + 0.007958 + 0.007584 + 0.009417 + 0.005958 + 0.005167 + 0.006292 + 0.007125 + 0.006708 + 0.007417 + 0.008084 + 0.007792 + 0.007458 + 0.007708 + 0.007583 + 0.007334 + 0.007875 + 0.007083 + 0.00675 + 0.007166 + 0.007125 + 0.007166 + 0.007083 + 0.007 + 0.007 + 0.007458 + 0.009084 + 0.009667 + 0.010625 + 0.01025 + 0.009792 + 0.008917 + 0.009166 + 0.008083 + 0.007834 + 0.007625 + 0.007625 + 0.007208 + 0.007417 + 0.007125 + 0.007208 + 0.006958 + 0.006333 + 0.007042 + 0.007291 + 0.008542 + 0.007334 + 0.00775 + 0.007125 + 0.007458 + 0.007584 + 0.007292 + 0.007916 + 0.00775 + 0.010291 + 0.010208 + 0.0095 + 0.009625 + 0.010167 + 0.008709 + 0.008875 + 0.010083 + 0.010167 + 0.009542 + 0.0085 + 0.007125 + 0.007209 + 0.012875 + 0.007042 + 0.007875 + 0.00825 + 0.007834 + 0.007292 + 0.007333 + 0.007292 + 0.009583 + 0.010041 + 0.009167 + 0.010459 + 0.010084 + 0.010459 + 0.008584 + 0.009791 + 0.011334 + 0.010917 + 0.010125 + 0.01 + 0.010333 + 0.01 + 0.010583 + 0.016417 + 0.009209 + 0.009 + 0.008125 + 0.006542 + 0.006333 + 0.006292 + 0.006333 + 0.006083 + 0.006666 + 0.006291 + 0.006958 + 0.006375 + 0.008125 + 0.005958 + 0.006042 + 0.005792 + 0.007375 + 0.008 + 0.00725 + 0.007583 + 0.006917 + 0.006959 + 0.006791 + 0.006917 + 0.006333 + 0.007125 + 0.007167 + 0.006833 + 0.006875 + 0.006875 + 0.006541 + 0.006459 + 0.006833 + 0.006875 + 0.007084 + 0.007291 + 0.006792 + 0.006667 + 0.006875 + 0.007041 + 0.006917 + 0.006958 + 0.006958 + 0.007125 + 0.0065 + 0.006958 + 0.006917 + 0.007083 + 0.006792 + 0.007333 + 0.007167 + 0.007334 + 0.007125 + 0.007292 + 0.006875 + 0.007417 + 0.007375 + 0.007292 + 0.007666 + 0.007209 + 0.007333 + 0.007458 + 0.008 + 0.00725 + 0.007 + 0.007666 + 0.007583 + 0.009875 + 0.010167 + 0.009292 + 0.009375 + 0.010041 + 0.010125 + 0.010125 + 0.009417 + 0.009666 + 0.009125 + 0.007916 + 0.008209 + 0.006834 + 0.007125 + 0.009917 + 0.007666 + 0.007417 + 0.007084 + 0.007459 + 0.007 + 0.011 + 0.019083 + 0.014875 + 0.0115 + 0.010583 + 0.009917 + 0.010541 + 0.009375 + 0.009166 + 0.008667 + 0.008375 + 0.008458 + 0.009375 + 0.011125 + 0.010959 + 0.01025 + 0.007791 + 0.008208 + 0.006791 + 0.006333 + 0.006375 + 0.005625 + 0.006041 + 0.006458 + 0.006 + 0.007083 + 0.008042 + 0.008 + 0.005916 + 0.006542 + 0.006959 + 0.006834 + 0.006834 + 0.006458 + 0.006541 + 0.007375 + 0.006875 + 0.006708 + 0.006875 + 0.006459 + 0.006584 + 0.008292 + 0.006708 + 0.007333 + 0.011209 + 0.006208 + 0.006417 + 0.006791 + 0.0075 + 0.00775 + 0.007166 + 0.006708 + 0.007042 + 0.006708 + 0.006458 + 0.006917 + 0.007083 + 0.006958 + 0.006917 + 0.007958 + 0.008208 + 0.007791 + 0.007208 + 0.006542 + 0.007084 + 0.006459 + 0.007125 + 0.007458 + 0.007792 + 0.007042 + 0.006791 + 0.006792 + 0.007125 + 0.0075 + 0.007208 + 0.007083 + 0.007042 + 0.006958 + 0.007208 + 0.009125 + 0.008833 + 0.00925 + 0.008333 + 0.00875 + 0.007625 + 0.007208 + 0.007208 + 0.006833 + 0.007 + 0.007 + 0.007375 + 0.008666 + 0.009458 + 0.009125 + 0.008208 + 0.008541 + 0.01 + 0.009125 + 0.010208 + 0.010333 + 0.009542 + 0.010334 + 0.010209 + 0.010084 + 0.00975 + 0.009792 + 0.011667 + 0.010416 + 0.009333 + 0.010417 + 0.008042 + 0.007875 + 0.007625 + 0.010542 + 0.010167 + 0.010084 + 0.006625 + 0.006416 + 0.02125 + 0.010708 + 0.010458 + 0.006834 + 0.006791 + 0.006542 + 0.006792 + 0.007291 + 0.007417 + 0.005292 + 0.005542 + 0.005291 + 0.006708 + 0.006542 + 0.006625 + 0.006833 + 0.006875 + 0.006459 + 0.006333 + 0.006958 + 0.006542 + 0.007041 + 0.006708 + 0.0065 + 0.006292 + 0.006833 + 0.006708 + 0.006417 + 0.006708 + 0.006542 + 0.006708 + 0.006583 + 0.006875 + 0.006625 + 0.006917 + 0.006458 + 0.007083 + 0.006583 + 0.006875 + 0.006834 + 0.006125 + 0.006625 + 0.006958 + 0.00675 + 0.006708 + 0.006709 + 0.007167 + 0.006583 + 0.007 + 0.006708 + 0.007125 + 0.009291 + 0.009042 + 0.008916 + 0.009208 + 0.0095 + 0.00775 + 0.006708 + 0.008334 + 0.006666 + 0.007041 + 0.006917 + 0.009958 + 0.009708 + 0.00925 + 0.010041 + 0.009541 + 0.007333 + 0.007167 + 0.007084 + 0.007292 + 0.007291 + 0.0075 + 0.007458 + 0.007083], + :filter-inline-eight + [0.013208 + 0.00725 + 0.007042 + 0.007708 + 0.009375 + 0.008958 + 0.009583 + 0.01275 + 0.008458 + 0.008375 + 0.006875 + 0.006833 + 0.007083 + 0.007292 + 0.006917 + 0.007125 + 0.006542 + 0.007042 + 0.007 + 0.006959 + 0.006875 + 0.007208 + 0.007125 + 0.007167 + 0.006833 + 0.006916 + 0.006792 + 0.007208 + 0.006917 + 0.007209 + 0.00725 + 0.006833 + 0.007083 + 0.007 + 0.007125 + 0.00775 + 0.007542 + 0.007291 + 0.00725 + 0.007334 + 0.007209 + 0.007084 + 0.007333 + 0.007291 + 0.007667 + 0.007459 + 0.007292 + 0.007541 + 0.007125 + 0.007459 + 0.007584 + 0.007042 + 0.007417 + 0.00725 + 0.008 + 0.007792 + 0.007375 + 0.007041 + 0.007709 + 0.012709 + 0.009792 + 0.008917 + 0.008167 + 0.007416 + 0.007167 + 0.007292 + 0.007083 + 0.008417 + 0.008042 + 0.007334 + 0.007334 + 0.008875 + 0.008208 + 0.010458 + 0.0105 + 0.009958 + 0.008958 + 0.008667 + 0.007625 + 0.008125 + 0.010708 + 0.009917 + 0.01 + 0.008916 + 0.009167 + 0.008125 + 0.009625 + 0.008791 + 0.008083 + 0.0075 + 0.006833 + 0.006333 + 0.007167 + 0.00675 + 0.00725 + 0.007791 + 0.009167 + 0.0105 + 0.010416 + 0.006458 + 0.008083 + 0.009875 + 0.010792 + 0.0085 + 0.009416 + 0.008792 + 0.008459 + 0.008167 + 0.018667 + 0.017042 + 0.009625 + 0.00975 + 0.01025 + 0.011125 + 0.010792 + 0.011042 + 0.010458 + 0.007708 + 0.008125 + 0.00825 + 0.008333 + 0.008375 + 0.008417 + 0.008334 + 0.008084 + 0.008125 + 0.007917 + 0.008291 + 0.007792 + 0.015917 + 0.008541 + 0.008333 + 0.007875 + 0.00825 + 0.008667 + 0.009292 + 0.008167 + 0.00825 + 0.008917 + 0.00875 + 0.00875 + 0.0085 + 0.008292 + 0.008375 + 0.008875 + 0.0085 + 0.0085 + 0.008667 + 0.008542 + 0.008542 + 0.008208 + 0.008041 + 0.008291 + 0.007875 + 0.00875 + 0.008375 + 0.008291 + 0.008167 + 0.008375 + 0.008167 + 0.008584 + 0.008333 + 0.008875 + 0.012459 + 0.010875 + 0.010875 + 0.011625 + 0.011625 + 0.010791 + 0.00875 + 0.008875 + 0.009667 + 0.009291 + 0.009541 + 0.012417 + 0.011833 + 0.01175 + 0.012208 + 0.009791 + 0.008833 + 0.009208 + 0.012042 + 0.012292 + 0.012208 + 0.012459 + 0.009917 + 0.009666 + 0.00775 + 0.007375 + 0.007291 + 0.0175 + 0.008 + 0.00725 + 0.007333 + 0.007375 + 0.007042 + 0.008333 + 0.009167 + 0.006667 + 0.006916 + 0.006917 + 0.008084 + 0.007833 + 0.008375 + 0.009375 + 0.010875 + 0.011041 + 0.008792 + 0.008084 + 0.008792 + 0.009417 + 0.008583 + 0.009625 + 0.011667 + 0.00975 + 0.009666 + 0.011208 + 0.010583 + 0.009583 + 0.010625 + 0.009917 + 0.009709 + 0.010125 + 0.069459 + 0.052375 + 0.01 + 0.008708 + 0.008542 + 0.008458 + 0.0085 + 0.008375 + 0.008291 + 0.008625 + 0.008416 + 0.008334 + 0.0085 + 0.0085 + 0.009875 + 0.00875 + 0.00875 + 0.008625 + 0.018584 + 0.009042 + 0.009167 + 0.015458 + 0.018625 + 0.009625 + 0.009334 + 0.008666 + 0.008667 + 0.00875 + 0.008875 + 0.010458 + 0.009167 + 0.010334 + 0.009125 + 0.00875 + 0.00875 + 0.00875 + 0.008583 + 0.008708 + 0.009958 + 0.010084 + 0.008625 + 0.008917 + 0.008583 + 0.012291 + 0.01025 + 0.010667 + 0.013417 + 0.01225 + 0.017375 + 0.011917 + 0.010417 + 0.012792 + 0.01175 + 0.010375 + 0.010459 + 0.010583 + 0.010875 + 0.013584 + 0.014416 + 0.0135 + 0.012791 + 0.00975 + 0.010459 + 0.011833 + 0.012084 + 0.009792 + 0.008958 + 0.0085 + 0.00825 + 0.007791 + 0.008958 + 0.008584 + 0.007917 + 0.008 + 0.010833 + 0.007958 + 0.007375 + 0.007584 + 0.009542 + 0.009291 + 0.009375 + 0.008958 + 0.008708 + 0.009084 + 0.008542 + 0.008542 + 0.008583 + 0.00875 + 0.008833 + 0.009834 + 0.00925 + 0.009042 + 0.03125 + 0.027458 + 0.011375 + 0.010125 + 0.009834 + 0.009334 + 0.009209 + 0.010917 + 0.010583 + 0.009375 + 0.009209 + 0.009625 + 0.009959 + 0.008833 + 0.009 + 0.008959 + 0.008708 + 0.008791 + 0.009083 + 0.010125 + 0.010416 + 0.010208 + 0.010375 + 0.011083 + 0.011625 + 0.010833 + 0.011208 + 0.009917 + 0.010542 + 0.024167 + 0.012333 + 0.01225 + 0.011334 + 0.058834 + 0.018958 + 0.011583 + 0.010416 + 0.011125 + 0.010666 + 0.01025 + 0.011375 + 0.012333 + 0.012208 + 0.010667 + 0.010625 + 0.011125 + 0.011333 + 0.010917 + 0.0145 + 0.011584 + 0.0105 + 0.010667 + 0.011167 + 0.012041 + 0.010708 + 0.012125 + 0.011416 + 0.013208 + 0.010792 + 0.0105 + 0.014042 + 0.010875 + 0.011709 + 0.011417 + 0.011583 + 0.012542 + 0.0125 + 0.012583 + 0.014291 + 0.011375 + 0.010917 + 0.010792 + 0.010208 + 0.00925 + 0.012666 + 0.01075 + 0.011042 + 0.011625 + 0.011333 + 0.011875 + 0.012792 + 0.009917 + 0.027708 + 0.014208 + 0.013916 + 0.009791 + 0.009458 + 0.012292 + 0.010209 + 0.009959 + 0.009375 + 0.0095 + 0.009667 + 0.009833 + 0.009166 + 0.009291 + 0.009416 + 0.009458 + 0.009708 + 0.010333 + 0.009833 + 0.011042 + 0.010792 + 0.009875 + 0.010042 + 0.031125 + 0.013833 + 0.009417 + 0.008875 + 0.008583 + 0.009041 + 0.008542 + 0.008916 + 0.00925 + 0.009792 + 0.011 + 0.010666 + 0.009416 + 0.009416 + 0.00925 + 0.008833 + 0.008542 + 0.008959 + 0.009417 + 0.008833 + 0.009 + 0.009625 + 0.011417 + 0.0115 + 0.009375 + 0.010709 + 0.010459 + 0.009625 + 0.01025 + 0.009292 + 0.009375 + 0.012291 + 0.011625 + 0.011667 + 0.011333 + 0.010666 + 0.008875 + 0.00875 + 0.009916 + 0.00875 + 0.008625 + 0.008709 + 0.00875 + 0.009 + 0.009333 + 0.009041 + 0.009125 + 0.009042 + 0.008959 + 0.008709 + 0.009292 + 0.008708 + 0.013166 + 0.012958 + 0.012583 + 0.012667 + 0.010834 + 0.009209 + 0.012125 + 0.011709 + 0.010708 + 0.01275 + 0.010416 + 0.010083 + 0.013625 + 0.012542 + 0.012167 + 0.010542 + 0.008667 + 0.008375 + 0.008333 + 0.009 + 0.008666 + 0.008667 + 0.0095 + 0.011 + 0.009084 + 0.010625 + 0.010208 + 0.009333 + 0.008041 + 0.006916 + 0.009167 + 0.00875 + 0.00875 + 0.009458 + 0.010375 + 0.009375 + 0.009042 + 0.009917 + 0.009958 + 0.009916 + 0.011958 + 0.011209 + 0.009542 + 0.009583 + 0.010458 + 0.01075 + 0.01025 + 0.010875 + 0.011459 + 0.011667 + 0.011334 + 0.011083 + 0.010666 + 0.010292 + 0.010833 + 0.010792 + 0.010667 + 0.011 + 0.010334 + 0.009125 + 0.008333 + 0.008041 + 0.008375 + 0.0085 + 0.008625 + 0.009208 + 0.010709 + 0.009458 + 0.009125 + 0.008917 + 0.008166 + 0.008417 + 0.010208 + 0.010709 + 0.009667 + 0.009 + 0.0085 + 0.0085 + 0.008292 + 0.008792 + 0.008875 + 0.008916 + 0.008708 + 0.009625 + 0.008708 + 0.008459 + 0.0085 + 0.00875 + 0.012166 + 0.012084 + 0.011583 + 0.012041 + 0.009167 + 0.008542 + 0.008458 + 0.008916 + 0.008792 + 0.009042 + 0.008958 + 0.008792 + 0.00975 + 0.008959 + 0.014792 + 0.012083 + 0.01225 + 0.011917 + 0.010125 + 0.009667 + 0.009083 + 0.012125 + 0.01225 + 0.012083 + 0.0125 + 0.010125 + 0.010042 + 0.011583 + 0.009917 + 0.010084 + 0.009542 + 0.009292 + 0.0085 + 0.008125 + 0.009084 + 0.009875 + 0.010333 + 0.010042 + 0.011 + 0.007708 + 0.007125 + 0.006417 + 0.008583 + 0.010583 + 0.0085 + 0.00775 + 0.008333 + 0.008417 + 0.008375 + 0.0085 + 0.008334 + 0.00875 + 0.00825 + 0.008042 + 0.007834 + 0.008125 + 0.007667 + 0.008416 + 0.00825 + 0.0085 + 0.0085 + 0.008 + 0.008209 + 0.008083 + 0.007666 + 0.008625 + 0.008334 + 0.008625 + 0.007667 + 0.00825 + 0.008083 + 0.008125 + 0.007875 + 0.008625 + 0.008375 + 0.008541 + 0.008375 + 0.010416 + 0.011583 + 0.009334 + 0.009125 + 0.008916 + 0.009291 + 0.009625 + 0.009375 + 0.008625 + 0.009083 + 0.009041 + 0.008458 + 0.008708 + 0.008708 + 0.008291 + 0.010291 + 0.0115 + 0.011167 + 0.011375 + 0.009333 + 0.008625 + 0.00875 + 0.008875 + 0.008333 + 0.00825 + 0.008875 + 0.008625 + 0.011167 + 0.011208 + 0.011083 + 0.011125 + 0.009416 + 0.008542 + 0.008709 + 0.008792 + 0.007916 + 0.009709 + 0.009542 + 0.009458 + 0.011542 + 0.012583 + 0.012291 + 0.01275 + 0.008709 + 0.010625 + 0.009709 + 0.012 + 0.012291 + 0.011916 + 0.01225 + 0.009333 + 0.009125 + 0.009917 + 0.0075 + 0.007458 + 0.007916 + 0.007583 + 0.007917 + 0.007875 + 0.017667 + 0.008834 + 0.010709 + 0.00975 + 0.010292 + 0.007542 + 0.009125 + 0.008125 + 0.00925 + 0.00875 + 0.010167 + 0.010375 + 0.0095 + 0.009125 + 0.009709 + 0.010792 + 0.010875 + 0.010041 + 0.010333 + 0.011417 + 0.011459 + 0.009667 + 0.009041 + 0.008417 + 0.015584 + 0.018292 + 0.009167 + 0.00925 + 0.008 + 0.00875 + 0.008375 + 0.008042 + 0.007541 + 0.008292 + 0.008625 + 0.010125 + 0.009333 + 0.008125 + 0.008709 + 0.008709 + 0.008042 + 0.00825 + 0.009334 + 0.009 + 0.009375 + 0.009375 + 0.008084 + 0.009959 + 0.010084 + 0.007667 + 0.009 + 0.009459 + 0.011834 + 0.011084 + 0.011375 + 0.010667 + 0.011 + 0.008542 + 0.008292 + 0.008125 + 0.008 + 0.00825 + 0.008625 + 0.008042 + 0.008208 + 0.008083 + 0.008 + 0.008 + 0.01325 + 0.010208 + 0.010291 + 0.00875 + 0.019042 + 0.009041 + 0.009708 + 0.009875 + 0.008625 + 0.0095 + 0.00925 + 0.009541 + 0.012042 + 0.012125 + 0.012292 + 0.01175 + 0.008792 + 0.009333 + 0.009208 + 0.011792 + 0.012167 + 0.0125 + 0.012416 + 0.011875 + 0.011708 + 0.009917 + 0.009875 + 0.008 + 0.007083 + 0.007583 + 0.008875 + 0.009834 + 0.010042 + 0.008 + 0.009833 + 0.010292 + 0.01075 + 0.008584 + 0.008292 + 0.010667 + 0.011292 + 0.010625 + 0.008542 + 0.008959 + 0.009875 + 0.009959 + 0.007917 + 0.008084 + 0.008167 + 0.009208 + 0.008833 + 0.007958 + 0.007916 + 0.00725 + 0.007791 + 0.007958 + 0.007625 + 0.007625 + 0.008167 + 0.007917 + 0.007792 + 0.007834 + 0.008125 + 0.008125 + 0.007625 + 0.008208 + 0.007917 + 0.007833 + 0.007292 + 0.007875 + 0.008125 + 0.008042 + 0.0075 + 0.007542 + 0.007917 + 0.008416 + 0.008 + 0.007958 + 0.008291 + 0.007958 + 0.008542 + 0.008042 + 0.008541 + 0.011125 + 0.010375 + 0.010041 + 0.00875 + 0.008834 + 0.008334 + 0.008292 + 0.008084 + 0.008667 + 0.008625 + 0.008416 + 0.008083 + 0.008625 + 0.008083 + 0.008333 + 0.008375 + 0.00875 + 0.0085 + 0.008625 + 0.009042 + 0.014583 + 0.010833 + 0.010625 + 0.008625 + 0.00875 + 0.010584 + 0.009792 + 0.009583 + 0.009917 + 0.009542 + 0.010916 + 0.010833 + 0.011125 + 0.008333 + 0.008375 + 0.009042 + 0.008791 + 0.017667 + 0.017042 + 0.010208 + 0.011 + 0.022291 + 0.010125 + 0.009792 + 0.008792 + 0.007334 + 0.007291 + 0.007042 + 0.007334 + 0.007083 + 0.007 + 0.008167 + 0.009167 + 0.007875 + 0.006458 + 0.005917 + 0.015209 + 0.007875 + 0.007542 + 0.007792 + 0.008333 + 0.00775 + 0.007833 + 0.007834 + 0.008084 + 0.007667 + 0.007792 + 0.007958 + 0.008625 + 0.008042 + 0.007917 + 0.008 + 0.0075 + 0.008 + 0.00925 + 0.008542 + 0.007792 + 0.008083 + 0.007958 + 0.008125 + 0.009 + 0.009167 + 0.010709 + 0.010666 + 0.00925 + 0.008584 + 0.008292 + 0.008666 + 0.01025 + 0.008 + 0.008042 + 0.008041 + 0.008041 + 0.007917 + 0.00825 + 0.008125 + 0.007917 + 0.008 + 0.007542 + 0.009292 + 0.008375 + 0.008375 + 0.007875 + 0.012791 + 0.01 + 0.009875 + 0.008875 + 0.008125 + 0.008375 + 0.0085 + 0.008375 + 0.008583 + 0.008458 + 0.007792 + 0.007792 + 0.007958 + 0.008083 + 0.008209 + 0.007792 + 0.0085 + 0.008417 + 0.008167 + 0.008417 + 0.010167 + 0.009833 + 0.009875 + 0.00975 + 0.008875 + 0.009084 + 0.008833 + 0.008833 + 0.011041 + 0.010958 + 0.010584 + 0.008542 + 0.008875 + 0.012834 + 0.008875 + 0.011167 + 0.010917 + 0.010458 + 0.009667 + 0.007166 + 0.007292 + 0.006792 + 0.00675 + 0.007166 + 0.007542 + 0.007042 + 0.007167 + 0.007 + 0.007333 + 0.008167 + 0.006375 + 0.006542 + 0.005959 + 0.007917 + 0.007959 + 0.007625 + 0.007916 + 0.008292 + 0.00775 + 0.007375 + 0.007833 + 0.00775 + 0.007583 + 0.007333 + 0.007792 + 0.007416 + 0.00775 + 0.007459 + 0.007667 + 0.007917 + 0.007625 + 0.007833 + 0.007625 + 0.007834 + 0.007334 + 0.007458 + 0.0075 + 0.007583 + 0.007625 + 0.008042 + 0.007917 + 0.0075 + 0.007583 + 0.007375 + 0.007709 + 0.00875 + 0.009167 + 0.007916 + 0.007833 + 0.007667 + 0.007958 + 0.009167 + 0.010542 + 0.010667 + 0.01075 + 0.010666 + 0.010291 + 0.010125 + 0.009584 + 0.008917 + 0.009917 + 0.0095 + 0.009541 + 0.014459 + 0.01025 + 0.009584 + 0.009042 + 0.009 + 0.009125 + 0.008833 + 0.008875 + 0.008791 + 0.008917 + 0.008542 + 0.008042 + 0.008041 + 0.008583 + 0.008417 + 0.00825 + 0.008167 + 0.008458 + 0.008417 + 0.008292 + 0.009041 + 0.013667 + 0.012334 + 0.009792 + 0.009625 + 0.009959 + 0.009958 + 0.011791 + 0.010958 + 0.01175 + 0.011583 + 0.01175 + 0.011333 + 0.01175 + 0.01425 + 0.011542 + 0.010625 + 0.00825 + 0.00875 + 0.008333 + 0.00825 + 0.010708 + 0.010917 + 0.010791 + 0.009416 + 0.009333 + 0.011709 + 0.008292 + 0.00925 + 0.0075 + 0.01025 + 0.009375 + 0.010458 + 0.009583 + 0.00975 + 0.009291 + 0.009583 + 0.009667 + 0.010459 + 0.011417 + 0.010042 + 0.009042 + 0.010167 + 0.009834 + 0.010584 + 0.010959 + 0.010333 + 0.01 + 0.009208 + 0.011041 + 0.010125 + 0.010625 + 0.009542 + 0.009708 + 0.010875 + 0.011625 + 0.04425 + 0.011042 + 0.01 + 0.009667 + 0.009375 + 0.009333 + 0.00925 + 0.009166 + 0.009708 + 0.01025 + 0.009458 + 0.010792 + 0.009541 + 0.008708 + 0.008334 + 0.00775 + 0.008042 + 0.008208 + 0.0105 + 0.008709 + 0.008625 + 0.009 + 0.008625 + 0.008292 + 0.012584 + 0.011375 + 0.011875 + 0.011083 + 0.00925 + 0.008542 + 0.00825 + 0.01275 + 0.008667 + 0.008375 + 0.008334 + 0.008417 + 0.00925 + 0.011375 + 0.01075 + 0.009209 + 0.010083 + 0.011375 + 0.010125 + 0.010334 + 0.009458 + 0.011 + 0.009333 + 0.009333 + 0.009166 + 0.009375 + 0.012625 + 0.012416 + 0.012042 + 0.01225 + 0.009667 + 0.009584 + 0.010042 + 0.011875 + 0.012375 + 0.010792 + 0.011792 + 0.010166 + 0.01 + 0.008542 + 0.008291 + 0.007916 + 0.007333 + 0.007333 + 0.007458 + 0.007166 + 0.010375 + 0.011125 + 0.012792 + 0.008583 + 0.008958 + 0.011542 + 0.009791 + 0.00925 + 0.008709 + 0.007959 + 0.008708 + 0.008375 + 0.009041 + 0.011209 + 0.010166 + 0.010416 + 0.010292 + 0.01025 + 0.01125 + 0.010042 + 0.00975 + 0.010334 + 0.009583 + 0.010583 + 0.011166 + 0.011666 + 0.011958 + 0.011 + 0.008542 + 0.008709 + 0.008917 + 0.009791 + 0.009125 + 0.008542 + 0.010167 + 0.021583 + 0.010833 + 0.010209 + 0.010333 + 0.01025 + 0.010625 + 0.011084 + 0.010458 + 0.009083 + 0.010958 + 0.01075 + 0.010666 + 0.0105 + 0.009292 + 0.008417 + 0.008458 + 0.011375 + 0.019333 + 0.0125 + 0.01125 + 0.012375 + 0.0145 + 0.012833 + 0.012958 + 0.010375 + 0.009542 + 0.009875 + 0.009667 + 0.010291 + 0.010625 + 0.01025 + 0.009334 + 0.009 + 0.008875 + 0.008333 + 0.008208 + 0.008666 + 0.008833 + 0.010625 + 0.009125 + 0.014083 + 0.012625 + 0.012584 + 0.017167 + 0.008917 + 0.009209 + 0.012458 + 0.011083 + 0.010083 + 0.009458 + 0.009291 + 0.009167 + 0.008667 + 0.012 + 0.011208 + 0.011083 + 0.007791 + 0.007417 + 0.007667 + 0.007541 + 0.007583 + 0.007333 + 0.007709 + 0.007375 + 0.007583 + 0.007958 + 0.009042 + 0.007 + 0.006416 + 0.005875 + 0.008333 + 0.008291 + 0.008416 + 0.008208 + 0.008083 + 0.008042 + 0.008333 + 0.007625 + 0.008541 + 0.008208 + 0.008458 + 0.008666 + 0.008667 + 0.008417 + 0.00775 + 0.008083 + 0.007958 + 0.008375 + 0.008708 + 0.008416 + 0.008833 + 0.008208 + 0.007916 + 0.007917 + 0.00775 + 0.008042 + 0.008625 + 0.008333 + 0.007916 + 0.008125 + 0.007708 + 0.007792 + 0.008125 + 0.00825 + 0.008041 + 0.008375 + 0.008459 + 0.0085 + 0.00825 + 0.008417 + 0.008625 + 0.008292 + 0.008667 + 0.009042 + 0.009208 + 0.008583 + 0.0085 + 0.00825 + 0.00825 + 0.008458 + 0.008667 + 0.008708 + 0.008916 + 0.008333 + 0.008208 + 0.013542 + 0.010625 + 0.010041 + 0.008375 + 0.008584 + 0.008958 + 0.008459 + 0.009375 + 0.010209 + 0.008625 + 0.009333 + 0.009 + 0.008667 + 0.009667 + 0.010417 + 0.010875 + 0.013167 + 0.01225 + 0.012209 + 0.011791 + 0.01125 + 0.008792 + 0.012042 + 0.010291 + 0.011125 + 0.00875 + 0.009375 + 0.0095 + 0.009916 + 0.011208 + 0.011458 + 0.009292 + 0.008875 + 0.008042 + 0.00775 + 0.007875 + 0.007584 + 0.007709 + 0.007375 + 0.007792 + 0.008041 + 0.011791 + 0.008667 + 0.008375 + 0.008583 + 0.011 + 0.009166 + 0.008125 + 0.008625 + 0.010625 + 0.010708 + 0.011292 + 0.010417 + 0.008917 + 0.007917 + 0.00775 + 0.008 + 0.008125 + 0.008458 + 0.010083 + 0.008833 + 0.008291 + 0.007958 + 0.007917 + 0.008 + 0.008666 + 0.00875 + 0.010416 + 0.00825 + 0.008834 + 0.008 + 0.008458 + 0.008334 + 0.007542 + 0.008167 + 0.008125 + 0.00825 + 0.008375 + 0.008125 + 0.008333 + 0.008083 + 0.007584 + 0.008625 + 0.008417 + 0.008416 + 0.008375 + 0.008292 + 0.008833 + 0.008917 + 0.008875 + 0.008166 + 0.0085 + 0.008708 + 0.008541 + 0.008625 + 0.008417 + 0.00925 + 0.008625 + 0.007542 + 0.008666 + 0.009083 + 0.008666 + 0.008417 + 0.008417 + 0.011542 + 0.011959 + 0.01025 + 0.011292 + 0.009834 + 0.008208 + 0.008875 + 0.009209 + 0.008667 + 0.008583 + 0.012792 + 0.008792 + 0.010166 + 0.009166 + 0.009459 + 0.018166 + 0.012541 + 0.011667 + 0.011959 + 0.008833 + 0.011791 + 0.012042 + 0.013083 + 0.012208 + 0.009167 + 0.008667 + 0.009584 + 0.009167 + 0.007542 + 0.007833 + 0.008291 + 0.007292 + 0.00825 + 0.007417 + 0.007334 + 0.007583 + 0.007666 + 0.009625 + 0.006375 + 0.006791 + 0.006833 + 0.008917 + 0.008125 + 0.007958 + 0.008291 + 0.008042 + 0.008084 + 0.008083 + 0.007833 + 0.008166 + 0.0075 + 0.007667 + 0.007875 + 0.00775 + 0.007416 + 0.008417 + 0.008084 + 0.008209 + 0.00775 + 0.008167 + 0.00825 + 0.00775 + 0.00775 + 0.008167 + 0.00825 + 0.008458 + 0.007625 + 0.007875 + 0.0075 + 0.007542 + 0.007791 + 0.007667 + 0.008042 + 0.01225 + 0.011 + 0.010292 + 0.01025 + 0.01025 + 0.008625 + 0.0095 + 0.010792 + 0.010709 + 0.010833 + 0.010375 + 0.008 + 0.008375 + 0.008541 + 0.008083 + 0.008625 + 0.007916 + 0.007916 + 0.008083 + 0.008459 + 0.00825 + 0.008041 + 0.008333 + 0.008916 + 0.008208 + 0.008583 + 0.007834 + 0.00775 + 0.008208 + 0.008583 + 0.0085 + 0.01125 + 0.011417 + 0.015166 + 0.010833 + 0.008833 + 0.008375 + 0.00875 + 0.00875 + 0.009042 + 0.008667 + 0.009125 + 0.014791 + 0.011916 + 0.012 + 0.011625 + 0.008834 + 0.008959 + 0.00925 + 0.011625 + 0.012 + 0.011708 + 0.011959 + 0.009584 + 0.008542 + 0.007333 + 0.007291 + 0.006916 + 0.007208 + 0.007583 + 0.007333 + 0.007292 + 0.0075 + 0.007208 + 0.009333 + 0.00925 + 0.00875 + 0.006375 + 0.005958 + 0.007792 + 0.00775 + 0.007625 + 0.007125 + 0.008417 + 0.007875 + 0.007708 + 0.008 + 0.008083 + 0.007792 + 0.008208 + 0.007791 + 0.007666 + 0.007541 + 0.007666 + 0.007875 + 0.008375 + 0.007458 + 0.007625 + 0.00825 + 0.008541 + 0.008 + 0.0085 + 0.008167 + 0.007792 + 0.008209 + 0.016667 + 0.008333 + 0.008167 + 0.008083 + 0.007833 + 0.007708 + 0.007625 + 0.008375 + 0.015417 + 0.009625 + 0.008958 + 0.008208 + 0.009417 + 0.008667 + 0.008209 + 0.009584 + 0.009334 + 0.01175 + 0.01175 + 0.01175 + 0.011458 + 0.009791 + 0.009208 + 0.009291 + 0.011583 + 0.011 + 0.012 + 0.012167 + 0.011958 + 0.011333 + 0.008417 + 0.008917 + 0.0085 + 0.009208 + 0.008416 + 0.008792 + 0.009167 + 0.013 + 0.010208 + 0.010666 + 0.0085 + 0.008333 + 0.007834 + 0.020959 + 0.012334 + 0.011333 + 0.009583 + 0.012417 + 0.012416 + 0.012541 + 0.012625 + 0.010167 + 0.008458 + 0.009042 + 0.010917 + 0.011125 + 0.011042 + 0.009 + 0.00925 + 0.009625 + 0.007542 + 0.007292 + 0.00775 + 0.00725 + 0.007916 + 0.00875 + 0.007583 + 0.007542 + 0.007541 + 0.007625 + 0.009416 + 0.006417 + 0.00675 + 0.006041 + 0.008625 + 0.007959 + 0.008458 + 0.008042 + 0.007583 + 0.007958 + 0.008 + 0.007875 + 0.007583 + 0.007833 + 0.008 + 0.0085 + 0.007416 + 0.007875 + 0.007792 + 0.007834 + 0.007709 + 0.009209 + 0.010667 + 0.008208 + 0.007917 + 0.007875 + 0.008333 + 0.008625 + 0.008875 + 0.0095 + 0.010375 + 0.010875 + 0.010542 + 0.010083 + 0.008208 + 0.00825 + 0.00775 + 0.008125 + 0.007625 + 0.008417 + 0.009417 + 0.0105 + 0.011042 + 0.010542 + 0.009667 + 0.008833 + 0.00875 + 0.008459 + 0.011625 + 0.010667 + 0.009959 + 0.008209 + 0.008459 + 0.008375 + 0.008375 + 0.008292 + 0.008292 + 0.008583 + 0.008125 + 0.008375 + 0.008208 + 0.008375 + 0.008625 + 0.008958 + 0.015542 + 0.009041 + 0.008042 + 0.008 + 0.010625 + 0.010208 + 0.010167 + 0.008541 + 0.008291 + 0.00825 + 0.009083 + 0.008959 + 0.008792 + 0.008625 + 0.011875 + 0.011 + 0.010542 + 0.009209 + 0.008667 + 0.00875 + 0.008625 + 0.011042 + 0.01075 + 0.010375 + 0.009208 + 0.009666 + 0.00725 + 0.006875 + 0.007291 + 0.0075 + 0.008125 + 0.007167 + 0.00775 + 0.007458 + 0.007375 + 0.007 + 0.009833 + 0.00675 + 0.006542 + 0.006709 + 0.008209 + 0.007584 + 0.008084 + 0.0075 + 0.0075 + 0.007791 + 0.007666 + 0.008125 + 0.00825 + 0.007792 + 0.00775 + 0.008041 + 0.007667 + 0.008417 + 0.007666 + 0.008458 + 0.008208 + 0.007625 + 0.008917 + 0.009167 + 0.01 + 0.009375 + 0.009166 + 0.008333 + 0.008042 + 0.008792 + 0.008542 + 0.008416 + 0.009958 + 0.008917 + 0.007792 + 0.008083 + 0.014458 + 0.007917 + 0.007959 + 0.007417 + 0.008125 + 0.009625 + 0.01075 + 0.010958 + 0.010958 + 0.010875 + 0.010167 + 0.008375 + 0.007792 + 0.00875 + 0.0085 + 0.01325 + 0.009917 + 0.009833 + 0.009 + 0.008875 + 0.008083 + 0.007792 + 0.008834 + 0.019917 + 0.008375 + 0.008125 + 0.009167 + 0.010209 + 0.007542 + 0.008292 + 0.008 + 0.008291 + 0.008417 + 0.010625 + 0.011208 + 0.01175 + 0.012541 + 0.010791 + 0.011208 + 0.011375 + 0.009541 + 0.009792 + 0.009292 + 0.009208 + 0.011875 + 0.012666 + 0.010458 + 0.009917 + 0.009875 + 0.01125 + 0.012209 + 0.013417 + 0.011917 + 0.011625 + 0.010375 + 0.007375 + 0.007625 + 0.006958 + 0.007166 + 0.007417 + 0.007084 + 0.007292 + 0.007542 + 0.007625 + 0.009459 + 0.00625 + 0.006583 + 0.006458 + 0.008208 + 0.008458 + 0.007917 + 0.007791 + 0.0075 + 0.007833 + 0.00775 + 0.007667 + 0.00775 + 0.007708 + 0.00775 + 0.007709 + 0.00775 + 0.00775 + 0.007709 + 0.00725 + 0.007333 + 0.007375 + 0.007708 + 0.008375 + 0.009333 + 0.00875 + 0.008167 + 0.007875 + 0.007791 + 0.007875 + 0.008208 + 0.010125 + 0.010584 + 0.009458 + 0.009083 + 0.008708 + 0.007541 + 0.007792 + 0.009083 + 0.010458 + 0.010334 + 0.011083 + 0.010917 + 0.008459 + 0.008667 + 0.008292 + 0.00825 + 0.008084 + 0.008459 + 0.008917 + 0.008417 + 0.008583 + 0.008292 + 0.008 + 0.008416 + 0.010875 + 0.011375 + 0.011 + 0.011041 + 0.009375 + 0.008125 + 0.010583 + 0.012208 + 0.010792 + 0.008708 + 0.00925 + 0.009375 + 0.009542 + 0.008584 + 0.008834 + 0.008375 + 0.008125 + 0.007959 + 0.008584 + 0.011042 + 0.015125 + 0.012458 + 0.011208 + 0.010167 + 0.010292 + 0.010083 + 0.01375 + 0.012667 + 0.011375 + 0.011458 + 0.010708 + 0.009375 + 0.008917 + 0.012291 + 0.012125 + 0.011792 + 0.009625 + 0.008667 + 0.007875 + 0.007084 + 0.007708 + 0.007125 + 0.007584 + 0.007625 + 0.012875 + 0.008875 + 0.008166 + 0.008625 + 0.005541 + 0.005208 + 0.006917 + 0.00725 + 0.006708 + 0.0075 + 0.007083 + 0.007167 + 0.006958 + 0.00675 + 0.00675 + 0.006875 + 0.006792 + 0.017916 + 0.007917 + 0.007792 + 0.006916 + 0.006708 + 0.006375 + 0.00725 + 0.007125 + 0.007292 + 0.006917 + 0.006667 + 0.007166 + 0.006959 + 0.007 + 0.006959 + 0.007667 + 0.007541 + 0.006667 + 0.006917 + 0.006792 + 0.006666 + 0.006708 + 0.007 + 0.006875 + 0.006708 + 0.007958 + 0.006916 + 0.007208 + 0.007125 + 0.007375 + 0.00725 + 0.007209 + 0.008542 + 0.008041 + 0.007667 + 0.006875 + 0.007125 + 0.007292 + 0.007208 + 0.007167 + 0.007083 + 0.007083 + 0.012583 + 0.010583 + 0.0075 + 0.007417 + 0.007334 + 0.00775 + 0.007042 + 0.00725 + 0.007416 + 0.007208 + 0.007125 + 0.007041 + 0.0075 + 0.007084 + 0.007292 + 0.007375 + 0.0075 + 0.008375 + 0.010916 + 0.009458 + 0.008334 + 0.008334 + 0.008167 + 0.007291 + 0.014667 + 0.008041 + 0.008583 + 0.007959 + 0.007959 + 0.007625 + 0.007958 + 0.011458 + 0.008417 + 0.006167 + 0.0065 + 0.006333 + 0.007083 + 0.008834 + 0.007 + 0.007166 + 0.007125 + 0.006792 + 0.00825 + 0.007958 + 0.006333 + 0.00625 + 0.006167 + 0.007791 + 0.00775 + 0.007542 + 0.007417 + 0.007791 + 0.007417 + 0.007791 + 0.00775 + 0.007542 + 0.007584 + 0.007334 + 0.007292 + 0.007291 + 0.007875 + 0.007375 + 0.008125 + 0.007583 + 0.007666 + 0.008292 + 0.008041 + 0.007958 + 0.008167 + 0.007333 + 0.007542 + 0.007459 + 0.007584 + 0.007459 + 0.008209 + 0.007709 + 0.007625 + 0.007833 + 0.008083 + 0.007875 + 0.007917 + 0.007791 + 0.00775 + 0.008667 + 0.007917 + 0.008583 + 0.007916 + 0.008542 + 0.007583 + 0.008208 + 0.011625 + 0.008375 + 0.008584 + 0.008416 + 0.007917 + 0.008625 + 0.008791 + 0.008333 + 0.007917 + 0.007834 + 0.008834 + 0.008125 + 0.011333 + 0.008208 + 0.008375 + 0.008042 + 0.007792 + 0.008041 + 0.008708 + 0.007917 + 0.007917 + 0.008 + 0.008041 + 0.00825 + 0.007917 + 0.008333 + 0.008125 + 0.009791 + 0.011792 + 0.009292 + 0.008208 + 0.008958 + 0.008667 + 0.008208 + 0.011333 + 0.008375 + 0.009 + 0.008917 + 0.008625 + 0.0085 + 0.00875 + 0.01175 + 0.009 + 0.007084 + 0.007084 + 0.007041 + 0.006708 + 0.006792 + 0.00675 + 0.007042 + 0.007375 + 0.006958 + 0.006583 + 0.008333 + 0.006417 + 0.006542 + 0.0065 + 0.007875 + 0.007875 + 0.008125 + 0.007291 + 0.007542 + 0.007458 + 0.007625 + 0.007791 + 0.00725 + 0.007708 + 0.007417 + 0.007041 + 0.012167 + 0.0075 + 0.007542 + 0.007708 + 0.007625 + 0.007459 + 0.00775 + 0.010333 + 0.0075 + 0.007583 + 0.007625 + 0.007833 + 0.007625 + 0.007958 + 0.007416 + 0.007584 + 0.007584 + 0.007667 + 0.007583 + 0.035125 + 0.027875 + 0.009375 + 0.010708 + 0.009416 + 0.009084 + 0.009583 + 0.008791 + 0.009 + 0.012083 + 0.011875 + 0.009916 + 0.014417 + 0.009542 + 0.008708 + 0.010416 + 0.010209 + 0.008833 + 0.009041 + 0.008958 + 0.010416 + 0.008792 + 0.009375 + 0.010791 + 0.009167 + 0.009 + 0.012208 + 0.013375 + 0.031875 + 0.029666 + 0.012625 + 0.010208 + 0.008958 + 0.008333 + 0.008333 + 0.008584 + 0.0085 + 0.007917 + 0.0085 + 0.008334 + 0.012083 + 0.009125 + 0.011708 + 0.009958 + 0.008583 + 0.009125 + 0.008959 + 0.008417 + 0.008833 + 0.011708 + 0.009458 + 0.009042 + 0.008792 + 0.008917 + 0.009042 + 0.011666 + 0.00775 + 0.007125 + 0.007042 + 0.007166 + 0.006542 + 0.006958 + 0.007125 + 0.00725 + 0.007542 + 0.010167 + 0.009042 + 0.008791 + 0.006625 + 0.006292 + 0.008625 + 0.008208 + 0.008125 + 0.007625 + 0.007583 + 0.007959 + 0.008084 + 0.007583 + 0.007458 + 0.007833 + 0.007958 + 0.00875 + 0.009125 + 0.010209 + 0.009834 + 0.010166 + 0.010625 + 0.009583 + 0.010292 + 0.010666 + 0.008833 + 0.009 + 0.00825 + 0.008334 + 0.009375 + 0.010333 + 0.008875 + 0.008666 + 0.009625 + 0.010458 + 0.007875 + 0.007791 + 0.007875 + 0.007625 + 0.007583 + 0.007708 + 0.00825 + 0.00825 + 0.007958 + 0.008 + 0.007958 + 0.008083 + 0.00825 + 0.008792 + 0.007834 + 0.00775 + 0.007541 + 0.007792 + 0.00825 + 0.007958 + 0.007708 + 0.008333 + 0.008375 + 0.008375 + 0.007791 + 0.007833 + 0.008 + 0.008 + 0.007833 + 0.007833 + 0.008542 + 0.007958 + 0.010583 + 0.008292 + 0.00825 + 0.008042 + 0.008292 + 0.009375 + 0.009625 + 0.008542 + 0.009042 + 0.009333 + 0.008792 + 0.011459 + 0.009167 + 0.0085 + 0.008458 + 0.00825 + 0.007625 + 0.008958 + 0.008791 + 0.010958 + 0.009542 + 0.009166 + 0.008292 + 0.008791 + 0.007417 + 0.007 + 0.007 + 0.006667 + 0.007209 + 0.00675 + 0.006583 + 0.007542 + 0.006583 + 0.008833 + 0.009083 + 0.008542 + 0.00625 + 0.006083 + 0.00775 + 0.007875 + 0.007625 + 0.007583 + 0.007208 + 0.008 + 0.007709 + 0.007958 + 0.007875 + 0.007541 + 0.00725 + 0.007625 + 0.0075 + 0.00725 + 0.007041 + 0.007167 + 0.007708 + 0.007375 + 0.008375 + 0.007625 + 0.007666 + 0.007167 + 0.00775 + 0.007458 + 0.0075 + 0.007208 + 0.007333 + 0.0075 + 0.007792 + 0.0075 + 0.007292 + 0.007333 + 0.00775 + 0.007958 + 0.007833 + 0.007458 + 0.008 + 0.008166 + 0.00775 + 0.0085 + 0.008292 + 0.008208 + 0.007792 + 0.008208 + 0.007875 + 0.00775 + 0.01075 + 0.007917 + 0.008083 + 0.007958 + 0.009542 + 0.00825 + 0.007875 + 0.008084 + 0.008042 + 0.007916 + 0.008 + 0.007792 + 0.007792 + 0.008083 + 0.008417 + 0.008333 + 0.008333 + 0.010375 + 0.008375 + 0.008209 + 0.008083 + 0.007666 + 0.008208 + 0.008458 + 0.009083 + 0.008625 + 0.00825 + 0.011292 + 0.008875 + 0.008375 + 0.008542 + 0.008666 + 0.008542 + 0.009292 + 0.011542 + 0.009083 + 0.008833 + 0.008625 + 0.008625 + 0.008917 + 0.007 + 0.006791 + 0.00775 + 0.0075 + 0.007959 + 0.007625 + 0.006375 + 0.006583 + 0.006833 + 0.007125 + 0.010041 + 0.006 + 0.006208 + 0.006167 + 0.008041 + 0.007834 + 0.007709 + 0.009417 + 0.007833 + 0.007875 + 0.007792 + 0.007709 + 0.007458 + 0.007625 + 0.007875 + 0.007541 + 0.007042 + 0.007333 + 0.007208 + 0.007167 + 0.007333 + 0.00725 + 0.007292 + 0.007375 + 0.007958 + 0.015542 + 0.00775 + 0.007667 + 0.007875 + 0.007667 + 0.007791 + 0.0075 + 0.007958 + 0.007792 + 0.007416 + 0.007709 + 0.008167 + 0.00725 + 0.007709 + 0.007042 + 0.008042 + 0.008 + 0.007458 + 0.007625 + 0.007458 + 0.008125 + 0.008 + 0.008417 + 0.011083 + 0.008 + 0.007916 + 0.007417 + 0.007625 + 0.007667 + 0.007666 + 0.00825 + 0.00775 + 0.008584 + 0.008125 + 0.008 + 0.007917 + 0.007625 + 0.007834 + 0.008042 + 0.008416 + 0.010875 + 0.008083 + 0.007792 + 0.01825 + 0.008208 + 0.00825 + 0.007833 + 0.007917 + 0.008084 + 0.011417 + 0.016041 + 0.00925 + 0.008541 + 0.011292 + 0.008958 + 0.008208 + 0.008708 + 0.008583 + 0.008417 + 0.00825 + 0.012292 + 0.010834 + 0.00975 + 0.009 + 0.009042 + 0.008541 + 0.007 + 0.006958 + 0.00675 + 0.007 + 0.006542 + 0.006583 + 0.006792 + 0.006959 + 0.006875 + 0.008625 + 0.008166 + 0.008083 + 0.006209 + 0.009167 + 0.007458 + 0.007333 + 0.008208 + 0.007417 + 0.007541 + 0.007458 + 0.007333 + 0.007667 + 0.007375 + 0.007583 + 0.007584 + 0.007791 + 0.007584 + 0.007292 + 0.007333 + 0.007209 + 0.007041 + 0.007292 + 0.010458 + 0.007333 + 0.00725 + 0.00775 + 0.008125 + 0.0075 + 0.007875 + 0.007917 + 0.007625 + 0.0075 + 0.007709 + 0.007333 + 0.007375 + 0.007583 + 0.008041 + 0.0075 + 0.012083 + 0.008167 + 0.008167 + 0.007625 + 0.007792 + 0.008041 + 0.007833 + 0.008125 + 0.007375 + 0.007791 + 0.008084 + 0.010958 + 0.013 + 0.00775 + 0.008375 + 0.008375 + 0.015333 + 0.008083 + 0.008458 + 0.02175 + 0.008333 + 0.008125 + 0.008458 + 0.007875 + 0.007958 + 0.007791 + 0.008208 + 0.008375 + 0.007958 + 0.008125 + 0.008083 + 0.007875 + 0.008 + 0.015375 + 0.007917 + 0.007625 + 0.009125 + 0.00925 + 0.008834 + 0.009125 + 0.008334 + 0.01525 + 0.008625 + 0.008084 + 0.00875 + 0.008709 + 0.0085 + 0.008417 + 0.011291 + 0.00875 + 0.008875 + 0.008416 + 0.006875 + 0.00725 + 0.006875 + 0.006958 + 0.007417 + 0.007 + 0.006959 + 0.006792 + 0.007875 + 0.009 + 0.009834 + 0.008167 + 0.006125 + 0.006209 + 0.007875 + 0.00775 + 0.007917 + 0.007375 + 0.00775 + 0.00775 + 0.007209 + 0.007542 + 0.00775 + 0.007125 + 0.007541 + 0.007709 + 0.007583 + 0.007208 + 0.007291 + 0.007375 + 0.007375 + 0.007042 + 0.006833 + 0.007292 + 0.007084 + 0.007209 + 0.007125 + 0.007667 + 0.007416 + 0.007667 + 0.007542 + 0.008 + 0.00775 + 0.007459 + 0.007083 + 0.007792 + 0.007458 + 0.007292 + 0.007166 + 0.007375 + 0.007875 + 0.007875 + 0.007875 + 0.007459 + 0.007458 + 0.007542 + 0.007959 + 0.007916 + 0.008084 + 0.008 + 0.008042 + 0.00775 + 0.008 + 0.007667 + 0.012625 + 0.008292 + 0.007958 + 0.007917 + 0.007708 + 0.008166 + 0.007667 + 0.007667 + 0.007792 + 0.008 + 0.008167 + 0.008042 + 0.007958 + 0.007583 + 0.007958 + 0.008208 + 0.007875 + 0.007875 + 0.008 + 0.007625 + 0.014125 + 0.009458 + 0.008542 + 0.009041 + 0.008708 + 0.009333 + 0.011 + 0.008792 + 0.008 + 0.00825 + 0.008458 + 0.008417 + 0.00875 + 0.010875 + 0.009708 + 0.008667 + 0.007125 + 0.007125 + 0.006833 + 0.007042 + 0.007 + 0.0075 + 0.007208 + 0.009167 + 0.006833 + 0.007625 + 0.008208 + 0.007916 + 0.006167 + 0.005708 + 0.007667 + 0.00725 + 0.007666 + 0.007084 + 0.007375 + 0.0075 + 0.007666 + 0.007208 + 0.007542 + 0.006958 + 0.0075 + 0.007125 + 0.007708 + 0.007625 + 0.007708 + 0.008125 + 0.008042 + 0.008292 + 0.007959 + 0.007541 + 0.0175 + 0.012792 + 0.007583 + 0.007958 + 0.0075 + 0.018833 + 0.007625 + 0.007417 + 0.0095 + 0.007833 + 0.007708 + 0.018125 + 0.007791 + 0.007875 + 0.008084 + 0.014458 + 0.008583 + 0.008125 + 0.008 + 0.008125 + 0.008667 + 0.008958 + 0.015042 + 0.008875 + 0.009875 + 0.008625 + 0.008583 + 0.008208 + 0.008917 + 0.010625 + 0.008125 + 0.0085 + 0.009 + 0.009042 + 0.008208 + 0.008458 + 0.00875 + 0.016584 + 0.008459 + 0.008542 + 0.009042 + 0.008791 + 0.00775 + 0.008125 + 0.007708 + 0.013958 + 0.007875 + 0.008333 + 0.008167 + 0.00825 + 0.012542 + 0.009625 + 0.009041 + 0.008625 + 0.009083 + 0.009125 + 0.012625 + 0.0095 + 0.008125 + 0.00875 + 0.008375 + 0.008333 + 0.008875 + 0.015458 + 0.009459 + 0.008709 + 0.007292 + 0.010667 + 0.007084 + 0.006792 + 0.007 + 0.007375 + 0.006625 + 0.007125 + 0.006875 + 0.006958 + 0.009167 + 0.006291 + 0.005875 + 0.006 + 0.007541 + 0.017417 + 0.007792 + 0.007583 + 0.007709 + 0.007459 + 0.007375 + 0.007875 + 0.007333 + 0.007125 + 0.0075 + 0.007541 + 0.007375 + 0.0075 + 0.007416 + 0.007667 + 0.007792 + 0.007083 + 0.007958 + 0.00775 + 0.007167 + 0.007667 + 0.007042 + 0.00725 + 0.0075 + 0.007041 + 0.007375 + 0.007833 + 0.010833 + 0.007208 + 0.007584 + 0.007291 + 0.007667 + 0.007584 + 0.007583 + 0.007458 + 0.007667 + 0.008208 + 0.008208 + 0.008208 + 0.00775 + 0.007791 + 0.007959 + 0.00825 + 0.0085 + 0.007917 + 0.008666 + 0.007833 + 0.007917 + 0.008209 + 0.00825 + 0.008209 + 0.008042 + 0.00825 + 0.007459 + 0.01175 + 0.007792 + 0.007417 + 0.007791 + 0.007708 + 0.008375 + 0.007625 + 0.008208 + 0.007583 + 0.007917 + 0.008 + 0.008042 + 0.008 + 0.008 + 0.008 + 0.007708 + 0.011667 + 0.008917 + 0.009041 + 0.017334 + 0.009459 + 0.008584 + 0.008792 + 0.008125 + 0.012083 + 0.008583 + 0.01525 + 0.008542 + 0.00825 + 0.008417 + 0.008291 + 0.008375 + 0.006958 + 0.007042 + 0.006833 + 0.006916 + 0.006708 + 0.006833 + 0.006458 + 0.006583 + 0.007208 + 0.008583 + 0.005709 + 0.006333 + 0.006125 + 0.00825 + 0.008125 + 0.008167 + 0.007708 + 0.007791 + 0.007875 + 0.007584 + 0.00775 + 0.007625 + 0.0075 + 0.007458 + 0.007625 + 0.007709 + 0.007333 + 0.0075 + 0.0075 + 0.007708 + 0.007916 + 0.008292 + 0.00775 + 0.007666 + 0.00775 + 0.007083 + 0.006958 + 0.007792 + 0.008125 + 0.007791 + 0.007417 + 0.011583 + 0.007875 + 0.007917 + 0.007875 + 0.00775 + 0.008333 + 0.008125 + 0.008 + 0.00825 + 0.008333 + 0.008208 + 0.008 + 0.007958 + 0.008208 + 0.014167 + 0.007958 + 0.013167 + 0.011708 + 0.008166 + 0.008458 + 0.008 + 0.00775 + 0.007709 + 0.00775 + 0.008292 + 0.00775 + 0.007584 + 0.007959 + 0.007584 + 0.00725 + 0.007791 + 0.007917 + 0.007709 + 0.008167 + 0.008208 + 0.008083 + 0.007875 + 0.008042 + 0.021167 + 0.008583 + 0.007958 + 0.008208 + 0.008042 + 0.012625 + 0.008625 + 0.008792 + 0.008458 + 0.015917 + 0.008917 + 0.008667 + 0.008208 + 0.009208 + 0.008875 + 0.008709 + 0.011584 + 0.008792 + 0.007917 + 0.009042 + 0.00825 + 0.007167 + 0.006958 + 0.007083 + 0.007042 + 0.007166 + 0.006833 + 0.007208 + 0.007625 + 0.006834 + 0.008459 + 0.008375 + 0.008667 + 0.006125 + 0.006334 + 0.008 + 0.007291 + 0.007834 + 0.007292 + 0.007208 + 0.007333 + 0.007292 + 0.007542 + 0.007333 + 0.00725 + 0.007458 + 0.008083 + 0.007417 + 0.0075 + 0.007375 + 0.007666 + 0.007292 + 0.007708 + 0.007625 + 0.007625 + 0.007708 + 0.007875 + 0.007583 + 0.007208 + 0.007333 + 0.007417 + 0.0075 + 0.008208 + 0.007041 + 0.007458 + 0.007625 + 0.0075 + 0.007584 + 0.007458 + 0.007625 + 0.00725 + 0.008 + 0.008083 + 0.00775 + 0.007541 + 0.008042 + 0.007833 + 0.007958 + 0.011125 + 0.008291 + 0.00825 + 0.007959 + 0.019375 + 0.008542 + 0.0085 + 0.007959 + 0.00825 + 0.00775 + 0.010833 + 0.00775 + 0.007833 + 0.007542 + 0.010916 + 0.007792 + 0.007833 + 0.008167 + 0.011083 + 0.008 + 0.007959 + 0.008 + 0.008042 + 0.008334 + 0.008125 + 0.008167 + 0.008333 + 0.017458 + 0.00925 + 0.00875 + 0.008542 + 0.008417 + 0.009084 + 0.011375 + 0.009042 + 0.007875 + 0.008625 + 0.008625 + 0.009125 + 0.008709 + 0.011459 + 0.00875 + 0.008334 + 0.007334 + 0.007083 + 0.006833 + 0.007 + 0.0075 + 0.007125 + 0.006917 + 0.0065 + 0.007167 + 0.006958 + 0.009042 + 0.007459 + 0.005875 + 0.005792 + 0.007833 + 0.007209 + 0.007667 + 0.007541 + 0.007375 + 0.007583 + 0.0075 + 0.007792 + 0.007208 + 0.007375 + 0.007333 + 0.007584 + 0.007416 + 0.007542 + 0.007541 + 0.007584 + 0.007083 + 0.007417 + 0.00725 + 0.007458 + 0.007625 + 0.007417 + 0.007416 + 0.007834 + 0.007417 + 0.007125 + 0.007208 + 0.007458 + 0.007 + 0.007542 + 0.007625 + 0.007375 + 0.008 + 0.007583 + 0.007583 + 0.007417 + 0.007791 + 0.007708 + 0.008125 + 0.008166 + 0.007833 + 0.007542 + 0.008084 + 0.008417 + 0.00775 + 0.008042 + 0.007958 + 0.007875 + 0.008 + 0.007875 + 0.007917 + 0.007875 + 0.007667 + 0.008208 + 0.010625 + 0.008209 + 0.007541 + 0.008042 + 0.007625 + 0.007459 + 0.008042 + 0.008083 + 0.007958 + 0.007458 + 0.009917 + 0.008375 + 0.007708 + 0.007917 + 0.007959 + 0.008 + 0.011458 + 0.008875 + 0.008792 + 0.008833 + 0.00875 + 0.008541 + 0.010667 + 0.009458 + 0.008125 + 0.008583 + 0.008625 + 0.008625 + 0.008792 + 0.011166 + 0.009167 + 0.009166 + 0.007334 + 0.006916 + 0.00675 + 0.006667 + 0.006875 + 0.006625 + 0.006917 + 0.006875 + 0.006834 + 0.006583 + 0.008875 + 0.005875 + 0.006459 + 0.005959 + 0.00775 + 0.007625 + 0.007375 + 0.007042 + 0.00775 + 0.009167 + 0.008 + 0.007625 + 0.007417 + 0.007792 + 0.007458 + 0.007417 + 0.007375 + 0.007291 + 0.007667 + 0.007333 + 0.007125 + 0.0075 + 0.007791 + 0.007417 + 0.007375 + 0.007375 + 0.00775 + 0.007625 + 0.007417 + 0.0075 + 0.007375 + 0.007 + 0.0075 + 0.007792 + 0.007292 + 0.007334 + 0.00725 + 0.007334 + 0.007042 + 0.0075 + 0.007333 + 0.007875 + 0.007958 + 0.007875 + 0.008041 + 0.00775 + 0.007667 + 0.008166 + 0.008292 + 0.007709 + 0.007709 + 0.008125 + 0.007667 + 0.008083 + 0.010667 + 0.008042 + 0.007583 + 0.007041 + 0.008125 + 0.00825 + 0.007584 + 0.007916 + 0.007417 + 0.007375 + 0.007875 + 0.007291 + 0.0075 + 0.007875 + 0.00775 + 0.007417 + 0.007958 + 0.008042 + 0.007458 + 0.008208 + 0.008333 + 0.011125 + 0.009042 + 0.008708 + 0.008791 + 0.008583 + 0.008167 + 0.011417 + 0.008333 + 0.0105 + 0.009 + 0.008625 + 0.008667 + 0.008791 + 0.011334 + 0.009208 + 0.008167 + 0.007083 + 0.007041 + 0.006959 + 0.006875 + 0.007042 + 0.006792 + 0.006833 + 0.007042 + 0.007166 + 0.008959 + 0.005792 + 0.005833 + 0.006208 + 0.008084 + 0.007208 + 0.007333 + 0.007583 + 0.00725 + 0.007041 + 0.00725 + 0.007916 + 0.007417 + 0.007458 + 0.00725 + 0.008125 + 0.007666 + 0.007375 + 0.007625 + 0.00775 + 0.007791 + 0.007625 + 0.0075 + 0.007875 + 0.012333 + 0.007833 + 0.007583 + 0.013917 + 0.047083 + 0.048167 + 0.014375 + 0.013334 + 0.012417 + 0.011041 + 0.009625 + 0.009958 + 0.010625 + 0.009209 + 0.009041 + 0.009375 + 0.010041 + 0.013125 + 0.011083 + 0.009708 + 0.01075 + 0.012333 + 0.009291 + 0.009333 + 0.021833 + 0.010042 + 0.010791 + 0.012333 + 0.01025 + 0.009833 + 0.010459 + 0.009709 + 0.012791 + 0.0095 + 0.01075 + 0.00875 + 0.015792 + 0.011458 + 0.010208 + 0.008542 + 0.009041 + 0.009583 + 0.009958 + 0.0085 + 0.008541 + 0.008583 + 0.008875 + 0.008916 + 0.008792 + 0.010625 + 0.009125 + 0.015292 + 0.014583 + 0.012291 + 0.01125 + 0.010333 + 0.009875 + 0.010458 + 0.011583 + 0.016458 + 0.012125 + 0.011458 + 0.009833 + 0.010875 + 0.010166 + 0.015417 + 0.01225 + 0.011459 + 0.007959 + 0.008583 + 0.008125 + 0.008333 + 0.008292 + 0.008334 + 0.0085 + 0.008125 + 0.012167 + 0.009584 + 0.01075 + 0.007542 + 0.008209 + 0.010625 + 0.00925 + 0.009333 + 0.009417 + 0.009833 + 0.011042 + 0.009292 + 0.008541 + 0.008667 + 0.008375 + 0.010333 + 0.012041 + 0.011458 + 0.011208 + 0.011 + 0.009 + 0.009584 + 0.008667 + 0.009083 + 0.009 + 0.009417 + 0.008916 + 0.008584 + 0.008959 + 0.007667 + 0.008875 + 0.008292 + 0.008 + 0.008333 + 0.0085 + 0.008792 + 0.009 + 0.007792 + 0.008291 + 0.008125 + 0.007542 + 0.008667 + 0.0095 + 0.009084 + 0.008834 + 0.009333 + 0.008583 + 0.008584 + 0.00875 + 0.0095 + 0.009417 + 0.009125 + 0.008542 + 0.008708 + 0.008583 + 0.008417 + 0.009084 + 0.008333 + 0.009042 + 0.009209 + 0.008875 + 0.008459 + 0.008416 + 0.01325 + 0.010459 + 0.011042 + 0.009083 + 0.009 + 0.009125 + 0.008333 + 0.009125 + 0.009042 + 0.00775 + 0.007375 + 0.008875 + 0.010125 + 0.009209 + 0.011667 + 0.011333 + 0.010791 + 0.0095 + 0.009791 + 0.008875 + 0.008125 + 0.011125 + 0.010875 + 0.01075 + 0.0095 + 0.009667 + 0.00975 + 0.011917 + 0.009833 + 0.009125 + 0.009791 + 0.009834 + 0.009708 + 0.010583 + 0.010375 + 0.008041 + 0.008333 + 0.007417 + 0.008834 + 0.006791 + 0.00625 + 0.006458 + 0.0085 + 0.00825 + 0.008334 + 0.0085 + 0.008333 + 0.00875 + 0.008792 + 0.007875 + 0.008 + 0.008625 + 0.008167 + 0.008375 + 0.007625 + 0.008042 + 0.008 + 0.008 + 0.008334 + 0.008959 + 0.008459 + 0.018542 + 0.008416 + 0.008917 + 0.009625 + 0.010333 + 0.010917 + 0.011084 + 0.011084 + 0.0105 + 0.007834 + 0.008375 + 0.008125 + 0.00825 + 0.007792 + 0.008959 + 0.008584 + 0.008459 + 0.009042 + 0.008417 + 0.008417 + 0.008541 + 0.008792 + 0.008375 + 0.010584 + 0.01 + 0.008917 + 0.008667 + 0.008833 + 0.008458 + 0.008417 + 0.009167 + 0.008416 + 0.008958 + 0.008875 + 0.008792 + 0.008208 + 0.008292 + 0.00875 + 0.008667 + 0.009 + 0.00925 + 0.008625 + 0.0085 + 0.01225 + 0.011667 + 0.010833 + 0.012083 + 0.010125 + 0.0095 + 0.008541 + 0.008792 + 0.009833 + 0.011 + 0.0125 + 0.019333 + 0.012041 + 0.010958 + 0.009417 + 0.009292 + 0.009166 + 0.011792 + 0.012875 + 0.01275 + 0.012541 + 0.009708 + 0.009292 + 0.012125 + 0.010167 + 0.00925 + 0.01025 + 0.01 + 0.007834 + 0.007334 + 0.007542 + 0.007791 + 0.007875 + 0.007792 + 0.01025 + 0.006833 + 0.007541 + 0.00775 + 0.008416 + 0.0085 + 0.008208 + 0.007833 + 0.008041 + 0.008125 + 0.008875 + 0.008583 + 0.009291 + 0.008292 + 0.008333 + 0.008 + 0.009125 + 0.0085 + 0.008541 + 0.008834 + 0.007541 + 0.00825 + 0.008583 + 0.0085 + 0.00825 + 0.008708 + 0.008625 + 0.008791 + 0.008833 + 0.008459 + 0.007791 + 0.007875 + 0.007792 + 0.0085 + 0.008292 + 0.0085 + 0.008209 + 0.008166 + 0.008 + 0.008042 + 0.007709 + 0.008291 + 0.008 + 0.008958 + 0.011 + 0.010625 + 0.0095 + 0.00925 + 0.011375 + 0.009625 + 0.009166 + 0.009458 + 0.009 + 0.008625 + 0.008916 + 0.00925 + 0.008333 + 0.007833 + 0.008625 + 0.008917 + 0.008333 + 0.008333 + 0.011208 + 0.0115 + 0.009958 + 0.008583 + 0.009208 + 0.009333 + 0.009 + 0.009292 + 0.009583 + 0.009416 + 0.008792 + 0.00925 + 0.008417 + 0.009375 + 0.009334 + 0.01225 + 0.012084 + 0.011417 + 0.012166 + 0.009792 + 0.008834 + 0.009417 + 0.012042 + 0.012042 + 0.011958 + 0.011417 + 0.00975 + 0.009209 + 0.011834 + 0.009625 + 0.009334 + 0.009583 + 0.009583 + 0.008083 + 0.0075 + 0.006958 + 0.007542 + 0.007042 + 0.009292 + 0.006709 + 0.006375 + 0.006375 + 0.0095 + 0.0085 + 0.008416 + 0.008083 + 0.007708 + 0.008208 + 0.007958 + 0.008042 + 0.008334 + 0.007666 + 0.007625 + 0.007833 + 0.007709 + 0.008125 + 0.008167 + 0.007959 + 0.007417 + 0.007916 + 0.00825 + 0.008416 + 0.008375 + 0.0145 + 0.011 + 0.00825 + 0.008083 + 0.013292 + 0.00825 + 0.017167 + 0.007791 + 0.007834 + 0.015375 + 0.014208 + 0.016333 + 0.009292 + 0.030958 + 0.02025 + 0.011292 + 0.0125 + 0.010125 + 0.01025 + 0.009708 + 0.009042 + 0.00875 + 0.00875 + 0.009667 + 0.0085 + 0.008375 + 0.008792 + 0.008834 + 0.008791 + 0.008917 + 0.0095 + 0.01025 + 0.01175 + 0.01225 + 0.011083 + 0.011166 + 0.011667 + 0.011834 + 0.011 + 0.011 + 0.008833 + 0.011708 + 0.013083 + 0.010958 + 0.011375 + 0.010125 + 0.009 + 0.008208 + 0.008792 + 0.008417 + 0.011333 + 0.009166 + 0.009 + 0.015041 + 0.01125 + 0.015 + 0.008958 + 0.008583 + 0.008542 + 0.009167 + 0.014083 + 0.011833 + 0.010208 + 0.00875 + 0.018208 + 0.009375 + 0.009083 + 0.008375 + 0.007959 + 0.00725 + 0.007375 + 0.007292 + 0.007375 + 0.008833 + 0.006917 + 0.010083 + 0.011 + 0.010041 + 0.008292 + 0.008209 + 0.008958 + 0.007958 + 0.007958 + 0.007542 + 0.008125 + 0.008417 + 0.009917 + 0.010791 + 0.010333 + 0.008792 + 0.008625 + 0.008375 + 0.007875 + 0.012041 + 0.008292 + 0.007875 + 0.007666 + 0.00775 + 0.007875 + 0.007833 + 0.008 + 0.008125 + 0.008625 + 0.007416 + 0.007417 + 0.007625 + 0.00775 + 0.008458 + 0.007666 + 0.014084 + 0.008334 + 0.008292 + 0.009792 + 0.008667 + 0.008041 + 0.007959 + 0.008666 + 0.008709 + 0.008584 + 0.01025 + 0.011167 + 0.010625 + 0.013875 + 0.014542 + 0.010583 + 0.010375 + 0.010041 + 0.008209 + 0.007917 + 0.008333 + 0.008083 + 0.008583 + 0.00825 + 0.008417 + 0.008083 + 0.008583 + 0.008166 + 0.008084 + 0.008042 + 0.008125 + 0.008125 + 0.008667 + 0.008541 + 0.008 + 0.013291 + 0.015875 + 0.00925 + 0.008417 + 0.008125 + 0.008542 + 0.009375 + 0.008917 + 0.013084 + 0.008875 + 0.010792 + 0.01075 + 0.010875 + 0.008709 + 0.008667 + 0.009375 + 0.009334 + 0.011292 + 0.011166 + 0.010875 + 0.0095 + 0.009291 + 0.007208 + 0.006958 + 0.007459 + 0.019125 + 0.008375 + 0.008125 + 0.009375 + 0.008583 + 0.00775 + 0.008875 + 0.011625 + 0.010083 + 0.010042 + 0.008584 + 0.012375 + 0.009 + 0.008709 + 0.0085 + 0.008458 + 0.012083 + 0.011583 + 0.01175 + 0.010875 + 0.010917 + 0.011875 + 0.010583 + 0.011042 + 0.011916 + 0.01075 + 0.01075 + 0.01075 + 0.009417 + 0.008667 + 0.0085 + 0.008958 + 0.008334 + 0.008709 + 0.008584 + 0.010584 + 0.012125 + 0.011667 + 0.012417 + 0.011 + 0.01075 + 0.010833 + 0.010542 + 0.010959 + 0.010583 + 0.009708 + 0.008625 + 0.010541 + 0.009125 + 0.008875 + 0.009166 + 0.008417 + 0.010584 + 0.011625 + 0.016083 + 0.011666 + 0.011333 + 0.01325 + 0.011 + 0.01 + 0.01 + 0.010041 + 0.0115 + 0.009625 + 0.013959 + 0.01075 + 0.010083 + 0.009583 + 0.008792 + 0.009 + 0.008792 + 0.009958 + 0.017083 + 0.011375 + 0.011417 + 0.011375 + 0.009959 + 0.008791 + 0.008584 + 0.00875 + 0.01025 + 0.013041 + 0.009458 + 0.009583 + 0.01225 + 0.012375 + 0.01225 + 0.0125 + 0.00975 + 0.010625 + 0.012084 + 0.011792 + 0.012291 + 0.0125 + 0.012209 + 0.013042 + 0.009667 + 0.008 + 0.008167 + 0.008667 + 0.008708 + 0.007583 + 0.007167 + 0.007459 + 0.007709 + 0.006709 + 0.007584 + 0.009625 + 0.006875 + 0.006958 + 0.0065 + 0.008125 + 0.007958 + 0.007875 + 0.007625 + 0.008542 + 0.008375 + 0.008333 + 0.008208 + 0.007792 + 0.007875 + 0.00775 + 0.007708 + 0.008708 + 0.008 + 0.008208 + 0.008708 + 0.0075 + 0.008042 + 0.008042 + 0.014125 + 0.01075 + 0.01075 + 0.0105 + 0.010333 + 0.0085 + 0.007833 + 0.007625 + 0.008208 + 0.008333 + 0.008584 + 0.00825 + 0.008084 + 0.007834 + 0.008333 + 0.007666 + 0.010083 + 0.010667 + 0.011583 + 0.01125 + 0.009417 + 0.008333 + 0.008333 + 0.008375 + 0.008542 + 0.008625 + 0.008667 + 0.008375 + 0.00875 + 0.008208 + 0.008208 + 0.008833 + 0.008292 + 0.008792 + 0.008084 + 0.00875 + 0.008584 + 0.008834 + 0.011541 + 0.008208 + 0.008583 + 0.007875 + 0.008083 + 0.008458 + 0.012 + 0.011875 + 0.018333 + 0.010208 + 0.009 + 0.018875 + 0.008916 + 0.008375 + 0.010291 + 0.010208 + 0.009084 + 0.012459 + 0.013167 + 0.01275 + 0.012458 + 0.009917 + 0.009333 + 0.013125 + 0.018 + 0.012542 + 0.013375 + 0.011375 + 0.010167 + 0.013375 + 0.024792 + 0.011667 + 0.0105 + 0.010917 + 0.009917 + 0.00875 + 0.008375 + 0.008584 + 0.007417 + 0.009958 + 0.011125 + 0.010584 + 0.007083 + 0.006875 + 0.010333 + 0.009083 + 0.008875 + 0.008083 + 0.007875 + 0.008709 + 0.017167 + 0.008417 + 0.008709 + 0.0085 + 0.009083 + 0.008208 + 0.008542 + 0.008041 + 0.008333 + 0.008292 + 0.008666 + 0.008792 + 0.007875 + 0.007834 + 0.007459 + 0.007792 + 0.007625 + 0.008416 + 0.008125 + 0.008208 + 0.007792 + 0.008042 + 0.00825 + 0.007958 + 0.00775 + 0.008834 + 0.00825 + 0.008125 + 0.008042 + 0.007791 + 0.00875 + 0.008542 + 0.008125 + 0.008 + 0.008667 + 0.00875 + 0.007792 + 0.008458 + 0.01125 + 0.009875 + 0.010416 + 0.010625 + 0.009625 + 0.009291 + 0.00875 + 0.008667 + 0.008833 + 0.0095 + 0.011041 + 0.011667 + 0.011375 + 0.010875 + 0.008416 + 0.008083 + 0.008833 + 0.008292 + 0.0095 + 0.0115 + 0.012334 + 0.01175 + 0.0105 + 0.010416 + 0.021 + 0.009708 + 0.009917 + 0.009417 + 0.009458 + 0.009583 + 0.010958 + 0.011416 + 0.0105 + 0.009041 + 0.007625 + 0.009833 + 0.009583 + 0.009333 + 0.014083 + 0.011416 + 0.023 + 0.0095 + 0.007458 + 0.007625 + 0.007833 + 0.007333 + 0.007 + 0.006833 + 0.00725 + 0.008 + 0.006917 + 0.008875 + 0.009375 + 0.011209 + 0.006542 + 0.006583 + 0.007916 + 0.008125 + 0.007625 + 0.007916 + 0.008375 + 0.007916 + 0.008334 + 0.007667 + 0.007833 + 0.007834 + 0.007917 + 0.007417 + 0.008292 + 0.008083 + 0.008208 + 0.00775 + 0.00775 + 0.00775 + 0.00775 + 0.007791 + 0.008 + 0.008417 + 0.007542 + 0.008167 + 0.008 + 0.007917 + 0.01075 + 0.008791 + 0.0085 + 0.0085 + 0.008375 + 0.008125 + 0.008209 + 0.007792 + 0.00775 + 0.00825 + 0.008375 + 0.008542 + 0.008625 + 0.00825 + 0.008959 + 0.008666 + 0.0085 + 0.00825 + 0.008958 + 0.008625 + 0.008709 + 0.0085 + 0.008333 + 0.008542 + 0.014583 + 0.01075 + 0.010375 + 0.009834 + 0.008167 + 0.008958 + 0.009542 + 0.008583 + 0.008583 + 0.009125 + 0.008958 + 0.008666 + 0.008042 + 0.008666 + 0.008667 + 0.007916 + 0.008042 + 0.016833 + 0.008542 + 0.014125 + 0.012667 + 0.012042 + 0.012083 + 0.011792 + 0.011791 + 0.009792 + 0.011708 + 0.0105 + 0.01 + 0.009875 + 0.008791 + 0.009 + 0.008958 + 0.011292 + 0.011041 + 0.020625 + 0.00775 + 0.007292 + 0.007416 + 0.007459 + 0.006875 + 0.007459 + 0.007791 + 0.007375 + 0.007 + 0.0075 + 0.00875 + 0.0065 + 0.006708 + 0.005667 + 0.007667 + 0.007791 + 0.007917 + 0.00775 + 0.007834 + 0.007542 + 0.008 + 0.007791 + 0.008 + 0.008541 + 0.007584 + 0.007583 + 0.007833 + 0.007708 + 0.00775 + 0.007625 + 0.007833 + 0.007958 + 0.008541 + 0.007917 + 0.007792 + 0.008042 + 0.007417 + 0.007541 + 0.007917 + 0.007625 + 0.007625 + 0.008 + 0.007959 + 0.007709 + 0.007584 + 0.007916 + 0.011167 + 0.007666 + 0.008 + 0.007625 + 0.009458 + 0.0095 + 0.008333 + 0.008 + 0.007917 + 0.008166 + 0.007959 + 0.0085 + 0.008917 + 0.0085 + 0.008292 + 0.007791 + 0.008167 + 0.008458 + 0.008 + 0.008041 + 0.008584 + 0.007792 + 0.007625 + 0.010875 + 0.009833 + 0.01 + 0.008625 + 0.007917 + 0.008417 + 0.008416 + 0.007792 + 0.008334 + 0.012916 + 0.007917 + 0.008167 + 0.008084 + 0.008625 + 0.008167 + 0.008291 + 0.011208 + 0.011208 + 0.010292 + 0.009 + 0.008542 + 0.008833 + 0.011375 + 0.009958 + 0.011 + 0.008459 + 0.008417 + 0.008292 + 0.008625 + 0.01625 + 0.013167 + 0.009959 + 0.007084 + 0.007125 + 0.006708 + 0.007125 + 0.007292 + 0.01 + 0.007 + 0.006875 + 0.007208 + 0.008916 + 0.007917 + 0.008208 + 0.009333 + 0.008875 + 0.007875 + 0.0075 + 0.007958 + 0.007709 + 0.007833 + 0.007458 + 0.0075 + 0.007584 + 0.007917 + 0.007291 + 0.008042 + 0.007875 + 0.008 + 0.007416 + 0.007292 + 0.00775 + 0.007583 + 0.007375 + 0.007583 + 0.007333 + 0.007 + 0.007625 + 0.007542 + 0.007708 + 0.007375 + 0.008125 + 0.007541 + 0.007292 + 0.007459 + 0.00725 + 0.007625 + 0.00775 + 0.007333 + 0.007792 + 0.007458 + 0.008083 + 0.008458 + 0.010375 + 0.010666 + 0.010625 + 0.0095 + 0.008334 + 0.008375 + 0.01025 + 0.01175 + 0.010834 + 0.010042 + 0.01 + 0.012208 + 0.011292 + 0.009417 + 0.009916 + 0.00975 + 0.009167 + 0.00975 + 0.011458 + 0.0115 + 0.011541 + 0.009 + 0.011417 + 0.009708 + 0.009125 + 0.0085 + 0.008084 + 0.008125 + 0.011541 + 0.010958 + 0.010792 + 0.010917 + 0.011834 + 0.012667 + 0.011333 + 0.01025 + 0.009917 + 0.011208 + 0.015208 + 0.012292 + 0.010667 + 0.011583 + 0.008458 + 0.008416 + 0.01125 + 0.012458 + 0.011458 + 0.010375 + 0.01375 + 0.009875 + 0.009375 + 0.009875 + 0.009875 + 0.009459 + 0.009125 + 0.007667 + 0.007959 + 0.008 + 0.010834 + 0.01 + 0.010666 + 0.0085 + 0.007958 + 0.012125 + 0.0105 + 0.010208 + 0.01025 + 0.010208 + 0.010125 + 0.009875 + 0.010125 + 0.010292 + 0.009334 + 0.007959 + 0.007709 + 0.009791 + 0.011333 + 0.010833 + 0.01125 + 0.011792 + 0.010334 + 0.008875 + 0.00975 + 0.009042 + 0.009333 + 0.008125 + 0.009709 + 0.009375 + 0.008625 + 0.009666 + 0.010167 + 0.009958 + 0.00875 + 0.007916 + 0.007875 + 0.007917 + 0.007625 + 0.007583 + 0.007792 + 0.008791 + 0.011208 + 0.008 + 0.009875 + 0.009083 + 0.008333 + 0.008125 + 0.008541 + 0.00825 + 0.007833 + 0.0085 + 0.010666 + 0.010042 + 0.009917 + 0.008542 + 0.0085 + 0.008333 + 0.008417 + 0.008333 + 0.007708 + 0.008209 + 0.007792 + 0.007791 + 0.008417 + 0.008458 + 0.008167 + 0.008792 + 0.008541 + 0.008125 + 0.0085 + 0.008333 + 0.010833 + 0.010542 + 0.009666 + 0.009625 + 0.00875 + 0.008833 + 0.00975 + 0.008834 + 0.011417 + 0.011333 + 0.010416 + 0.008292 + 0.009125 + 0.008709 + 0.008625 + 0.01125 + 0.011042 + 0.010292 + 0.008875 + 0.007292 + 0.007291 + 0.006917 + 0.007041 + 0.007208 + 0.007083 + 0.006958 + 0.007083 + 0.006791 + 0.008292 + 0.009292 + 0.008166 + 0.00575 + 0.006 + 0.008292 + 0.007291 + 0.00775 + 0.007625 + 0.007459 + 0.008 + 0.007708 + 0.007708 + 0.00775 + 0.007292 + 0.007625 + 0.007667 + 0.008167 + 0.007875 + 0.007292 + 0.007708 + 0.0075 + 0.007875 + 0.007958 + 0.008083 + 0.007709 + 0.0075 + 0.007375 + 0.007792 + 0.007084 + 0.007625 + 0.007375 + 0.007625 + 0.007583 + 0.0075 + 0.007833 + 0.0075 + 0.007584 + 0.007916 + 0.00775 + 0.0075 + 0.00825 + 0.008417 + 0.008375 + 0.00775 + 0.008208 + 0.007917 + 0.008084 + 0.008375 + 0.00825 + 0.008334 + 0.008125 + 0.008 + 0.008042 + 0.008083 + 0.01075 + 0.009958 + 0.009584 + 0.008709 + 0.007958 + 0.007792 + 0.008083 + 0.00775 + 0.0075 + 0.007833 + 0.008583 + 0.008 + 0.007958 + 0.0085 + 0.007959 + 0.008375 + 0.008125 + 0.008292 + 0.007875 + 0.008208 + 0.011083 + 0.01075 + 0.010833 + 0.008916 + 0.00875 + 0.00925 + 0.013833 + 0.010875 + 0.009917 + 0.009292 + 0.009 + 0.009084 + 0.008709 + 0.010666 + 0.010458 + 0.010792 + 0.00725 + 0.007083 + 0.006959 + 0.006542 + 0.007166 + 0.007292 + 0.007041 + 0.007125 + 0.006916 + 0.006959 + 0.008959 + 0.009584 + 0.005958 + 0.006667 + 0.008208 + 0.007625 + 0.008042 + 0.007625 + 0.007875 + 0.007708 + 0.008083 + 0.007625 + 0.007666 + 0.007292 + 0.007541 + 0.008 + 0.007667 + 0.007791 + 0.007584 + 0.00775 + 0.007542 + 0.007917 + 0.00775 + 0.007708 + 0.007666 + 0.007625 + 0.007375 + 0.007708 + 0.007416 + 0.007875 + 0.007833 + 0.007791 + 0.00775 + 0.00725 + 0.007625 + 0.008292 + 0.007458 + 0.00775 + 0.00775 + 0.007791 + 0.008083 + 0.00775 + 0.008 + 0.007792 + 0.007666 + 0.00775 + 0.008166 + 0.008 + 0.008375 + 0.008083 + 0.008041 + 0.008625 + 0.010834 + 0.008125 + 0.008333 + 0.008292 + 0.008 + 0.008042 + 0.008167 + 0.0135 + 0.0095 + 0.00975 + 0.008375 + 0.008166 + 0.008792 + 0.007708 + 0.008167 + 0.008166 + 0.007708 + 0.008542 + 0.008291 + 0.008125 + 0.008083 + 0.008334 + 0.007958 + 0.009792 + 0.010584 + 0.011042 + 0.01075 + 0.008875 + 0.008458 + 0.008667 + 0.008292 + 0.011708 + 0.011125 + 0.010583 + 0.008667 + 0.008916 + 0.009 + 0.013625 + 0.010583 + 0.008792 + 0.007625 + 0.007084 + 0.006875 + 0.007042 + 0.007 + 0.00675 + 0.007167 + 0.007125 + 0.008333 + 0.006375 + 0.009041 + 0.00625 + 0.008458 + 0.007833 + 0.0075 + 0.007917 + 0.007792 + 0.007375 + 0.007709 + 0.007834 + 0.0075 + 0.007542 + 0.007541 + 0.007333 + 0.0075 + 0.008542 + 0.009375 + 0.007917 + 0.007417 + 0.00775 + 0.010625 + 0.007625 + 0.008042 + 0.007875 + 0.0075 + 0.010125 + 0.007791 + 0.007958 + 0.007 + 0.00775 + 0.007042 + 0.0075 + 0.007417 + 0.007792 + 0.007541 + 0.010084 + 0.00775 + 0.007375 + 0.007875 + 0.008542 + 0.007875 + 0.00825 + 0.008 + 0.007958 + 0.008125 + 0.007542 + 0.008125 + 0.008292 + 0.008417 + 0.016584 + 0.008833 + 0.008 + 0.009042 + 0.00975 + 0.008 + 0.01025 + 0.014125 + 0.010666 + 0.011959 + 0.008875 + 0.007833 + 0.008125 + 0.008 + 0.007958 + 0.007958 + 0.007875 + 0.007875 + 0.008458 + 0.007917 + 0.008416 + 0.008084 + 0.008 + 0.008583 + 0.01125 + 0.013917 + 0.011667 + 0.011291 + 0.011125 + 0.009 + 0.008709 + 0.008125 + 0.014708 + 0.011875 + 0.011167 + 0.011542 + 0.009458 + 0.008667 + 0.011417 + 0.011666 + 0.010542 + 0.009167 + 0.008583 + 0.006916 + 0.010167 + 0.006625 + 0.007417 + 0.007917 + 0.007292 + 0.00775 + 0.008959 + 0.008625 + 0.006084 + 0.00675 + 0.008042 + 0.008042 + 0.007459 + 0.007292 + 0.007833 + 0.007959 + 0.007916 + 0.007792 + 0.0075 + 0.007417 + 0.007958 + 0.007584 + 0.007583 + 0.007792 + 0.007417 + 0.007666 + 0.007583 + 0.007834 + 0.00775 + 0.007875 + 0.00775 + 0.007916 + 0.007667 + 0.007541 + 0.010625 + 0.008333 + 0.010625 + 0.007625 + 0.007583 + 0.007792 + 0.009416 + 0.009959 + 0.009875 + 0.009792 + 0.00975 + 0.007542 + 0.008625 + 0.007792 + 0.00775 + 0.008458 + 0.008375 + 0.00925 + 0.008292 + 0.008042 + 0.007834 + 0.0085 + 0.008 + 0.008125 + 0.008083 + 0.008333 + 0.007958 + 0.008125 + 0.007875 + 0.008417 + 0.007833 + 0.008458 + 0.007959 + 0.013042 + 0.010375 + 0.010375 + 0.010791 + 0.009708 + 0.007792 + 0.00775 + 0.007958 + 0.007917 + 0.008125 + 0.008291 + 0.023917 + 0.009625 + 0.010583 + 0.008667 + 0.011791 + 0.011708 + 0.011708 + 0.011458 + 0.009708 + 0.00875 + 0.008416 + 0.011417 + 0.012125 + 0.011542 + 0.0115 + 0.009833 + 0.008625 + 0.011583 + 0.009666 + 0.008875 + 0.009041 + 0.009291 + 0.009417 + 0.009084 + 0.009334 + 0.0095 + 0.009083 + 0.00875 + 0.00875 + 0.008625 + 0.00625 + 0.006209 + 0.01075 + 0.007834 + 0.007417 + 0.007583 + 0.00825 + 0.007625 + 0.007292 + 0.011125 + 0.008042 + 0.007708 + 0.007458 + 0.007959 + 0.00775 + 0.007917 + 0.007583 + 0.008416 + 0.007625 + 0.007875 + 0.007625 + 0.007917 + 0.007667 + 0.007875 + 0.007959 + 0.007791 + 0.008042 + 0.007958 + 0.007875 + 0.008209 + 0.007917 + 0.007292 + 0.007583 + 0.007458 + 0.007708 + 0.008 + 0.007208 + 0.007708 + 0.00825 + 0.008166 + 0.007959 + 0.008333 + 0.008083 + 0.007917 + 0.008375 + 0.008542 + 0.00825 + 0.008041 + 0.008083 + 0.007959 + 0.007875 + 0.00825 + 0.008292 + 0.007916 + 0.00775 + 0.008125 + 0.008209 + 0.008209 + 0.008 + 0.008042 + 0.008041 + 0.007917 + 0.00825 + 0.00775 + 0.014375 + 0.010292 + 0.010292 + 0.010458 + 0.010541 + 0.008167 + 0.008125 + 0.008375 + 0.009333 + 0.008708 + 0.009 + 0.011625 + 0.011916 + 0.014708 + 0.012458 + 0.01 + 0.008291 + 0.008625 + 0.015875 + 0.012042 + 0.011875 + 0.011459 + 0.009667 + 0.008875 + 0.007833 + 0.009167 + 0.008917 + 0.008709 + 0.007916 + 0.007708 + 0.007625 + 0.010708 + 0.007583 + 0.0085 + 0.012083 + 0.021625 + 0.007083 + 0.006625 + 0.008458 + 0.008583 + 0.007583 + 0.007458 + 0.007292 + 0.007166 + 0.007625 + 0.00725 + 0.007625 + 0.007375 + 0.007292 + 0.00725 + 0.007583 + 0.007917 + 0.010375 + 0.007625 + 0.015834 + 0.01 + 0.009542 + 0.008292 + 0.007875 + 0.007833 + 0.007917 + 0.008 + 0.007542 + 0.016209 + 0.01225 + 0.007709 + 0.007458 + 0.007791 + 0.007875 + 0.007792 + 0.007875 + 0.007625 + 0.007625 + 0.007959 + 0.008333 + 0.008958 + 0.009583 + 0.0105 + 0.008208 + 0.008167 + 0.008333 + 0.009541 + 0.011667 + 0.020292 + 0.012375 + 0.009708 + 0.008083 + 0.008542 + 0.009 + 0.008333 + 0.008333 + 0.008583 + 0.008375 + 0.00875 + 0.010292 + 0.008583 + 0.015792 + 0.009833 + 0.01075 + 0.008083 + 0.009125 + 0.009667 + 0.008417 + 0.011166 + 0.011792 + 0.011083 + 0.010958 + 0.010875 + 0.010833 + 0.012625 + 0.01275 + 0.010459 + 0.009959 + 0.015875 + 0.011875 + 0.012625 + 0.011584 + 0.013542 + 0.012666 + 0.011542 + 0.012667 + 0.010833 + 0.010625 + 0.009792 + 0.008791 + 0.007459 + 0.007291 + 0.006833 + 0.00775 + 0.007041 + 0.010334 + 0.007333 + 0.00775 + 0.007375 + 0.00925 + 0.0065 + 0.018125 + 0.0065 + 0.008583 + 0.010875 + 0.007959 + 0.007958 + 0.007625 + 0.00775 + 0.007417 + 0.007583 + 0.00775 + 0.007458 + 0.007708 + 0.007958 + 0.008083 + 0.007291 + 0.007917 + 0.007916 + 0.010292 + 0.007375 + 0.008583 + 0.007791 + 0.00775 + 0.007792 + 0.011042 + 0.008042 + 0.008041 + 0.007667 + 0.007792 + 0.008166 + 0.007833 + 0.008 + 0.007917 + 0.007666 + 0.008125 + 0.007792 + 0.007917 + 0.007833 + 0.007584 + 0.008583 + 0.008292 + 0.008167 + 0.008 + 0.008209 + 0.008208 + 0.007667 + 0.008791 + 0.00825 + 0.00825 + 0.0085 + 0.010584 + 0.009875 + 0.009875 + 0.008292 + 0.008375 + 0.008083 + 0.008291 + 0.008292 + 0.007958 + 0.008458 + 0.008 + 0.008542 + 0.008125 + 0.008208 + 0.008167 + 0.008208 + 0.008042 + 0.00825 + 0.0085 + 0.008041 + 0.013209 + 0.010292 + 0.009875 + 0.009583 + 0.009583 + 0.008875 + 0.009083 + 0.009 + 0.011833 + 0.011 + 0.010167 + 0.009292 + 0.008792 + 0.008709 + 0.009041 + 0.013375 + 0.011166 + 0.010875 + 0.009125 + 0.0075 + 0.007541 + 0.007125 + 0.007291 + 0.006958 + 0.007083 + 0.007667 + 0.007958 + 0.007 + 0.009 + 0.009292 + 0.010083 + 0.006875 + 0.006458 + 0.008458 + 0.008583 + 0.008 + 0.008709 + 0.010042 + 0.007791 + 0.007959 + 0.007709 + 0.008458 + 0.008583 + 0.01025 + 0.010541 + 0.013 + 0.010708 + 0.009792 + 0.011042 + 0.01075 + 0.011375 + 0.009708 + 0.008167 + 0.009584 + 0.007667 + 0.00875 + 0.007916 + 0.008292 + 0.015833 + 0.008083 + 0.007667 + 0.007833 + 0.00775 + 0.010208 + 0.011875 + 0.011542 + 0.010084 + 0.009084 + 0.009834 + 0.010042 + 0.00875 + 0.008709 + 0.008667 + 0.0085 + 0.00825 + 0.007834 + 0.008583 + 0.008167 + 0.008125 + 0.008292 + 0.012958 + 0.017542 + 0.009125 + 0.008542 + 0.008667 + 0.009625 + 0.008042 + 0.008 + 0.008417 + 0.00825 + 0.008167 + 0.008333 + 0.008042 + 0.008458 + 0.00775 + 0.008542 + 0.00875 + 0.007917 + 0.008291 + 0.007833 + 0.01325 + 0.009208 + 0.008416 + 0.009125 + 0.008833 + 0.008583 + 0.008792 + 0.009208 + 0.012542 + 0.012291 + 0.011959 + 0.011959 + 0.011875 + 0.011917 + 0.011708 + 0.012916 + 0.01225 + 0.011875 + 0.011791 + 0.008042 + 0.009042 + 0.010125 + 0.010292 + 0.0095 + 0.011125 + 0.010209 + 0.010292 + 0.008875 + 0.011125 + 0.01175 + 0.006917 + 0.008208 + 0.007667 + 0.009125 + 0.009 + 0.008833 + 0.008708 + 0.009417 + 0.009209 + 0.008375 + 0.008792 + 0.008542 + 0.00975 + 0.011375 + 0.014583 + 0.010417 + 0.010208 + 0.016833 + 0.010084 + 0.00975 + 0.01 + 0.00925 + 0.008 + 0.007875 + 0.010792 + 0.018625 + 0.010041 + 0.011 + 0.011833 + 0.011375 + 0.009625 + 0.01025 + 0.00875 + 0.008916 + 0.010375 + 0.010708 + 0.010708 + 0.008667 + 0.009625 + 0.010875 + 0.009458 + 0.008417 + 0.007917 + 0.008208 + 0.008167 + 0.015792 + 0.008917 + 0.008375 + 0.01 + 0.009417 + 0.01075 + 0.010042 + 0.010041 + 0.009834 + 0.008375 + 0.008167 + 0.008667 + 0.00825 + 0.008084 + 0.008 + 0.008167 + 0.008208 + 0.008167 + 0.008542 + 0.00825 + 0.008125 + 0.008375 + 0.008167 + 0.008458 + 0.008583 + 0.013459 + 0.010125 + 0.010125 + 0.009208 + 0.018 + 0.009 + 0.00925 + 0.011375 + 0.01325 + 0.012292 + 0.011667 + 0.011125 + 0.010166 + 0.008917 + 0.009542 + 0.010333 + 0.011083 + 0.010958 + 0.009125 + 0.00875 + 0.008333 + 0.007125 + 0.007375 + 0.007375 + 0.007125 + 0.00725 + 0.007458 + 0.007208 + 0.007583 + 0.009167 + 0.006375 + 0.006417 + 0.00675 + 0.008042 + 0.007791 + 0.008208 + 0.007542 + 0.007916 + 0.007834 + 0.008084 + 0.0075 + 0.00825 + 0.007625 + 0.007459 + 0.007917 + 0.007917 + 0.007709 + 0.0075 + 0.007958 + 0.007791 + 0.007625 + 0.007875 + 0.007667 + 0.008084 + 0.008 + 0.00775 + 0.007584 + 0.00775 + 0.007625 + 0.007541 + 0.00775 + 0.008084 + 0.007958 + 0.008208 + 0.010375 + 0.010375 + 0.010041 + 0.010458 + 0.009125 + 0.007833 + 0.01375 + 0.00975 + 0.008334 + 0.008166 + 0.008459 + 0.008209 + 0.008459 + 0.008291 + 0.008542 + 0.008209 + 0.013584 + 0.010916 + 0.010542 + 0.0105 + 0.01 + 0.008375 + 0.007792 + 0.008708 + 0.0085 + 0.008417 + 0.008084 + 0.008291 + 0.008416 + 0.008625 + 0.00775 + 0.008125 + 0.00825 + 0.008125 + 0.008209 + 0.008375 + 0.008084 + 0.008166 + 0.008625 + 0.008625 + 0.013375 + 0.011542 + 0.0105 + 0.010334 + 0.0095 + 0.010041 + 0.0135 + 0.011333 + 0.009083 + 0.008666 + 0.009042 + 0.008583 + 0.008792 + 0.014583 + 0.011958 + 0.011625 + 0.009459 + 0.008208 + 0.006791 + 0.00725 + 0.006667 + 0.00725 + 0.007333 + 0.007625 + 0.007125 + 0.008292 + 0.007459 + 0.005875 + 0.006125 + 0.0085 + 0.008625 + 0.008084 + 0.007959 + 0.008208 + 0.007666 + 0.008042 + 0.007417 + 0.008 + 0.007417 + 0.007292 + 0.008334 + 0.008 + 0.007625 + 0.007458 + 0.00725 + 0.007875 + 0.008084 + 0.008125 + 0.007709 + 0.007709 + 0.016084 + 0.010542 + 0.033583 + 0.024083 + 0.016708 + 0.008375 + 0.008291 + 0.007875 + 0.01575 + 0.007875 + 0.008125 + 0.009666 + 0.010459 + 0.011 + 0.010458 + 0.010667 + 0.010834 + 0.008333 + 0.008291 + 0.008125 + 0.009916 + 0.009167 + 0.012792 + 0.00975 + 0.009042 + 0.008375 + 0.00825 + 0.008 + 0.008625 + 0.008292 + 0.008166 + 0.008708 + 0.008542 + 0.008417 + 0.007917 + 0.011458 + 0.010333 + 0.010125 + 0.008166 + 0.007958 + 0.008042 + 0.008209 + 0.00825 + 0.008 + 0.008042 + 0.00875 + 0.008167 + 0.008166 + 0.008334 + 0.008125 + 0.010542 + 0.011709 + 0.011167 + 0.010708 + 0.008958 + 0.008541 + 0.008625 + 0.008542 + 0.013 + 0.011083 + 0.010625 + 0.009084 + 0.009042 + 0.022416 + 0.011583 + 0.010791 + 0.008042 + 0.007458 + 0.007417 + 0.007291 + 0.007458 + 0.007083 + 0.008292 + 0.01 + 0.009 + 0.012083 + 0.009458 + 0.009375 + 0.006416 + 0.006542 + 0.007917 + 0.007834 + 0.007833 + 0.007791 + 0.007792 + 0.00775 + 0.007542 + 0.007875 + 0.00775 + 0.0075 + 0.008 + 0.007958 + 0.0075 + 0.008083 + 0.007417 + 0.007666 + 0.007709 + 0.007708 + 0.007792 + 0.007875 + 0.007709 + 0.007417 + 0.009583 + 0.009333 + 0.0095 + 0.007875 + 0.007458 + 0.008666 + 0.007458 + 0.007333 + 0.007875 + 0.0075 + 0.007459 + 0.007709 + 0.007959 + 0.007875 + 0.008167 + 0.007958 + 0.007875 + 0.007917 + 0.0085 + 0.007959 + 0.007916 + 0.008125 + 0.007792 + 0.008375 + 0.008334 + 0.007792 + 0.008125 + 0.008333 + 0.008083 + 0.008125 + 0.008209 + 0.0085 + 0.020417 + 0.010041 + 0.008667 + 0.009584 + 0.00975 + 0.010542 + 0.011375 + 0.011042 + 0.010791 + 0.010083 + 0.008917 + 0.008 + 0.00775 + 0.008042 + 0.007958 + 0.008333 + 0.009083 + 0.01275 + 0.010917 + 0.010125 + 0.008458 + 0.008375 + 0.008625 + 0.011625 + 0.009875 + 0.009333 + 0.00925 + 0.008958 + 0.009042 + 0.008959 + 0.012209 + 0.011667 + 0.009125 + 0.009209 + 0.009 + 0.007166 + 0.007084 + 0.006709 + 0.006875 + 0.007042 + 0.007083 + 0.007708 + 0.008917 + 0.006417 + 0.006083 + 0.006208 + 0.008167 + 0.007625 + 0.007792 + 0.007792 + 0.007791 + 0.007833 + 0.007708 + 0.007875 + 0.007667 + 0.00775 + 0.007708 + 0.008125 + 0.007334 + 0.007709 + 0.0075 + 0.007667 + 0.007541 + 0.007292 + 0.007708 + 0.007833 + 0.008083 + 0.010167 + 0.010125 + 0.01025 + 0.010209 + 0.008875 + 0.00775 + 0.008 + 0.0075 + 0.007667 + 0.007541 + 0.008042 + 0.007583 + 0.007708 + 0.00775 + 0.00775 + 0.009208 + 0.008 + 0.008334 + 0.008084 + 0.007917 + 0.007791 + 0.008458 + 0.008667 + 0.008708 + 0.008 + 0.007958 + 0.008166 + 0.008084 + 0.007625 + 0.008458 + 0.00925 + 0.008041 + 0.008375 + 0.00875 + 0.008166 + 0.008292 + 0.008333 + 0.01075 + 0.010583 + 0.011667 + 0.010959 + 0.009459 + 0.008209 + 0.0085 + 0.008083 + 0.008167 + 0.008292 + 0.008166 + 0.009458 + 0.012083 + 0.01175 + 0.012542 + 0.010708 + 0.009041 + 0.008667 + 0.00875 + 0.008667 + 0.008333 + 0.012333 + 0.0115 + 0.011875 + 0.011917 + 0.010333 + 0.01525 + 0.012 + 0.009667 + 0.009667 + 0.009834 + 0.0075 + 0.007625 + 0.007125 + 0.007125 + 0.006458 + 0.006791 + 0.006292 + 0.00975 + 0.007584 + 0.008125 + 0.0075 + 0.0095 + 0.009375 + 0.01025 + 0.0095 + 0.008958 + 0.008291 + 0.008792 + 0.00825 + 0.009834 + 0.010334 + 0.010041 + 0.009292 + 0.007958 + 0.007834 + 0.009208 + 0.009541 + 0.009667 + 0.00975 + 0.009083 + 0.009 + 0.009333 + 0.008875 + 0.011041 + 0.008084 + 0.009292 + 0.010291 + 0.009583 + 0.009709 + 0.009209 + 0.008583 + 0.013083 + 0.012 + 0.012083 + 0.011334 + 0.00875 + 0.008292 + 0.009584 + 0.009 + 0.011292 + 0.009916 + 0.008958 + 0.007959 + 0.008041 + 0.009375 + 0.008 + 0.009291 + 0.009333 + 0.009208 + 0.00825 + 0.0075 + 0.010125 + 0.010167 + 0.00925 + 0.008333 + 0.00825 + 0.008625 + 0.009792 + 0.0085 + 0.00775 + 0.007458 + 0.007375 + 0.008 + 0.008083 + 0.007833 + 0.007667 + 0.007417 + 0.007625 + 0.00725 + 0.007625 + 0.007667 + 0.00775 + 0.011375 + 0.010583 + 0.00975 + 0.008583 + 0.007875 + 0.008334 + 0.010666 + 0.009292 + 0.011292 + 0.009333 + 0.00875 + 0.00875 + 0.009042 + 0.011083 + 0.010666 + 0.010708 + 0.007625 + 0.007458 + 0.007042 + 0.007125 + 0.007625 + 0.007 + 0.007292 + 0.007209 + 0.007125 + 0.008584 + 0.008667 + 0.008834 + 0.006625 + 0.006208 + 0.008291 + 0.008125 + 0.007541 + 0.008083 + 0.007959 + 0.007667 + 0.008125 + 0.007583 + 0.0075 + 0.007875 + 0.008208 + 0.00775 + 0.007458 + 0.007792 + 0.007834 + 0.007667 + 0.007625 + 0.007875 + 0.007959 + 0.007709 + 0.007125 + 0.007792 + 0.007458 + 0.008042 + 0.007542 + 0.007416 + 0.007833 + 0.007917 + 0.008125 + 0.007625 + 0.007625 + 0.007958 + 0.007708 + 0.007875 + 0.0075 + 0.007708 + 0.008375 + 0.007958 + 0.008333 + 0.007917 + 0.008 + 0.008084 + 0.007667 + 0.008541 + 0.00825 + 0.008083 + 0.008416 + 0.00875 + 0.007917 + 0.008583 + 0.007875 + 0.008083 + 0.008125 + 0.008125 + 0.00775 + 0.010291 + 0.010583 + 0.012667 + 0.008375 + 0.007708 + 0.008417 + 0.008292 + 0.008333 + 0.008208 + 0.008166 + 0.008417 + 0.008 + 0.008334 + 0.008125 + 0.0085 + 0.009292 + 0.011666 + 0.011917 + 0.010917 + 0.008625 + 0.009083 + 0.008833 + 0.008375 + 0.008125 + 0.012167 + 0.011042 + 0.010625 + 0.009042 + 0.008791 + 0.009042 + 0.011458 + 0.008958 + 0.008375 + 0.008 + 0.007333 + 0.007166 + 0.007125 + 0.00725 + 0.00725 + 0.00675 + 0.008291 + 0.009 + 0.008208 + 0.006709 + 0.006625 + 0.00775 + 0.008209 + 0.007584 + 0.00725 + 0.007667 + 0.008084 + 0.008125 + 0.00775 + 0.007459 + 0.007584 + 0.007917 + 0.007291 + 0.008 + 0.008333 + 0.00775 + 0.007667 + 0.007916 + 0.008083 + 0.008083 + 0.007625 + 0.00775 + 0.007875 + 0.007667 + 0.007583 + 0.007958 + 0.007667 + 0.009209 + 0.008375 + 0.007666 + 0.007667 + 0.007792 + 0.007458 + 0.008125 + 0.008292 + 0.00725 + 0.0075 + 0.008334 + 0.008292 + 0.00875 + 0.008708 + 0.008 + 0.008084 + 0.008 + 0.00875 + 0.00825 + 0.008125 + 0.008709 + 0.008375 + 0.008 + 0.007958 + 0.008 + 0.008209 + 0.007958 + 0.008792 + 0.008041 + 0.008 + 0.008125 + 0.010542 + 0.00975 + 0.010042 + 0.008042 + 0.008458 + 0.008166 + 0.00825 + 0.008166 + 0.007709 + 0.00825 + 0.007917 + 0.00775 + 0.008166 + 0.009542 + 0.008667 + 0.011708 + 0.011042 + 0.010333 + 0.008291 + 0.009 + 0.008458 + 0.008458 + 0.011042 + 0.010958 + 0.010666 + 0.008708 + 0.00825 + 0.009 + 0.010792 + 0.008917 + 0.009042 + 0.0075 + 0.007083 + 0.007 + 0.006709 + 0.0075 + 0.007125 + 0.00725 + 0.0075 + 0.008541 + 0.005917 + 0.006416 + 0.0065 + 0.008 + 0.007875 + 0.007583 + 0.007625 + 0.007625 + 0.007625 + 0.007542 + 0.008 + 0.007167 + 0.007666 + 0.008 + 0.007542 + 0.007625 + 0.007833 + 0.00725 + 0.007625 + 0.007375 + 0.007417 + 0.007917 + 0.007791 + 0.007417 + 0.007625 + 0.008 + 0.00775 + 0.0075 + 0.007625 + 0.007625 + 0.007458 + 0.00725 + 0.007584 + 0.007792 + 0.007666 + 0.007708 + 0.00775 + 0.007583 + 0.007875 + 0.008208 + 0.008542 + 0.008208 + 0.008208 + 0.008125 + 0.007958 + 0.009834 + 0.00975 + 0.008875 + 0.00825 + 0.007916 + 0.007875 + 0.007833 + 0.008292 + 0.00825 + 0.00825 + 0.00775 + 0.008333 + 0.008041 + 0.007875 + 0.00775 + 0.007958 + 0.0075 + 0.008 + 0.008125 + 0.008458 + 0.010541 + 0.010083 + 0.010291 + 0.008334 + 0.008125 + 0.008292 + 0.008375 + 0.008375 + 0.008459 + 0.008667 + 0.008875 + 0.010541 + 0.019333 + 0.009833 + 0.008417 + 0.010209 + 0.009833 + 0.009208 + 0.012125 + 0.012875 + 0.011709 + 0.0115 + 0.009208 + 0.008458 + 0.011042 + 0.008792 + 0.008416 + 0.007667 + 0.007167 + 0.006875 + 0.007083 + 0.006542 + 0.007084 + 0.007167 + 0.0095 + 0.008375 + 0.009625 + 0.009084 + 0.011167 + 0.010833 + 0.010333 + 0.009 + 0.007625 + 0.007833 + 0.007625 + 0.007542 + 0.007584 + 0.007792 + 0.007416 + 0.008042 + 0.007541 + 0.007792 + 0.007583 + 0.008 + 0.008042 + 0.007625 + 0.007917 + 0.007834 + 0.007667 + 0.007916 + 0.0075 + 0.007458 + 0.007667 + 0.007125 + 0.007875 + 0.007583 + 0.007291 + 0.007667 + 0.007417 + 0.008 + 0.007625 + 0.007584 + 0.008459 + 0.007709 + 0.008375 + 0.008292 + 0.008333 + 0.015625 + 0.008416 + 0.00825 + 0.0085 + 0.008459 + 0.008042 + 0.008333 + 0.008167 + 0.00825 + 0.008959 + 0.008417 + 0.008208 + 0.007792 + 0.010417 + 0.010625 + 0.010625 + 0.01075 + 0.008917 + 0.008166 + 0.008084 + 0.007916 + 0.007792 + 0.00925 + 0.011334 + 0.010459 + 0.010791 + 0.010625 + 0.009958 + 0.008458 + 0.008041 + 0.008792 + 0.00825 + 0.009084 + 0.009084 + 0.009084 + 0.011625 + 0.01175 + 0.011541 + 0.011417 + 0.009125 + 0.009042 + 0.009083 + 0.0115 + 0.011459 + 0.01125 + 0.011875 + 0.011334 + 0.009042 + 0.007459 + 0.007333 + 0.007208 + 0.007541 + 0.007 + 0.007208 + 0.007083 + 0.007 + 0.006708 + 0.009167 + 0.009084 + 0.0085 + 0.006125 + 0.006458 + 0.007958 + 0.008416 + 0.007792 + 0.007375 + 0.008 + 0.007792 + 0.007666 + 0.009042 + 0.010334 + 0.01 + 0.009583 + 0.009917 + 0.007917 + 0.007666 + 0.007833 + 0.007375 + 0.007708 + 0.007833 + 0.007791 + 0.007792 + 0.008 + 0.007958 + 0.007625 + 0.007709 + 0.007875 + 0.007792 + 0.007458 + 0.00725 + 0.0075 + 0.007292 + 0.007791 + 0.008208 + 0.007792 + 0.007917 + 0.007875 + 0.008167 + 0.008125 + 0.008375 + 0.008042 + 0.007958 + 0.007958 + 0.008458 + 0.007709 + 0.008625 + 0.011834 + 0.011 + 0.010708 + 0.010792 + 0.009959 + 0.008459 + 0.008166 + 0.008458 + 0.008125 + 0.008125 + 0.008292 + 0.007834 + 0.00775 + 0.007917 + 0.008416 + 0.007708 + 0.008708 + 0.007875 + 0.008 + 0.008542 + 0.011333 + 0.011291 + 0.010625 + 0.010792 + 0.009875 + 0.008042 + 0.009 + 0.009208 + 0.008708 + 0.009208 + 0.012083 + 0.011791 + 0.011542 + 0.010834 + 0.009875 + 0.009334 + 0.009042 + 0.009041 + 0.01125 + 0.011708 + 0.011542 + 0.015375 + 0.008209 + 0.007 + 0.007375 + 0.007333 + 0.011875 + 0.00875 + 0.008542 + 0.007458 + 0.007208 + 0.008542 + 0.011791 + 0.009542 + 0.007167 + 0.007 + 0.009334 + 0.010042 + 0.009375 + 0.009292 + 0.009208 + 0.010042 + 0.02125 + 0.028208 + 0.010333 + 0.009708 + 0.008625 + 0.008167 + 0.008041 + 0.007792 + 0.007958 + 0.008083 + 0.008333 + 0.007667 + 0.008167 + 0.0075 + 0.008041 + 0.008 + 0.016666 + 0.009125 + 0.009667 + 0.008667 + 0.01425 + 0.012875 + 0.007875 + 0.007958 + 0.007791 + 0.018458 + 0.012167 + 0.009125 + 0.008333 + 0.007959 + 0.008708 + 0.009542 + 0.009458 + 0.008083 + 0.008292 + 0.008334 + 0.008625 + 0.009875 + 0.008917 + 0.010375 + 0.009625 + 0.009083 + 0.00925 + 0.013667 + 0.012292 + 0.014125 + 0.011792 + 0.012125 + 0.009125 + 0.008292 + 0.00875 + 0.008292 + 0.008625 + 0.008916 + 0.009417 + 0.009083 + 0.009 + 0.009958 + 0.010083 + 0.009083 + 0.008459 + 0.008833 + 0.00875 + 0.008625 + 0.015167 + 0.013042 + 0.013125 + 0.011959 + 0.010625 + 0.012625 + 0.015458 + 0.015125 + 0.011625 + 0.013208 + 0.00975 + 0.00925 + 0.009375 + 0.012292 + 0.011292 + 0.010792 + 0.007834 + 0.0075 + 0.006958 + 0.007125 + 0.007416 + 0.007834 + 0.007625 + 0.007292 + 0.007375 + 0.007791 + 0.009417 + 0.007125 + 0.006375 + 0.006667 + 0.008291 + 0.008167 + 0.008209 + 0.020459 + 0.008375 + 0.008542 + 0.007709 + 0.008 + 0.007833 + 0.008042 + 0.007959 + 0.007958 + 0.007875 + 0.007583 + 0.007791 + 0.00775 + 0.007875 + 0.008375 + 0.008584 + 0.008083 + 0.008042 + 0.008 + 0.007916 + 0.00775 + 0.008417 + 0.008125 + 0.007792 + 0.007958 + 0.008167 + 0.008416 + 0.00725 + 0.007916 + 0.007958 + 0.007625 + 0.007959 + 0.008042 + 0.0075 + 0.008209 + 0.00825 + 0.007875 + 0.013625 + 0.008625 + 0.008791 + 0.008708 + 0.008333 + 0.008666 + 0.00825 + 0.008083 + 0.008208 + 0.008583 + 0.008291 + 0.008292 + 0.008166 + 0.008167 + 0.008 + 0.011 + 0.009917 + 0.010125 + 0.008417 + 0.008167 + 0.008583 + 0.008458 + 0.008167 + 0.008375 + 0.008125 + 0.008125 + 0.00825 + 0.008125 + 0.008333 + 0.008458 + 0.007917 + 0.009125 + 0.011334 + 0.011375 + 0.010709 + 0.009 + 0.00875 + 0.008833 + 0.007833 + 0.011416 + 0.011458 + 0.010875 + 0.00925 + 0.008709 + 0.0085 + 0.010625 + 0.010541 + 0.008875 + 0.007916 + 0.007292 + 0.007167 + 0.007375 + 0.007583 + 0.006958 + 0.007083 + 0.007167 + 0.009 + 0.0065 + 0.00925 + 0.006333 + 0.009334 + 0.008541 + 0.00975 + 0.008459 + 0.008125 + 0.007667 + 0.007792 + 0.007958 + 0.008291 + 0.008167 + 0.007375 + 0.008167 + 0.00825 + 0.008208 + 0.007625 + 0.007625 + 0.007875 + 0.008334 + 0.008125 + 0.008125 + 0.008542 + 0.007833 + 0.008417 + 0.007959 + 0.008459 + 0.008083 + 0.008458 + 0.007875 + 0.008167 + 0.007958 + 0.007917 + 0.007709 + 0.00775 + 0.007875 + 0.007959 + 0.007709 + 0.00825 + 0.008584 + 0.008583 + 0.014833 + 0.00825 + 0.008416 + 0.008792 + 0.009542 + 0.01175 + 0.011417 + 0.010708 + 0.010625 + 0.009666 + 0.0085 + 0.008042 + 0.007916 + 0.008583 + 0.0075 + 0.008625 + 0.008 + 0.008291 + 0.008 + 0.007834 + 0.010375 + 0.010042 + 0.016792 + 0.008541 + 0.00825 + 0.008042 + 0.008334 + 0.008042 + 0.008 + 0.0085 + 0.008334 + 0.008167 + 0.009416 + 0.009375 + 0.011625 + 0.011 + 0.011125 + 0.008959 + 0.009042 + 0.008208 + 0.009584 + 0.011875 + 0.010625 + 0.013416 + 0.013 + 0.012 + 0.011625 + 0.01425 + 0.010166 + 0.010125 + 0.010333 + 0.010292 + 0.008208 + 0.007541 + 0.007375 + 0.008167 + 0.008917 + 0.010875 + 0.011958 + 0.009333 + 0.006875 + 0.007208 + 0.009833 + 0.00975 + 0.010625 + 0.055542 + 0.01325 + 0.00875 + 0.008959 + 0.008625 + 0.008792 + 0.008917 + 0.010708 + 0.012417 + 0.010417 + 0.011083 + 0.010958 + 0.011291 + 0.010709 + 0.009875 + 0.009125 + 0.010667 + 0.011625 + 0.01075 + 0.009333 + 0.012333 + 0.0105 + 0.011083 + 0.010333 + 0.010208 + 0.014042 + 0.012167 + 0.009625 + 0.011709 + 0.010875 + 0.0085 + 0.012209 + 0.008083 + 0.008458 + 0.012208 + 0.008083 + 0.009 + 0.011 + 0.014583 + 0.010291 + 0.011584 + 0.011542 + 0.009583 + 0.009833 + 0.010542 + 0.010125 + 0.009375 + 0.010208 + 0.010125 + 0.0085 + 0.010083 + 0.009667 + 0.01 + 0.0095 + 0.009042 + 0.008167 + 0.008666 + 0.008292 + 0.012084 + 0.011209 + 0.010042 + 0.009083 + 0.009167 + 0.009667 + 0.010625 + 0.01025 + 0.008583 + 0.010334 + 0.009125 + 0.00925 + 0.014375 + 0.011084 + 0.010667 + 0.009166 + 0.008958 + 0.008333 + 0.009041 + 0.014583 + 0.011167 + 0.01075 + 0.0095 + 0.009125 + 0.009042 + 0.006917 + 0.007625 + 0.007333 + 0.007208 + 0.007375 + 0.007458 + 0.007292 + 0.007334 + 0.0075 + 0.008 + 0.009 + 0.008792 + 0.006083 + 0.00625 + 0.008208 + 0.007875 + 0.007917 + 0.007791 + 0.007709 + 0.007959 + 0.007792 + 0.012875 + 0.007916 + 0.007708 + 0.008125 + 0.009875 + 0.008667 + 0.010292 + 0.010416 + 0.01 + 0.01025 + 0.010375 + 0.008667 + 0.007959 + 0.008 + 0.007833 + 0.007875 + 0.007833 + 0.007916 + 0.00875 + 0.009167 + 0.008833 + 0.008125 + 0.00775 + 0.008209 + 0.009458 + 0.00775 + 0.007833 + 0.008416 + 0.007875 + 0.013792 + 0.009083 + 0.008458 + 0.008625 + 0.008583 + 0.008333 + 0.009042 + 0.008958 + 0.011291 + 0.01125 + 0.011125 + 0.011291 + 0.0095 + 0.008125 + 0.0085 + 0.008959 + 0.008584 + 0.008834 + 0.0085 + 0.008167 + 0.008209 + 0.008291 + 0.008667 + 0.008875 + 0.008542 + 0.008333 + 0.008333 + 0.010834 + 0.0115 + 0.009625 + 0.009 + 0.00925 + 0.008458 + 0.008625 + 0.009709 + 0.009833 + 0.009416 + 0.009333 + 0.013084 + 0.012 + 0.011833 + 0.011916 + 0.010542 + 0.009084 + 0.009417 + 0.012125 + 0.012042 + 0.011583 + 0.012375 + 0.009917 + 0.007417 + 0.007125 + 0.006917 + 0.006959 + 0.007833 + 0.007583 + 0.007208 + 0.007583 + 0.007083 + 0.006875 + 0.009166 + 0.006584 + 0.006 + 0.006292 + 0.008583 + 0.00825 + 0.007959 + 0.007542 + 0.007958 + 0.008083 + 0.007625 + 0.008042 + 0.008125 + 0.007792 + 0.007666 + 0.00725 + 0.007416 + 0.007583 + 0.008125 + 0.007958 + 0.007875 + 0.007542 + 0.007709 + 0.007833 + 0.008 + 0.008209 + 0.007875 + 0.007625 + 0.008042 + 0.008042 + 0.008 + 0.008042 + 0.007917 + 0.008042 + 0.007708 + 0.008208 + 0.008041 + 0.007917 + 0.007625 + 0.007333 + 0.008208 + 0.008458 + 0.008042 + 0.008041 + 0.008208 + 0.008208 + 0.0085 + 0.008542 + 0.008167 + 0.008292 + 0.008458 + 0.011583 + 0.011125 + 0.010459 + 0.011584 + 0.009875 + 0.00875 + 0.014625 + 0.008334 + 0.010084 + 0.01125 + 0.0245 + 0.011583 + 0.018 + 0.008958 + 0.007792 + 0.008125 + 0.008542 + 0.015833 + 0.00875 + 0.009541 + 0.011917 + 0.012 + 0.010709 + 0.010709 + 0.011667 + 0.008791 + 0.008667 + 0.008875 + 0.008917 + 0.0125 + 0.010084 + 0.008292 + 0.009041 + 0.009125 + 0.008834 + 0.009167 + 0.013833 + 0.010583 + 0.010666 + 0.008792 + 0.007125 + 0.007042 + 0.0065 + 0.007166 + 0.007125 + 0.007083 + 0.007167 + 0.007792 + 0.007041 + 0.009209 + 0.005791 + 0.006166 + 0.005792 + 0.008416 + 0.007958 + 0.008 + 0.007583 + 0.007625 + 0.007834 + 0.007375 + 0.007291 + 0.007542 + 0.007541 + 0.0075 + 0.007333 + 0.007542 + 0.007542 + 0.0075 + 0.007625 + 0.00775 + 0.007666 + 0.007667 + 0.007916 + 0.007375 + 0.007583 + 0.007792 + 0.00775 + 0.007792 + 0.007417 + 0.018667 + 0.007791 + 0.009208 + 0.008834 + 0.008042 + 0.007834 + 0.008166 + 0.007792 + 0.010083 + 0.01025 + 0.010209 + 0.009334 + 0.008708 + 0.008708 + 0.008208 + 0.007958 + 0.008292 + 0.007792 + 0.008292 + 0.008292 + 0.007792 + 0.008209 + 0.00825 + 0.007625 + 0.009583 + 0.0115 + 0.011041 + 0.010792 + 0.010625 + 0.01075 + 0.008167 + 0.008167 + 0.008 + 0.008375 + 0.007875 + 0.00825 + 0.008125 + 0.00825 + 0.008042 + 0.008584 + 0.008375 + 0.008083 + 0.008083 + 0.00825 + 0.00825 + 0.013416 + 0.010667 + 0.0105 + 0.010709 + 0.008875 + 0.008834 + 0.010417 + 0.009958 + 0.018666 + 0.008792 + 0.009125 + 0.010417 + 0.010084 + 0.013084 + 0.011584 + 0.011709 + 0.009417 + 0.007833 + 0.007042 + 0.006666 + 0.007083 + 0.006708 + 0.006834 + 0.007334 + 0.006958 + 0.008417 + 0.008125 + 0.007875 + 0.00675 + 0.006167 + 0.00775 + 0.007791 + 0.007583 + 0.008667 + 0.009583 + 0.0105 + 0.010291 + 0.010209 + 0.01025 + 0.008458 + 0.007375 + 0.007292 + 0.007625 + 0.007709 + 0.007625 + 0.007417 + 0.008125 + 0.007667 + 0.007833 + 0.007916 + 0.008167 + 0.007458 + 0.007708 + 0.010625 + 0.007875 + 0.007458 + 0.007834 + 0.007584 + 0.007625 + 0.007917 + 0.008083 + 0.007792 + 0.007917 + 0.008042 + 0.007708 + 0.008125 + 0.008042 + 0.008917 + 0.008333 + 0.008292 + 0.007833 + 0.008125 + 0.008292 + 0.010833 + 0.010708 + 0.0105 + 0.010334 + 0.009042 + 0.008 + 0.007792 + 0.008125 + 0.008125 + 0.008 + 0.008291 + 0.013542 + 0.010875 + 0.01075 + 0.010958 + 0.009916 + 0.008167 + 0.008583 + 0.008333 + 0.008167 + 0.008125 + 0.007917 + 0.00825 + 0.008541 + 0.008333 + 0.006958 + 0.008167 + 0.009125 + 0.011375 + 0.011583 + 0.011667 + 0.011334 + 0.0095 + 0.008625 + 0.01125 + 0.011416 + 0.011917 + 0.011959 + 0.009959 + 0.008709 + 0.008708 + 0.01175 + 0.011834 + 0.009709 + 0.009042 + 0.00875 + 0.006917 + 0.007208 + 0.008625 + 0.009334 + 0.010541 + 0.0095 + 0.009916 + 0.01025 + 0.006041 + 0.00625 + 0.005792 + 0.007709 + 0.007709 + 0.007916 + 0.008084 + 0.007416 + 0.00775 + 0.007916 + 0.007875 + 0.008042 + 0.007542 + 0.007208 + 0.007792 + 0.007583 + 0.007708 + 0.007084 + 0.008167 + 0.007542 + 0.008125 + 0.008417 + 0.007958 + 0.014791 + 0.009958 + 0.00775 + 0.007667 + 0.009166 + 0.010125 + 0.01 + 0.008375 + 0.00775 + 0.007958 + 0.009208 + 0.009125 + 0.009375 + 0.011 + 0.010292 + 0.009875 + 0.010209 + 0.009167 + 0.010666 + 0.023167 + 0.02175 + 0.017666 + 0.009958 + 0.00975 + 0.008792 + 0.009291 + 0.00925 + 0.008583 + 0.008583 + 0.009375 + 0.009083 + 0.009541 + 0.008875 + 0.018125 + 0.009709 + 0.009958 + 0.01025 + 0.009417 + 0.012375 + 0.01175 + 0.018917 + 0.011416 + 0.010542 + 0.008791 + 0.008959 + 0.008792 + 0.008958 + 0.008625 + 0.009417 + 0.008667 + 0.010958 + 0.009375 + 0.014625 + 0.01125 + 0.011084 + 0.0095 + 0.008792 + 0.008791 + 0.008875 + 0.012375 + 0.011417 + 0.01075 + 0.009291 + 0.00875 + 0.009291 + 0.010959 + 0.008834 + 0.009 + 0.007542 + 0.007792 + 0.00725 + 0.013459 + 0.007791 + 0.008625 + 0.0085 + 0.007417 + 0.009625 + 0.00675 + 0.006667 + 0.007083 + 0.008 + 0.007917 + 0.007958 + 0.008042 + 0.008333 + 0.010167 + 0.0105 + 0.011 + 0.010833 + 0.009666 + 0.007917 + 0.007375 + 0.007625 + 0.00775 + 0.008333 + 0.007792 + 0.007875 + 0.007542 + 0.008292 + 0.007833 + 0.008083 + 0.008541 + 0.007958 + 0.008 + 0.008416 + 0.00775 + 0.007875 + 0.008041 + 0.007958 + 0.008 + 0.008041 + 0.007959 + 0.008083 + 0.008125 + 0.008 + 0.007959 + 0.007541 + 0.008209 + 0.008041 + 0.008083 + 0.008542 + 0.008291 + 0.008333 + 0.008625 + 0.008292 + 0.008084 + 0.008625 + 0.008208 + 0.008167 + 0.008584 + 0.008541 + 0.008333 + 0.007792 + 0.007708 + 0.008459 + 0.008292 + 0.008666 + 0.008375 + 0.008708 + 0.008292 + 0.008458 + 0.007791 + 0.008459 + 0.008416 + 0.013542 + 0.01025 + 0.0105 + 0.008917 + 0.007833 + 0.008542 + 0.00825 + 0.009542 + 0.009166 + 0.009125 + 0.008958 + 0.011083 + 0.010417 + 0.01075 + 0.008917 + 0.009209 + 0.008916 + 0.009 + 0.011 + 0.010667 + 0.011083 + 0.008916 + 0.008875 + 0.007833 + 0.007916 + 0.007292 + 0.007167 + 0.007 + 0.007375 + 0.007125 + 0.007917 + 0.0075 + 0.009083 + 0.007125 + 0.006667 + 0.007 + 0.00875 + 0.008917 + 0.008125 + 0.008709 + 0.00775 + 0.007625 + 0.009 + 0.008292 + 0.008 + 0.008416 + 0.007958 + 0.008333 + 0.008583 + 0.007708 + 0.009625 + 0.007875 + 0.008 + 0.007708 + 0.00825 + 0.008666 + 0.008125 + 0.008667 + 0.007792 + 0.008333 + 0.00725 + 0.008125 + 0.008042 + 0.014916 + 0.007542 + 0.007333 + 0.008542 + 0.009 + 0.010417 + 0.010667 + 0.010375 + 0.01 + 0.00975 + 0.008625 + 0.007875 + 0.008417 + 0.00825 + 0.008 + 0.008375 + 0.008084 + 0.008834 + 0.008167 + 0.00825 + 0.007917 + 0.013125 + 0.0175 + 0.008875 + 0.008083 + 0.008584 + 0.008083 + 0.008041 + 0.00825 + 0.008333 + 0.008375 + 0.008083 + 0.008583 + 0.008041 + 0.008167 + 0.00825 + 0.008333 + 0.007875 + 0.008042 + 0.008417 + 0.00825 + 0.013166 + 0.010167 + 0.009584 + 0.009708 + 0.009167 + 0.009083 + 0.008666 + 0.009208 + 0.010959 + 0.010458 + 0.010333 + 0.009333 + 0.009 + 0.008584 + 0.009291 + 0.011 + 0.010916 + 0.010375 + 0.009041 + 0.007209 + 0.007084 + 0.007417 + 0.007625 + 0.00725 + 0.00725 + 0.007 + 0.007625 + 0.007208 + 0.008917 + 0.008584 + 0.008708 + 0.010791 + 0.007084 + 0.008166 + 0.007625 + 0.007833 + 0.007583 + 0.007583 + 0.007791 + 0.008083 + 0.007542 + 0.007625 + 0.007458 + 0.007458 + 0.007958 + 0.0095 + 0.009292 + 0.007875 + 0.00775 + 0.007916 + 0.007584 + 0.0075 + 0.007666 + 0.007542 + 0.0075 + 0.007958 + 0.007875 + 0.007834 + 0.008041 + 0.007833 + 0.007833 + 0.007667 + 0.007625 + 0.007792 + 0.007625 + 0.007542 + 0.007667 + 0.00775 + 0.007541 + 0.008625 + 0.007958 + 0.00825 + 0.008041 + 0.007958 + 0.008 + 0.008 + 0.008625 + 0.007917 + 0.008125 + 0.007959 + 0.008125 + 0.008166 + 0.008167 + 0.010083 + 0.01025 + 0.009625 + 0.008209 + 0.008417 + 0.00825 + 0.007792 + 0.008208 + 0.007791 + 0.007792 + 0.008291 + 0.008208 + 0.008042 + 0.008208 + 0.008208 + 0.008333 + 0.00775 + 0.007916 + 0.008417 + 0.008334 + 0.014041 + 0.010583 + 0.010666 + 0.008583 + 0.008583 + 0.009 + 0.010292 + 0.011083 + 0.009875 + 0.0095 + 0.008834 + 0.008625 + 0.008333 + 0.01075 + 0.010958 + 0.010666 + 0.007 + 0.007417 + 0.006875 + 0.00725 + 0.006917 + 0.007167 + 0.006959 + 0.006916 + 0.006792 + 0.007084 + 0.008792 + 0.006166 + 0.00625 + 0.006083 + 0.00825 + 0.0075 + 0.007791 + 0.007292 + 0.007583 + 0.0075 + 0.0075 + 0.00725 + 0.007708 + 0.007667 + 0.007542 + 0.007458 + 0.00775 + 0.007541 + 0.007833 + 0.007667 + 0.007375 + 0.007375 + 0.007792 + 0.008875 + 0.009084 + 0.007209 + 0.010459 + 0.008666 + 0.010208 + 0.010792 + 0.009208 + 0.009583 + 0.011167 + 0.011417 + 0.009208 + 0.009291 + 0.00925 + 0.008958 + 0.008666 + 0.0105 + 0.011625 + 0.010792 + 0.008875 + 0.008667 + 0.009666 + 0.008417 + 0.009084 + 0.009625 + 0.009708 + 0.011 + 0.011625 + 0.011417 + 0.010791 + 0.009084 + 0.008833 + 0.008834 + 0.0085 + 0.007833 + 0.008 + 0.011375 + 0.010375 + 0.011792 + 0.011875 + 0.009583 + 0.011542 + 0.011708 + 0.010458 + 0.009333 + 0.009916 + 0.009583 + 0.009 + 0.00875 + 0.009291 + 0.009375 + 0.01075 + 0.012292 + 0.012333 + 0.012333 + 0.010375 + 0.01 + 0.017 + 0.013625 + 0.011 + 0.015833 + 0.011625 + 0.010417 + 0.010041 + 0.008875 + 0.015958 + 0.015875 + 0.012625 + 0.011125 + 0.0115 + 0.010709 + 0.009416 + 0.008292 + 0.008417 + 0.009708 + 0.008541 + 0.008209 + 0.012458 + 0.007125 + 0.008417 + 0.009125 + 0.009292 + 0.007917 + 0.007791 + 0.007875 + 0.007625 + 0.014417 + 0.012458 + 0.007958 + 0.00925 + 0.009459 + 0.00775 + 0.008083 + 0.00775 + 0.007792 + 0.007625 + 0.007917 + 0.007583 + 0.007625 + 0.008292 + 0.0075 + 0.007666 + 0.0075 + 0.007792 + 0.0075 + 0.00775 + 0.007417 + 0.007833 + 0.007875 + 0.008083 + 0.007666 + 0.007458 + 0.007958 + 0.007708 + 0.008167 + 0.008042 + 0.007625 + 0.007625 + 0.008125 + 0.008 + 0.007875 + 0.008 + 0.008084 + 0.008 + 0.008458 + 0.008333 + 0.008125 + 0.008583 + 0.008125 + 0.008083 + 0.008041 + 0.008542 + 0.008667 + 0.008125 + 0.007959 + 0.008334 + 0.008167 + 0.008333 + 0.008208 + 0.008167 + 0.010542 + 0.009833 + 0.009958 + 0.008542 + 0.008458 + 0.008 + 0.007875 + 0.007917 + 0.0085 + 0.00825 + 0.007834 + 0.007917 + 0.009291 + 0.008792 + 0.011417 + 0.010916 + 0.010917 + 0.009375 + 0.008458 + 0.008166 + 0.014334 + 0.00875 + 0.008583 + 0.008667 + 0.010583 + 0.011042 + 0.010833 + 0.011833 + 0.008167 + 0.006541 + 0.006417 + 0.006542 + 0.007 + 0.006917 + 0.006208 + 0.006792 + 0.006458 + 0.008333 + 0.008084 + 0.0085 + 0.005834 + 0.005958 + 0.007583 + 0.007042 + 0.006875 + 0.007042 + 0.007042 + 0.00725 + 0.007167 + 0.011292 + 0.007167 + 0.006875 + 0.007208 + 0.007291 + 0.007292 + 0.00725 + 0.007375 + 0.007125 + 0.008583 + 0.009708 + 0.00975 + 0.009542 + 0.009667 + 0.009708 + 0.00775 + 0.006833 + 0.007125 + 0.006958 + 0.00675 + 0.007542 + 0.0075 + 0.007667 + 0.007834 + 0.006916 + 0.006833 + 0.006792 + 0.007208 + 0.007125 + 0.007792 + 0.007708 + 0.007375 + 0.00775 + 0.007375 + 0.010959 + 0.007791 + 0.007541 + 0.008292 + 0.008 + 0.007958 + 0.008625 + 0.010125 + 0.009834 + 0.009625 + 0.009542 + 0.008209 + 0.008833 + 0.008958 + 0.008708 + 0.00725 + 0.007958 + 0.00775 + 0.013666 + 0.008084 + 0.00875 + 0.007916 + 0.007917 + 0.007458 + 0.007625 + 0.007667 + 0.007875 + 0.007625 + 0.007834 + 0.01275 + 0.011833 + 0.01075 + 0.012875 + 0.010792 + 0.010167 + 0.014292 + 0.01425 + 0.0105 + 0.012958 + 0.025 + 0.021708 + 0.010125 + 0.009792 + 0.01325 + 0.011125 + 0.010209 + 0.009959 + 0.011084 + 0.010459 + 0.010458 + 0.007875 + 0.007958 + 0.00775 + 0.008 + 0.010125 + 0.010208 + 0.010667 + 0.008458 + 0.008208 + 0.009292 + 0.008708 + 0.008625 + 0.008083 + 0.008875 + 0.008875 + 0.008625 + 0.007875 + 0.008334 + 0.008209 + 0.008291 + 0.00875 + 0.0085 + 0.008708 + 0.008542 + 0.007958 + 0.00825 + 0.008417 + 0.008333 + 0.0085 + 0.008458 + 0.008333 + 0.008917 + 0.008083 + 0.008542 + 0.008291 + 0.008 + 0.008333 + 0.00825 + 0.008125 + 0.008 + 0.007708 + 0.007958 + 0.008125 + 0.008125 + 0.007958 + 0.008583 + 0.008917 + 0.009084 + 0.008917 + 0.0085 + 0.018584 + 0.008583 + 0.010625 + 0.009916 + 0.008959 + 0.008834 + 0.008667 + 0.009958 + 0.010333 + 0.011 + 0.012916 + 0.011875 + 0.010333 + 0.008583 + 0.011125 + 0.011375 + 0.010834 + 0.011375 + 0.011333 + 0.011291 + 0.011917 + 0.010959 + 0.011167 + 0.010708 + 0.011416 + 0.011917 + 0.011166 + 0.010375 + 0.008917 + 0.009791 + 0.012375 + 0.012542 + 0.01125 + 0.010416 + 0.010417 + 0.010375 + 0.012917 + 0.011959 + 0.011167 + 0.010333 + 0.012417 + 0.009875 + 0.009667 + 0.013333 + 0.012916 + 0.009583 + 0.009167 + 0.009584 + 0.008292 + 0.007458 + 0.009625 + 0.009958 + 0.009625 + 0.010125 + 0.009917 + 0.010292 + 0.009041 + 0.00825 + 0.008458 + 0.010833 + 0.009416 + 0.010333 + 0.0095 + 0.008291 + 0.007834 + 0.007833 + 0.008458 + 0.008667 + 0.008583 + 0.008708 + 0.007875 + 0.008167 + 0.008291 + 0.0085 + 0.007708 + 0.00825 + 0.008375 + 0.008917 + 0.008125 + 0.00825 + 0.007791 + 0.008667 + 0.008166 + 0.00875 + 0.008042 + 0.0085 + 0.009292 + 0.008041 + 0.008167 + 0.008 + 0.008916 + 0.008875 + 0.008459 + 0.008416 + 0.007875 + 0.009292 + 0.00875 + 0.008708 + 0.008917 + 0.008458 + 0.008583 + 0.009292 + 0.008625 + 0.009 + 0.008917 + 0.008709 + 0.008042 + 0.008583 + 0.016042 + 0.016 + 0.008916 + 0.00975 + 0.011041 + 0.011417 + 0.011042 + 0.011375 + 0.010417 + 0.013709 + 0.010041 + 0.010292 + 0.008625 + 0.008167 + 0.008541 + 0.009167 + 0.008834 + 0.008667 + 0.008291 + 0.008667 + 0.008458 + 0.008958 + 0.009417 + 0.011917 + 0.011291 + 0.011167 + 0.009541 + 0.008833 + 0.009083 + 0.0085 + 0.014333 + 0.011458 + 0.01125 + 0.01 + 0.017125 + 0.009042 + 0.011875 + 0.010417 + 0.009125 + 0.008333 + 0.007375 + 0.00725 + 0.007667 + 0.009125 + 0.009042 + 0.009459 + 0.00975 + 0.011042 + 0.009333 + 0.013291 + 0.007167 + 0.007625 + 0.008125 + 0.009583 + 0.009708 + 0.010625 + 0.009125 + 0.008875 + 0.009042 + 0.008375 + 0.00775 + 0.009958 + 0.0105 + 0.010042 + 0.011041 + 0.011125 + 0.009333 + 0.009 + 0.008875 + 0.010791 + 0.010708 + 0.010416 + 0.011042 + 0.009584 + 0.008666 + 0.00825 + 0.009375 + 0.010208 + 0.010333 + 0.010791 + 0.011833 + 0.009292 + 0.008125 + 0.008 + 0.008042 + 0.010125 + 0.007917 + 0.00875 + 0.009125 + 0.008083 + 0.008334 + 0.00825 + 0.013375 + 0.009083 + 0.008167 + 0.008792 + 0.008375 + 0.008917 + 0.008375 + 0.008417 + 0.008292 + 0.011166 + 0.013125 + 0.010375 + 0.010042 + 0.00975 + 0.009084 + 0.0115 + 0.011292 + 0.011459 + 0.010792 + 0.010583 + 0.009291 + 0.008834 + 0.008584 + 0.008667 + 0.00925 + 0.010709 + 0.011792 + 0.010166 + 0.009958 + 0.009542 + 0.010292 + 0.01525 + 0.012875 + 0.012083 + 0.012042 + 0.010167 + 0.009291 + 0.008542 + 0.011625 + 0.011875 + 0.011875 + 0.011917 + 0.009291 + 0.020167 + 0.012041 + 0.011917 + 0.009584 + 0.009917 + 0.009125 + 0.007416 + 0.007375 + 0.0075 + 0.008542 + 0.00975 + 0.009083 + 0.010458 + 0.011917 + 0.010959 + 0.008834 + 0.007583 + 0.009667 + 0.010709 + 0.011084 + 0.008625 + 0.022792 + 0.085375 + 0.032834 + 0.009875 + 0.008209 + 0.008625 + 0.008166 + 0.008292 + 0.008041 + 0.008584 + 0.007916 + 0.007875 + 0.008417 + 0.008334 + 0.008458 + 0.008042 + 0.008333 + 0.007958 + 0.008 + 0.008625 + 0.008167 + 0.008125 + 0.008333 + 0.007917 + 0.007791 + 0.008083 + 0.007708 + 0.007334 + 0.008292 + 0.007791 + 0.007583 + 0.008083 + 0.009292 + 0.008542 + 0.008292 + 0.008375 + 0.008292 + 0.008542 + 0.009 + 0.01325 + 0.009208 + 0.008542 + 0.008708 + 0.008541 + 0.008708 + 0.008625 + 0.008583 + 0.009167 + 0.008542 + 0.01 + 0.008666 + 0.008708 + 0.0085 + 0.008542 + 0.016 + 0.009583 + 0.011292 + 0.010791 + 0.008375 + 0.008542 + 0.008416 + 0.024667 + 0.039959 + 0.031833 + 0.01125 + 0.010167 + 0.014709 + 0.012875 + 0.014958 + 0.013542 + 0.013125 + 0.04625 + 0.011667 + 0.020083 + 0.030625 + 0.01575 + 0.023 + 0.014625 + 0.015125 + 0.025333 + 0.056209 + 0.012084 + 0.00925 + 0.008333 + 0.008208 + 0.009291 + 0.008 + 0.008125 + 0.008292 + 0.017125 + 0.007375 + 0.010291 + 0.028 + 0.015542 + 0.009834 + 0.009542 + 0.012166 + 0.00975 + 0.009166 + 0.009167 + 0.008375 + 0.010125 + 0.009666 + 0.010167 + 0.009708 + 0.010416 + 0.010042 + 0.009833 + 0.008708 + 0.008667 + 0.007833 + 0.012042 + 0.009209 + 0.010417 + 0.009708 + 0.008167 + 0.008375 + 0.008167 + 0.008041 + 0.009208 + 0.007584 + 0.008166 + 0.008583 + 0.00875 + 0.008583 + 0.008458 + 0.008417 + 0.008166 + 0.008333 + 0.008458 + 0.00875 + 0.008375 + 0.010083 + 0.008625 + 0.00875 + 0.009416 + 0.0085 + 0.0085 + 0.008292 + 0.010166 + 0.008875 + 0.008917 + 0.008709 + 0.008791 + 0.008334 + 0.00925 + 0.01125 + 0.011125 + 0.01075 + 0.011209 + 0.00875 + 0.00925 + 0.009 + 0.00875 + 0.008541 + 0.008917 + 0.009875 + 0.009584 + 0.009291 + 0.008833 + 0.008417 + 0.009667 + 0.010417 + 0.010791 + 0.010792 + 0.011834 + 0.01425 + 0.011167 + 0.012625 + 0.011792 + 0.010041 + 0.011542 + 0.013917 + 0.012417 + 0.023333 + 0.012375 + 0.010541 + 0.010417 + 0.010542 + 0.012375 + 0.011 + 0.009584 + 0.008792 + 0.00825 + 0.007166 + 0.014208 + 0.007667 + 0.007625 + 0.007583 + 0.007542 + 0.006959 + 0.007583 + 0.009292 + 0.007083 + 0.00675 + 0.006542 + 0.008083 + 0.008333 + 0.008208 + 0.007666 + 0.0075 + 0.007708 + 0.007833 + 0.00825 + 0.008208 + 0.008333 + 0.007875 + 0.007834 + 0.007709 + 0.00775 + 0.008334 + 0.008041 + 0.008292 + 0.008458 + 0.008084 + 0.00775 + 0.008083 + 0.008083 + 0.008042 + 0.008584 + 0.008042 + 0.008333 + 0.007583 + 0.008125 + 0.007875 + 0.008333 + 0.008333 + 0.00825 + 0.008375 + 0.008375 + 0.008 + 0.008 + 0.013958 + 0.0085 + 0.008209 + 0.008417 + 0.008458 + 0.008542 + 0.008709 + 0.008417 + 0.008417 + 0.008292 + 0.00875 + 0.008833 + 0.008667 + 0.008834 + 0.007959 + 0.008625 + 0.008292 + 0.008542 + 0.008458 + 0.011542 + 0.010334 + 0.008875 + 0.008375 + 0.008125 + 0.00825 + 0.008417 + 0.008291 + 0.008792 + 0.00875 + 0.008417 + 0.008208 + 0.008583 + 0.008583 + 0.008833 + 0.007875 + 0.009833 + 0.012125 + 0.010917 + 0.011625 + 0.010125 + 0.009375 + 0.010167 + 0.010333 + 0.013208 + 0.012583 + 0.012125 + 0.012042 + 0.011709 + 0.012084 + 0.013125 + 0.011667 + 0.009375 + 0.009417 + 0.009625 + 0.009625 + 0.009958 + 0.009834 + 0.008583 + 0.008083 + 0.009583 + 0.010708 + 0.009375 + 0.009125 + 0.007667 + 0.011167 + 0.010541 + 0.0105 + 0.010625 + 0.009959 + 0.009417 + 0.008791 + 0.007792 + 0.007833 + 0.007708 + 0.009083 + 0.009625 + 0.00975 + 0.008459 + 0.007458 + 0.007583 + 0.008 + 0.008 + 0.007875 + 0.008584 + 0.008458 + 0.008416 + 0.007917 + 0.00825 + 0.008125 + 0.008084 + 0.008208 + 0.008166 + 0.007792 + 0.008791 + 0.00825 + 0.007583 + 0.007917 + 0.008333 + 0.007916 + 0.008083 + 0.00875 + 0.008584 + 0.0085 + 0.008334 + 0.009333 + 0.01 + 0.008084 + 0.0075 + 0.017375 + 0.008791 + 0.010167 + 0.00925 + 0.00975 + 0.011333 + 0.011083 + 0.010542 + 0.01175 + 0.009083 + 0.008167 + 0.008334 + 0.008417 + 0.008333 + 0.008375 + 0.010792 + 0.00975 + 0.010167 + 0.008916 + 0.008542 + 0.008583 + 0.00825 + 0.009666 + 0.010042 + 0.008541 + 0.013792 + 0.00875 + 0.009333 + 0.009458 + 0.012375 + 0.012417 + 0.012291 + 0.01225 + 0.011833 + 0.011167 + 0.012208 + 0.017333 + 0.011834 + 0.011792 + 0.010958 + 0.009208 + 0.009166 + 0.0145 + 0.009584 + 0.009459 + 0.008917 + 0.009333 + 0.007958 + 0.0075 + 0.007584 + 0.007959 + 0.007208 + 0.008416 + 0.00925 + 0.008458 + 0.006375 + 0.00675 + 0.008625 + 0.007708 + 0.008 + 0.007667 + 0.008292 + 0.008041 + 0.008083 + 0.007917 + 0.008125 + 0.008042 + 0.007875 + 0.008041 + 0.007875 + 0.007541 + 0.008083 + 0.007958 + 0.008584 + 0.007666 + 0.007875 + 0.008125 + 0.00825 + 0.007625 + 0.0085 + 0.008209 + 0.010292 + 0.010458 + 0.010666 + 0.010417 + 0.009209 + 0.007875 + 0.008 + 0.008417 + 0.008708 + 0.007583 + 0.008375 + 0.008709 + 0.009 + 0.008458 + 0.008083 + 0.00825 + 0.008375 + 0.008209 + 0.008292 + 0.009 + 0.008166 + 0.008167 + 0.008625 + 0.008542 + 0.008667 + 0.008583 + 0.008583 + 0.008666 + 0.008917 + 0.008292 + 0.008792 + 0.008375 + 0.008125 + 0.008417 + 0.008042 + 0.008375 + 0.008834 + 0.007959 + 0.012084 + 0.011167 + 0.011417 + 0.010916 + 0.009917 + 0.007916 + 0.008167 + 0.008208 + 0.008833 + 0.008958 + 0.00875 + 0.0115 + 0.012292 + 0.011666 + 0.011542 + 0.017292 + 0.008292 + 0.009166 + 0.011833 + 0.011791 + 0.014083 + 0.011916 + 0.009333 + 0.011958 + 0.009292 + 0.008084 + 0.007875 + 0.008167 + 0.009083 + 0.0095 + 0.009 + 0.009167 + 0.008959 + 0.008541 + 0.009 + 0.009375 + 0.005625 + 0.006042 + 0.008416 + 0.007709 + 0.00775 + 0.007791 + 0.007792 + 0.007542 + 0.007791 + 0.007334 + 0.007459 + 0.007792 + 0.007166 + 0.007583 + 0.007875 + 0.007416 + 0.007625 + 0.007875 + 0.008 + 0.007916 + 0.007875 + 0.007917 + 0.00825 + 0.007875 + 0.007541 + 0.008167 + 0.007625 + 0.008167 + 0.007625 + 0.008209 + 0.007875 + 0.007875 + 0.007625 + 0.007708 + 0.00825 + 0.008084 + 0.007708 + 0.007916 + 0.008333 + 0.00825 + 0.008125 + 0.008041 + 0.008292 + 0.007917 + 0.008333 + 0.008292 + 0.014083 + 0.011417 + 0.010709 + 0.010417 + 0.009791 + 0.008042 + 0.007875 + 0.00825 + 0.008459 + 0.008666 + 0.008208 + 0.00825 + 0.008291 + 0.007959 + 0.008041 + 0.008584 + 0.008041 + 0.008375 + 0.008875 + 0.008292 + 0.010625 + 0.010916 + 0.011042 + 0.016042 + 0.008833 + 0.008333 + 0.009125 + 0.008458 + 0.009042 + 0.008708 + 0.011375 + 0.012416 + 0.011917 + 0.012042 + 0.00925 + 0.009166 + 0.009 + 0.011458 + 0.012041 + 0.011625 + 0.011625 + 0.010917 + 0.007375 + 0.013916 + 0.022625 + 0.01325 + 0.008125 + 0.007458 + 0.007 + 0.007375 + 0.007416 + 0.007417 + 0.009042 + 0.006291 + 0.006084 + 0.006416 + 0.008333 + 0.007334 + 0.009416 + 0.009125 + 0.010542 + 0.010541 + 0.019292 + 0.010959 + 0.009791 + 0.008208 + 0.014708 + 0.007542 + 0.007625 + 0.007584 + 0.008333 + 0.0225 + 0.011875 + 0.011917 + 0.008041 + 0.008625 + 0.008125 + 0.007709 + 0.007917 + 0.008625 + 0.008291 + 0.008375 + 0.008792 + 0.008041 + 0.015375 + 0.008333 + 0.008875 + 0.008875 + 0.009166 + 0.018792 + 0.009333 + 0.009083 + 0.008292 + 0.008583 + 0.012458 + 0.009459 + 0.008708 + 0.008791 + 0.008875 + 0.00925 + 0.012166 + 0.010333 + 0.020375 + 0.009583 + 0.011 + 0.009833 + 0.008583 + 0.0095 + 0.008542 + 0.008416 + 0.016292 + 0.012166 + 0.012167 + 0.011625 + 0.010875 + 0.009292 + 0.009 + 0.008041 + 0.008875 + 0.00975 + 0.011708 + 0.011542 + 0.010042 + 0.009375 + 0.0095 + 0.010375 + 0.008917 + 0.011125 + 0.009958 + 0.009292 + 0.011708 + 0.013625 + 0.012708 + 0.012333 + 0.011458 + 0.011292 + 0.009917 + 0.009125 + 0.013 + 0.012209 + 0.0235 + 0.010375 + 0.0095 + 0.010583 + 0.010166 + 0.008667 + 0.008375 + 0.007708 + 0.008084 + 0.008166 + 0.009375 + 0.021125 + 0.012375 + 0.008458 + 0.008833 + 0.008708 + 0.011458 + 0.010458 + 0.010417 + 0.010583 + 0.00925 + 0.009083 + 0.009708 + 0.010458 + 0.0105 + 0.010208 + 0.010791 + 0.010417 + 0.009334 + 0.008541 + 0.008917 + 0.008208 + 0.008666 + 0.00925 + 0.009708 + 0.009667 + 0.008291 + 0.008292 + 0.008042 + 0.008208 + 0.007875 + 0.007875 + 0.007875 + 0.008041 + 0.008042 + 0.008166 + 0.008042 + 0.008208 + 0.008 + 0.008083 + 0.007583 + 0.008 + 0.008125 + 0.008583 + 0.008083 + 0.008042 + 0.00825 + 0.008375 + 0.008 + 0.008084 + 0.008709 + 0.008709 + 0.008417 + 0.0085 + 0.0105 + 0.011666 + 0.01025 + 0.00875 + 0.010167 + 0.009708 + 0.010333 + 0.008916 + 0.008334 + 0.008209 + 0.008417 + 0.008541 + 0.008083 + 0.00825 + 0.008041 + 0.007875 + 0.008375 + 0.0085 + 0.008875 + 0.010291 + 0.010667 + 0.010375 + 0.008375 + 0.00925 + 0.009417 + 0.008708 + 0.009125 + 0.009584 + 0.01225 + 0.016334 + 0.00975 + 0.009209 + 0.009041 + 0.010125 + 0.009208 + 0.015334 + 0.010833 + 0.011791 + 0.009333 + 0.007708 + 0.008125 + 0.007583 + 0.0175 + 0.00775 + 0.008084 + 0.008459 + 0.00875 + 0.007125 + 0.009167 + 0.007083 + 0.009125 + 0.006541 + 0.007209 + 0.008333 + 0.00825 + 0.009667 + 0.009917 + 0.010375 + 0.010417 + 0.008667 + 0.008834 + 0.009834 + 0.01025 + 0.010208 + 0.009167 + 0.008375 + 0.009167 + 0.010625 + 0.0115 + 0.026833 + 0.021 + 0.011334 + 0.009167 + 0.009125 + 0.010416 + 0.020333 + 0.011 + 0.011042 + 0.011166 + 0.01025 + 0.011583 + 0.011 + 0.011 + 0.01075 + 0.010916 + 0.010708 + 0.010792 + 0.010834 + 0.009584 + 0.008584 + 0.008083 + 0.008416 + 0.008791 + 0.008417 + 0.008375 + 0.008417 + 0.009541 + 0.009083 + 0.008333 + 0.00875 + 0.008541 + 0.008667 + 0.008375 + 0.015458 + 0.012042 + 0.011334 + 0.011709 + 0.01125 + 0.011958 + 0.010542 + 0.010125 + 0.009125 + 0.009458 + 0.009583 + 0.009208 + 0.008959 + 0.0085 + 0.0085 + 0.0085 + 0.009291 + 0.009333 + 0.011458 + 0.011458 + 0.014334 + 0.011875 + 0.010708 + 0.010333 + 0.00875 + 0.009417 + 0.012916 + 0.012458 + 0.012917 + 0.012541 + 0.010667 + 0.008958 + 0.009333 + 0.011833 + 0.012166 + 0.012541 + 0.010125 + 0.00875 + 0.007625 + 0.00775 + 0.007542 + 0.008291 + 0.010208 + 0.010333 + 0.009875 + 0.011958 + 0.013916 + 0.015958 + 0.017916 + 0.015334 + 0.044041 + 0.019667 + 0.010292 + 0.0105 + 0.0105 + 0.009792 + 0.010041 + 0.031875 + 0.011917 + 0.009958 + 0.038208 + 0.037958 + 0.010459 + 0.011 + 0.010875 + 0.011625 + 0.011084 + 0.009459 + 0.010583 + 0.010791 + 0.009708 + 0.009833 + 0.008875 + 0.008959 + 0.008666 + 0.010083 + 0.011166 + 0.009084 + 0.008709 + 0.008917 + 0.008916 + 0.008625 + 0.007833 + 0.008625 + 0.008125 + 0.008375 + 0.009541 + 0.008292 + 0.008583 + 0.008083 + 0.008333 + 0.00825 + 0.008833 + 0.010375 + 0.009833 + 0.011292 + 0.011792 + 0.011666 + 0.01125 + 0.011208 + 0.011583 + 0.009458 + 0.009583 + 0.013542 + 0.011416 + 0.01325 + 0.011208 + 0.011666 + 0.011417 + 0.009917 + 0.0115 + 0.010334 + 0.011 + 0.012417 + 0.021959 + 0.012042 + 0.010125 + 0.010375 + 0.010167 + 0.010666 + 0.013542 + 0.014583 + 0.01425 + 0.012166 + 0.011084 + 0.023083 + 0.012167 + 0.017791 + 0.012459 + 0.012584 + 0.010709 + 0.0115 + 0.011333 + 0.011292 + 0.013834 + 0.014125 + 0.010125 + 0.01 + 0.009416 + 0.008625 + 0.0095 + 0.008417 + 0.008375 + 0.008667 + 0.008416 + 0.007917 + 0.011833 + 0.007958 + 0.009167 + 0.009833 + 0.0115 + 0.010667 + 0.011375 + 0.0145 + 0.01175 + 0.012167 + 0.01175 + 0.016209 + 0.010125 + 0.009459 + 0.009209 + 0.012084 + 0.00975 + 0.010209 + 0.011416 + 0.010708 + 0.009875 + 0.009667 + 0.0085 + 0.008167 + 0.008208 + 0.0085 + 0.00825 + 0.008125 + 0.009334 + 0.011125 + 0.0105 + 0.010916 + 0.010917 + 0.009 + 0.00875 + 0.008666 + 0.0085 + 0.008625 + 0.008417 + 0.009 + 0.008709 + 0.008416 + 0.008792 + 0.008791 + 0.008375 + 0.009084 + 0.008584 + 0.010083 + 0.008833 + 0.009083 + 0.00925 + 0.00875 + 0.008708 + 0.008583 + 0.008417 + 0.01 + 0.008583 + 0.0085 + 0.009708 + 0.00925 + 0.009292 + 0.008583 + 0.008208 + 0.011667 + 0.011959 + 0.010917 + 0.012125 + 0.009625 + 0.013625 + 0.008875 + 0.008417 + 0.009041 + 0.009125 + 0.008917 + 0.008416 + 0.010292 + 0.014625 + 0.013208 + 0.01775 + 0.013167 + 0.012334 + 0.012167 + 0.012 + 0.012667 + 0.009625 + 0.016625 + 0.012333 + 0.01175 + 0.012291 + 0.01025 + 0.009333 + 0.008541 + 0.007375 + 0.007166 + 0.007333 + 0.007708 + 0.007583 + 0.007875 + 0.007458 + 0.008416 + 0.020125 + 0.006958 + 0.008875 + 0.017666 + 0.015458 + 0.012125 + 0.010791 + 0.016458 + 0.008583 + 0.008167 + 0.007916 + 0.016833 + 0.008167 + 0.009541 + 0.009959 + 0.010417 + 0.011083 + 0.010708 + 0.010625 + 0.010291 + 0.008334 + 0.007958 + 0.016291 + 0.00875 + 0.010375 + 0.009041 + 0.008208 + 0.007875 + 0.007792 + 0.008083 + 0.008167 + 0.008541 + 0.008209 + 0.008166 + 0.008084 + 0.007959 + 0.008 + 0.009 + 0.008042 + 0.008458 + 0.008375 + 0.009417 + 0.008583 + 0.008291 + 0.008167 + 0.008458 + 0.008459 + 0.008666 + 0.011417 + 0.010916 + 0.009417 + 0.008458 + 0.008375 + 0.00875 + 0.008709 + 0.008125 + 0.008917 + 0.008125 + 0.008417 + 0.008667 + 0.01125 + 0.0085 + 0.008917 + 0.00875 + 0.008083 + 0.008625 + 0.013458 + 0.00975 + 0.014583 + 0.008833 + 0.016708 + 0.010166 + 0.010042 + 0.010416 + 0.011417 + 0.012584 + 0.010291 + 0.012292 + 0.024291 + 0.115458 + 0.017792 + 0.010125 + 0.010333 + 0.011542 + 0.009375 + 0.021667 + 0.010959 + 0.0105 + 0.009458 + 0.009708 + 0.009292 + 0.008375 + 0.007125 + 0.008292 + 0.007583 + 0.007958 + 0.007875 + 0.007541 + 0.007625 + 0.007667 + 0.009375 + 0.009459 + 0.009417 + 0.006458 + 0.006708 + 0.01025 + 0.008541 + 0.008209 + 0.008292 + 0.007833 + 0.008417 + 0.008416 + 0.008375 + 0.00775 + 0.007917 + 0.00825 + 0.008125 + 0.0085 + 0.0085 + 0.008166 + 0.00825 + 0.008416 + 0.00825 + 0.008125 + 0.008208 + 0.008042 + 0.020875 + 0.008417 + 0.008916 + 0.008209 + 0.008208 + 0.008708 + 0.008291 + 0.008375 + 0.008458 + 0.0085 + 0.007834 + 0.008125 + 0.008584 + 0.008209 + 0.008292 + 0.010333 + 0.008333 + 0.00875 + 0.008542 + 0.008541 + 0.008375 + 0.008791 + 0.010625 + 0.014167 + 0.008625 + 0.009083 + 0.008458 + 0.008709 + 0.0085 + 0.008667 + 0.009708 + 0.008417 + 0.010083 + 0.008541 + 0.008667 + 0.015 + 0.00925 + 0.00875 + 0.008209 + 0.0095 + 0.010416 + 0.008792 + 0.00875 + 0.011 + 0.009292 + 0.009041 + 0.008667 + 0.008166 + 0.00975 + 0.009834 + 0.009209 + 0.009334 + 0.009667 + 0.011708 + 0.009375 + 0.008959 + 0.008917 + 0.008166 + 0.008917 + 0.00925 + 0.01275 + 0.009042 + 0.009291 + 0.00925 + 0.009292 + 0.007625 + 0.007375 + 0.0075 + 0.007458 + 0.007125 + 0.007541 + 0.00775 + 0.007375 + 0.007333 + 0.007583 + 0.009 + 0.006333 + 0.00625 + 0.005917 + 0.008083 + 0.007708 + 0.007917 + 0.008125 + 0.008458 + 0.008292 + 0.009291 + 0.008042 + 0.00775 + 0.019042 + 0.010708 + 0.007583 + 0.00825 + 0.009208 + 0.02025 + 0.008875 + 0.00825 + 0.008042 + 0.00875 + 0.008708 + 0.015917 + 0.007959 + 0.008166 + 0.008917 + 0.008417 + 0.007666 + 0.008625 + 0.008167 + 0.016709 + 0.016417 + 0.01 + 0.008708 + 0.008625 + 0.008833 + 0.009042 + 0.008416 + 0.010375 + 0.008542 + 0.00925 + 0.009 + 0.008459 + 0.012084 + 0.009166 + 0.009542 + 0.009333 + 0.0095 + 0.017042 + 0.012208 + 0.009333 + 0.008625 + 0.008708 + 0.016209 + 0.008958 + 0.009291 + 0.008458 + 0.00975 + 0.008458 + 0.008667 + 0.008958 + 0.008333 + 0.009333 + 0.009208 + 0.017125 + 0.016916 + 0.008792 + 0.00875 + 0.008583 + 0.00825 + 0.008125 + 0.00875 + 0.012333 + 0.009583 + 0.009042 + 0.009625 + 0.009292 + 0.00925 + 0.012208 + 0.009667 + 0.00875 + 0.009375 + 0.009833 + 0.00925 + 0.009209 + 0.011959 + 0.008917 + 0.008916 + 0.007583 + 0.007542 + 0.007625 + 0.007375 + 0.007708 + 0.007 + 0.0185 + 0.008167 + 0.0075 + 0.007208 + 0.008708 + 0.00675 + 0.006583 + 0.006125 + 0.007916 + 0.008542 + 0.007625 + 0.007791 + 0.00825 + 0.007625 + 0.008208 + 0.008 + 0.007458 + 0.007416 + 0.007917 + 0.00775 + 0.007708 + 0.008333 + 0.008 + 0.008417 + 0.007375 + 0.007875 + 0.007958 + 0.008042 + 0.008541 + 0.008125 + 0.008167 + 0.007875 + 0.008 + 0.008042 + 0.007667 + 0.007958 + 0.007916 + 0.007959 + 0.007959 + 0.008041 + 0.007875 + 0.008041 + 0.007792 + 0.007958 + 0.007917 + 0.008 + 0.008167 + 0.008791 + 0.008625 + 0.008542 + 0.008541 + 0.00875 + 0.008375 + 0.008583 + 0.008917 + 0.008625 + 0.008 + 0.008625 + 0.008458 + 0.008417 + 0.008125 + 0.007959 + 0.008791 + 0.014833 + 0.00875 + 0.00825 + 0.008541 + 0.007667 + 0.007541 + 0.010875 + 0.008417 + 0.008417 + 0.008208 + 0.008208 + 0.008375 + 0.008333 + 0.0085 + 0.008209 + 0.008375 + 0.009417 + 0.012833 + 0.0095 + 0.009083 + 0.008916 + 0.01775 + 0.009333 + 0.00875 + 0.012458 + 0.009417 + 0.008959 + 0.009041 + 0.008875 + 0.009042 + 0.011541 + 0.009167 + 0.007625 + 0.007417 + 0.007583 + 0.007583 + 0.00725 + 0.008291 + 0.007292 + 0.007541 + 0.007375 + 0.010583 + 0.009833 + 0.008958 + 0.006375 + 0.006667 + 0.00875 + 0.008541 + 0.008292 + 0.007833 + 0.008 + 0.007916 + 0.007917 + 0.007667 + 0.008 + 0.008417 + 0.007791 + 0.007959 + 0.009334 + 0.007708 + 0.007958 + 0.007166 + 0.01475 + 0.015667 + 0.008 + 0.007625 + 0.007917 + 0.007666 + 0.00825 + 0.007667 + 0.007542 + 0.007708 + 0.007875 + 0.00775 + 0.007709 + 0.007917 + 0.007333 + 0.007875 + 0.008041 + 0.008 + 0.007958 + 0.007583 + 0.008125 + 0.008166 + 0.008209 + 0.018042 + 0.008167 + 0.007625 + 0.008333 + 0.012625 + 0.008458 + 0.008208 + 0.008459 + 0.008167 + 0.008333 + 0.0085 + 0.007875 + 0.008 + 0.007916 + 0.008292 + 0.0085 + 0.00775 + 0.007708 + 0.008292 + 0.010959 + 0.008125 + 0.008375 + 0.008333 + 0.008708 + 0.008375 + 0.008 + 0.008542 + 0.007958 + 0.008208 + 0.008708 + 0.008167 + 0.00925 + 0.009125 + 0.011958 + 0.012417 + 0.008541 + 0.011125 + 0.010292 + 0.009416 + 0.009917 + 0.010667 + 0.009 + 0.008458 + 0.011083 + 0.011083 + 0.011041 + 0.011083 + 0.00875 + 0.009 + 0.009791 + 0.008334 + 0.007417 + 0.007333 + 0.007333 + 0.008333 + 0.008875 + 0.009583 + 0.011 + 0.010417 + 0.006625 + 0.006541 + 0.00825 + 0.007792 + 0.007916 + 0.008917 + 0.009833 + 0.010416 + 0.009583 + 0.007833 + 0.008125 + 0.01575 + 0.008959 + 0.00875 + 0.008459 + 0.007875 + 0.007917 + 0.009041 + 0.00825 + 0.007042 + 0.007417 + 0.007625 + 0.007625 + 0.007 + 0.007125 + 0.007375 + 0.007084 + 0.007209 + 0.007291 + 0.007125 + 0.00725 + 0.007125 + 0.00725 + 0.007 + 0.006916 + 0.0085 + 0.010083 + 0.010292 + 0.011125 + 0.01025 + 0.009916 + 0.010167 + 0.009 + 0.007667 + 0.007458 + 0.007708 + 0.008292 + 0.007792 + 0.00775 + 0.007334 + 0.007375 + 0.010542 + 0.010625 + 0.010417 + 0.010292 + 0.010458 + 0.00825 + 0.007583 + 0.007458 + 0.007542 + 0.007458 + 0.007375 + 0.007916 + 0.008667 + 0.0105 + 0.01025 + 0.011 + 0.010542 + 0.008833 + 0.008334 + 0.008292 + 0.008792 + 0.012583 + 0.012333 + 0.012334 + 0.012333 + 0.009708 + 0.009375 + 0.012625 + 0.012417 + 0.011291 + 0.012542 + 0.01025 + 0.009041 + 0.009042 + 0.012875 + 0.012333 + 0.012708 + 0.009708 + 0.008125 + 0.007666 + 0.007667 + 0.0075 + 0.008125 + 0.0075 + 0.008 + 0.007292 + 0.008 + 0.009834 + 0.007 + 0.006417 + 0.006208 + 0.008291 + 0.00825 + 0.008333 + 0.008291 + 0.00775 + 0.008167 + 0.007834 + 0.007917 + 0.008375 + 0.008667 + 0.00825 + 0.008084 + 0.008667 + 0.010833 + 0.01075 + 0.010167 + 0.011041 + 0.008917 + 0.008291 + 0.007958 + 0.00825 + 0.008 + 0.008542 + 0.008125 + 0.008375 + 0.007834 + 0.008667 + 0.008 + 0.008167 + 0.008208 + 0.008375 + 0.008084 + 0.008292 + 0.009083 + 0.008625 + 0.007959 + 0.0085 + 0.008667 + 0.008417 + 0.009083 + 0.008375 + 0.008417 + 0.009625 + 0.009042 + 0.010292 + 0.011709 + 0.011375 + 0.011542 + 0.011667 + 0.011625 + 0.009958 + 0.009625 + 0.009833 + 0.0105 + 0.010125 + 0.012375 + 0.011709 + 0.011875 + 0.01175 + 0.010792 + 0.009167 + 0.008667 + 0.00925 + 0.009583 + 0.008917 + 0.01025 + 0.009833 + 0.009583 + 0.011834 + 0.029541 + 0.020667 + 0.018209 + 0.012709 + 0.012209 + 0.010083 + 0.00975 + 0.009791 + 0.012208 + 0.011541 + 0.012083 + 0.010208 + 0.009208 + 0.009083 + 0.009708 + 0.012375 + 0.012875 + 0.011458 + 0.010666 + 0.009833 + 0.010834 + 0.010125 + 0.010041 + 0.010667 + 0.010084 + 0.008084 + 0.008541 + 0.010209 + 0.007 + 0.006792 + 0.007625 + 0.008708 + 0.008291 + 0.008167 + 0.008208 + 0.011958 + 0.008417 + 0.008541 + 0.008792 + 0.008291 + 0.008 + 0.008083 + 0.008541 + 0.008417 + 0.00875 + 0.008625 + 0.007667 + 0.008208 + 0.008042 + 0.008417 + 0.008333 + 0.008875 + 0.0085 + 0.008333 + 0.008125 + 0.008334 + 0.008542 + 0.008083 + 0.007916 + 0.008709 + 0.010709 + 0.011292 + 0.009375 + 0.009292 + 0.009166 + 0.009583 + 0.008625 + 0.009458 + 0.009792 + 0.009834 + 0.0095 + 0.009834 + 0.009084 + 0.009416 + 0.020542 + 0.014833 + 0.010541 + 0.009959 + 0.009542 + 0.009041 + 0.009375 + 0.009791 + 0.010208 + 0.011292 + 0.011541 + 0.010167 + 0.009416 + 0.011 + 0.009666 + 0.009292 + 0.009416 + 0.00925 + 0.009083 + 0.009792 + 0.009584 + 0.009667 + 0.012041 + 0.011667 + 0.010709 + 0.010292 + 0.014 + 0.019084 + 0.019125 + 0.015708 + 0.014583 + 0.012875 + 0.013042 + 0.012875 + 0.013333 + 0.011875 + 0.014375 + 0.012875 + 0.012458 + 0.012333 + 0.011375 + 0.009417 + 0.014833 + 0.011541 + 0.008959 + 0.008084 + 0.007584 + 0.007084 + 0.007583 + 0.00825 + 0.007791 + 0.008417 + 0.007084 + 0.010125 + 0.009792 + 0.010125 + 0.00675 + 0.007 + 0.009792 + 0.009208 + 0.008833 + 0.0085 + 0.008458 + 0.008375 + 0.00775 + 0.008416 + 0.008459 + 0.008208 + 0.008291 + 0.008 + 0.008458 + 0.008333 + 0.008375 + 0.008792 + 0.008958 + 0.00875 + 0.008333 + 0.008041 + 0.00825 + 0.008708 + 0.008625 + 0.008583 + 0.009333 + 0.009333 + 0.0085 + 0.009375 + 0.008166 + 0.008542 + 0.008 + 0.01675 + 0.0105 + 0.009291 + 0.009458 + 0.011 + 0.011958 + 0.01125 + 0.011167 + 0.0105 + 0.010875 + 0.0115 + 0.010791 + 0.010292 + 0.010166 + 0.011458 + 0.010708 + 0.009292 + 0.019208 + 0.010875 + 0.011 + 0.010833 + 0.011458 + 0.012 + 0.009416 + 0.01025 + 0.010125 + 0.008875 + 0.010792 + 0.01125 + 0.011625 + 0.011541 + 0.011375 + 0.01025 + 0.008958 + 0.00925 + 0.008625 + 0.008417 + 0.008541 + 0.007917 + 0.009416 + 0.009709 + 0.015125 + 0.011458 + 0.010875 + 0.01075 + 0.009875 + 0.008959 + 0.008333 + 0.009792 + 0.011125 + 0.011458 + 0.011375 + 0.009458 + 0.009625 + 0.00925 + 0.007375 + 0.007375 + 0.007666 + 0.007292 + 0.007208 + 0.007333 + 0.007667 + 0.008375 + 0.0085 + 0.00975 + 0.009083 + 0.009167 + 0.006666 + 0.006084 + 0.008333 + 0.008291 + 0.008083 + 0.012584 + 0.008833 + 0.008 + 0.008 + 0.007708 + 0.008125 + 0.007459 + 0.014292 + 0.010166 + 0.010083 + 0.008625 + 0.008291 + 0.008084 + 0.007667 + 0.008125 + 0.007917 + 0.008417 + 0.008083 + 0.008208 + 0.007833 + 0.007917 + 0.007958 + 0.008292 + 0.007792 + 0.008208 + 0.008417 + 0.008625 + 0.007917 + 0.008292 + 0.00825 + 0.007834 + 0.007667 + 0.013416 + 0.008833 + 0.008125 + 0.008791 + 0.00825 + 0.008666 + 0.008583 + 0.007917 + 0.008375 + 0.010167 + 0.009834 + 0.010709 + 0.010916 + 0.011083 + 0.010833 + 0.009666 + 0.010833 + 0.011 + 0.010417 + 0.00925 + 0.010833 + 0.011208 + 0.011041 + 0.01125 + 0.010791 + 0.00975 + 0.0095 + 0.011208 + 0.009708 + 0.009958 + 0.009875 + 0.010083 + 0.009375 + 0.008625 + 0.008292 + 0.009 + 0.008875 + 0.008875 + 0.0125 + 0.011333 + 0.011 + 0.009167 + 0.009125 + 0.008458 + 0.009375 + 0.011709 + 0.010875 + 0.011167 + 0.009834 + 0.009375 + 0.008834 + 0.007333 + 0.007458 + 0.007291 + 0.007209 + 0.007584 + 0.008375 + 0.008667 + 0.009 + 0.009125 + 0.014708 + 0.009792 + 0.007208 + 0.00825 + 0.010125 + 0.011541 + 0.010958 + 0.009875 + 0.009708 + 0.009916 + 0.008917 + 0.0095 + 0.008833 + 0.008833 + 0.010375 + 0.011375 + 0.011291 + 0.009042 + 0.009541 + 0.008917 + 0.009042 + 0.009625 + 0.011041 + 0.011417 + 0.010291 + 0.010833 + 0.009333 + 0.00875 + 0.008125 + 0.008459 + 0.007792 + 0.008 + 0.008125 + 0.008167 + 0.008083 + 0.009542 + 0.011 + 0.009875 + 0.009792 + 0.008542 + 0.008833 + 0.008833 + 0.008458 + 0.008875 + 0.013958 + 0.008375 + 0.008375 + 0.008541 + 0.008458 + 0.011542 + 0.011709 + 0.01175 + 0.011417 + 0.038541 + 0.01575 + 0.022167 + 0.011625 + 0.01075 + 0.039583 + 0.012709 + 0.009959 + 0.00975 + 0.011 + 0.010125 + 0.009708 + 0.010375 + 0.010542 + 0.0095 + 0.009375 + 0.012292 + 0.010417 + 0.011416 + 0.010375 + 0.010625 + 0.078125 + 0.01125 + 0.012333 + 0.012959 + 0.011959 + 0.010209 + 0.011334 + 0.010125 + 0.01025 + 0.009875 + 0.011708 + 0.010875 + 0.010875 + 0.012875 + 0.011083 + 0.010041 + 0.011167 + 0.011875 + 0.008584 + 0.008 + 0.008125 + 0.008084 + 0.007875 + 0.007291 + 0.007542 + 0.007042 + 0.008 + 0.010667 + 0.008917 + 0.009 + 0.006875 + 0.009458 + 0.008042 + 0.007791 + 0.008334 + 0.007875 + 0.008042 + 0.008125 + 0.00825 + 0.008084 + 0.007584 + 0.008042 + 0.008125 + 0.008792 + 0.008125 + 0.007958 + 0.010125 + 0.01125 + 0.010125 + 0.010792 + 0.009542 + 0.008208 + 0.008125 + 0.008334 + 0.007875 + 0.009042 + 0.008667 + 0.009625 + 0.011375 + 0.010875 + 0.011709 + 0.011709 + 0.011083 + 0.01075 + 0.01 + 0.011417 + 0.011458 + 0.01075 + 0.009584 + 0.009959 + 0.011459 + 0.012584 + 0.011542 + 0.009458 + 0.0105 + 0.0805 + 0.012958 + 0.010416 + 0.010333 + 0.012125 + 0.011083 + 0.010667 + 0.009125 + 0.010708 + 0.011333 + 0.011125 + 0.00875 + 0.011667 + 0.01175 + 0.010084 + 0.00925 + 0.009459 + 0.008792 + 0.009083 + 0.009334 + 0.012833 + 0.01325 + 0.013208 + 0.011125 + 0.013125 + 0.013125 + 0.011333 + 0.012166 + 0.01025 + 0.010541 + 0.011459 + 0.011167 + 0.013292 + 0.013583 + 0.011958 + 0.010208 + 0.009584 + 0.009792 + 0.009375 + 0.012208 + 0.012667 + 0.014 + 0.013458 + 0.010708 + 0.009959 + 0.00825 + 0.007958 + 0.008458 + 0.008083 + 0.007833 + 0.008167 + 0.007416 + 0.009542 + 0.008875 + 0.008667 + 0.006459 + 0.007042 + 0.009583 + 0.008875 + 0.007875 + 0.008209 + 0.008292 + 0.008208 + 0.0085 + 0.008541 + 0.007875 + 0.008375 + 0.008291 + 0.007792 + 0.008 + 0.008334 + 0.008292 + 0.008458 + 0.014083 + 0.008625 + 0.007458 + 0.008125 + 0.007875 + 0.008666 + 0.008375 + 0.00875 + 0.008125 + 0.00875 + 0.010166 + 0.010167 + 0.010916 + 0.010292 + 0.009459 + 0.009375 + 0.0105 + 0.010333 + 0.0105 + 0.009208 + 0.0095 + 0.008792 + 0.009916 + 0.011875 + 0.011583 + 0.010541 + 0.011125 + 0.011791 + 0.011417 + 0.010542 + 0.0115 + 0.01025 + 0.009875 + 0.008833 + 0.010958 + 0.010417 + 0.010625 + 0.009833 + 0.009125 + 0.008792 + 0.009 + 0.008416 + 0.00825 + 0.008041 + 0.008708 + 0.008375 + 0.00925 + 0.008875 + 0.008875 + 0.008208 + 0.008375 + 0.008833 + 0.008583 + 0.008125 + 0.01175 + 0.011917 + 0.011209 + 0.009417 + 0.008917 + 0.009084 + 0.01075 + 0.010875 + 0.010166 + 0.009708 + 0.009167 + 0.009333 + 0.008791 + 0.0115 + 0.011208 + 0.011 + 0.013792 + 0.008375 + 0.009167 + 0.007666 + 0.007209 + 0.00725 + 0.007417 + 0.007208 + 0.008042 + 0.008083 + 0.01125 + 0.011042 + 0.008667 + 0.008416 + 0.010583 + 0.009667 + 0.009792 + 0.009834 + 0.009792 + 0.008083 + 0.008041 + 0.007708 + 0.007875 + 0.007875 + 0.007875 + 0.008375 + 0.008334 + 0.008333 + 0.007625 + 0.008083 + 0.007667 + 0.00925 + 0.00875 + 0.008792 + 0.008208 + 0.008292 + 0.014666 + 0.008583 + 0.008292 + 0.007916 + 0.008292 + 0.008834 + 0.008875 + 0.009625 + 0.009417 + 0.0105 + 0.010791 + 0.010167 + 0.010917 + 0.010375 + 0.010959 + 0.01125 + 0.009167 + 0.00975 + 0.011291 + 0.010875 + 0.011167 + 0.012291 + 0.012042 + 0.011 + 0.009208 + 0.00925 + 0.010583 + 0.01425 + 0.011334 + 0.0115 + 0.011584 + 0.011209 + 0.010042 + 0.009166 + 0.008417 + 0.008208 + 0.008666 + 0.008833 + 0.008625 + 0.008458 + 0.008167 + 0.0085 + 0.008375 + 0.008833 + 0.009166 + 0.008708 + 0.008167 + 0.008917 + 0.013125 + 0.012459 + 0.011959 + 0.011625 + 0.0095 + 0.00975 + 0.012 + 0.012042 + 0.011583 + 0.011791 + 0.009917 + 0.009 + 0.00925 + 0.011917 + 0.012125 + 0.012083 + 0.009833 + 0.0085 + 0.007334 + 0.007166 + 0.007792 + 0.007375 + 0.007708 + 0.007208 + 0.007458 + 0.007333 + 0.00875 + 0.0065 + 0.006584 + 0.006709 + 0.008125 + 0.008375 + 0.007708 + 0.008042 + 0.008208 + 0.00775 + 0.008292 + 0.008458 + 0.009083 + 0.010625 + 0.010375 + 0.0105 + 0.010375 + 0.00875 + 0.007917 + 0.008042 + 0.007958 + 0.008292 + 0.008 + 0.008209 + 0.00875 + 0.008084 + 0.007666 + 0.008084 + 0.008292 + 0.007875 + 0.007917 + 0.008542 + 0.008291 + 0.007917 + 0.008208 + 0.008375 + 0.008417 + 0.008167 + 0.008459 + 0.008209 + 0.007875 + 0.008417 + 0.008958 + 0.008 + 0.008167 + 0.008542 + 0.008458 + 0.010416 + 0.011375 + 0.0105 + 0.01125 + 0.011125 + 0.01075 + 0.010708 + 0.0095 + 0.008792 + 0.008875 + 0.007625 + 0.008583 + 0.013833 + 0.0115 + 0.011 + 0.011083 + 0.010333 + 0.011792 + 0.009791 + 0.008834 + 0.009666 + 0.009417 + 0.008959 + 0.008292 + 0.008875 + 0.00825 + 0.0085 + 0.008208 + 0.011417 + 0.01275 + 0.010208 + 0.009917 + 0.009167 + 0.008583 + 0.008666 + 0.008584 + 0.011709 + 0.011417 + 0.011791 + 0.011833 + 0.010083 + 0.008667 + 0.011583 + 0.011625 + 0.009875 + 0.009333 + 0.009083 + 0.007375 + 0.00725 + 0.007208 + 0.007042 + 0.007292 + 0.0075 + 0.008542 + 0.006417 + 0.006625 + 0.006375 + 0.008708 + 0.008 + 0.008 + 0.008625 + 0.008084 + 0.007875 + 0.007958 + 0.007709 + 0.008666 + 0.007917 + 0.007541 + 0.008 + 0.008042 + 0.007791 + 0.008 + 0.007709 + 0.007291 + 0.007625 + 0.007708 + 0.007625 + 0.007666 + 0.008125 + 0.007917 + 0.008 + 0.007875 + 0.007667 + 0.007708 + 0.008166 + 0.007959 + 0.007667 + 0.007833 + 0.008458 + 0.008083 + 0.008167 + 0.007792 + 0.007916 + 0.00775 + 0.008625 + 0.007875 + 0.00825 + 0.007833 + 0.007958 + 0.007958 + 0.008542 + 0.008459 + 0.0085 + 0.008084 + 0.008666 + 0.00775 + 0.008292 + 0.00875 + 0.008459 + 0.00875 + 0.008167 + 0.008292 + 0.008791 + 0.009667 + 0.009875 + 0.010708 + 0.015125 + 0.010083 + 0.009208 + 0.008667 + 0.00875 + 0.008375 + 0.011583 + 0.010542 + 0.010292 + 0.008333 + 0.008667 + 0.008041 + 0.009209 + 0.009709 + 0.012041 + 0.01275 + 0.01225 + 0.011792 + 0.010125 + 0.009667 + 0.009792 + 0.011958 + 0.018083 + 0.012583 + 0.01175 + 0.010125 + 0.009875 + 0.012875 + 0.011208 + 0.010375 + 0.010208 + 0.009417 + 0.019958 + 0.010792 + 0.015084 + 0.009125 + 0.007959 + 0.010333 + 0.009416 + 0.01125 + 0.00675 + 0.00775 + 0.009167 + 0.007958 + 0.008 + 0.008541 + 0.016042 + 0.008916 + 0.008709 + 0.008709 + 0.008125 + 0.015334 + 0.009167 + 0.009417 + 0.01 + 0.008334 + 0.00925 + 0.007959 + 0.008833 + 0.008125 + 0.007792 + 0.008791 + 0.008625 + 0.008292 + 0.008708 + 0.008083 + 0.008041 + 0.00775 + 0.008 + 0.008 + 0.007958 + 0.007958 + 0.008125 + 0.008375 + 0.007792 + 0.007791 + 0.008084 + 0.008541 + 0.008917 + 0.00825 + 0.008333 + 0.008833 + 0.008542 + 0.00825 + 0.007958 + 0.008875 + 0.013333 + 0.00875 + 0.008666 + 0.008458 + 0.00825 + 0.008375 + 0.008541 + 0.009292 + 0.00825 + 0.008708 + 0.008917 + 0.0085 + 0.008166 + 0.008292 + 0.008583 + 0.009875 + 0.01125 + 0.011208 + 0.011417 + 0.00975 + 0.009 + 0.008833 + 0.011208 + 0.011 + 0.011375 + 0.01075 + 0.010542 + 0.009583 + 0.009166 + 0.012917 + 0.012375 + 0.012709 + 0.01225 + 0.011417 + 0.011959 + 0.012375 + 0.014167 + 0.012084 + 0.012084 + 0.010125 + 0.010041 + 0.010375 + 0.009042 + 0.008167 + 0.007667 + 0.007458 + 0.007416 + 0.007542 + 0.007625 + 0.007666 + 0.007584 + 0.008166 + 0.009 + 0.006667 + 0.007209 + 0.007709 + 0.011042 + 0.008958 + 0.009 + 0.008959 + 0.00975 + 0.009584 + 0.009834 + 0.010166 + 0.010792 + 0.010166 + 0.009417 + 0.009083 + 0.009333 + 0.009041 + 0.00825 + 0.008291 + 0.007958 + 0.007792 + 0.00825 + 0.00875 + 0.008459 + 0.008083 + 0.008083 + 0.008167 + 0.012417 + 0.010125 + 0.010125 + 0.009542 + 0.007834 + 0.008958 + 0.007667 + 0.008291 + 0.00775 + 0.008583 + 0.008875 + 0.008 + 0.008792 + 0.008708 + 0.008458 + 0.007958 + 0.008458 + 0.008625 + 0.010333 + 0.010041 + 0.008875 + 0.009583 + 0.008708 + 0.008583 + 0.009458 + 0.010292 + 0.014833 + 0.010625 + 0.008917 + 0.008792 + 0.008666 + 0.008667 + 0.008958 + 0.009792 + 0.009958 + 0.011625 + 0.010958 + 0.011916 + 0.009375 + 0.011209 + 0.01025 + 0.011667 + 0.010791 + 0.009417 + 0.009084 + 0.010542 + 0.012125 + 0.0125 + 0.009917 + 0.012959 + 0.01225 + 0.010709 + 0.010875 + 0.011375 + 0.009833 + 0.008916 + 0.011375 + 0.01125 + 0.010666 + 0.008833 + 0.008958 + 0.009 + 0.007041 + 0.007209 + 0.006834 + 0.007084 + 0.007334 + 0.007375 + 0.007208 + 0.007375 + 0.007375 + 0.007 + 0.009416 + 0.00625 + 0.006084 + 0.006459 + 0.008375 + 0.008 + 0.007791 + 0.007709 + 0.007709 + 0.007708 + 0.008125 + 0.00775 + 0.008417 + 0.007917 + 0.008041 + 0.007834 + 0.007541 + 0.00825 + 0.008041 + 0.007916 + 0.007875 + 0.007917 + 0.007625 + 0.009 + 0.009833 + 0.00775 + 0.007666 + 0.008209 + 0.007791 + 0.010459 + 0.010167 + 0.008958 + 0.0085 + 0.009 + 0.009083 + 0.011208 + 0.010833 + 0.010083 + 0.009459 + 0.009042 + 0.008625 + 0.009459 + 0.050125 + 0.015542 + 0.009375 + 0.009292 + 0.008791 + 0.010083 + 0.013416 + 0.0115 + 0.011583 + 0.010334 + 0.009334 + 0.008917 + 0.009667 + 0.009417 + 0.008292 + 0.007333 + 0.016791 + 0.00825 + 0.008708 + 0.008083 + 0.008375 + 0.0085 + 0.009625 + 0.01075 + 0.011458 + 0.010292 + 0.009125 + 0.009125 + 0.010291 + 0.009333 + 0.010083 + 0.011125 + 0.009875 + 0.012542 + 0.012125 + 0.0135 + 0.012084 + 0.016209 + 0.012292 + 0.012292 + 0.012041 + 0.01225 + 0.009958 + 0.010625 + 0.015166 + 0.012291 + 0.011292 + 0.010125 + 0.009875 + 0.008583 + 0.008083 + 0.00825 + 0.009041 + 0.009833 + 0.01075 + 0.010875 + 0.010417 + 0.009291 + 0.012333 + 0.008417 + 0.008459 + 0.008417 + 0.010125 + 0.010916 + 0.0095 + 0.009625 + 0.00975 + 0.009542 + 0.009959 + 0.009209 + 0.011166 + 0.011125 + 0.010833 + 0.0095 + 0.010292 + 0.009667 + 0.009666 + 0.010625 + 0.01125 + 0.011584 + 0.011375 + 0.038333 + 0.014542 + 0.010416 + 0.010375 + 0.011042 + 0.011666 + 0.010334 + 0.0105 + 0.009708 + 0.009833 + 0.010667 + 0.009417 + 0.009333 + 0.008708 + 0.009375 + 0.008792 + 0.009708 + 0.00975 + 0.009959 + 0.00925 + 0.009083 + 0.00875 + 0.008417 + 0.008417 + 0.00825 + 0.008958 + 0.008875 + 0.008667 + 0.008708 + 0.014375 + 0.01125 + 0.011084 + 0.010959 + 0.010417 + 0.008666 + 0.011125 + 0.011917 + 0.011292 + 0.018708 + 0.009167 + 0.008917 + 0.010083 + 0.018666 + 0.015584 + 0.010375 + 0.010417 + 0.009417 + 0.011541 + 0.042375 + 0.016458 + 0.013375 + 0.011125 + 0.0155 + 0.011958 + 0.011208 + 0.012833 + 0.013708 + 0.015042 + 0.0115 + 0.011834 + 0.011958 + 0.011208 + 0.010958 + 0.01075 + 0.012583 + 0.011666 + 0.011375 + 0.010375 + 0.008416 + 0.008417 + 0.009125 + 0.009209 + 0.009292 + 0.008833 + 0.00925 + 0.009167 + 0.009708 + 0.011833 + 0.013208 + 0.012541 + 0.008583 + 0.007875 + 0.011708 + 0.011291 + 0.01025 + 0.010416 + 0.010792 + 0.009625 + 0.009541 + 0.008708 + 0.011042 + 0.010166 + 0.009833 + 0.011209 + 0.011125 + 0.010292 + 0.010083 + 0.010625 + 0.008333 + 0.008708 + 0.011625 + 0.010708 + 0.01025 + 0.011083 + 0.0105 + 0.010333 + 0.010541 + 0.01 + 0.009167 + 0.009541 + 0.009834 + 0.010292 + 0.011375 + 0.009292 + 0.00975 + 0.011917 + 0.011916 + 0.014292 + 0.011417 + 0.010041 + 0.010167 + 0.010291 + 0.009833 + 0.012125 + 0.011417 + 0.01275 + 0.010667 + 0.009208 + 0.009375 + 0.011 + 0.011959 + 0.010834 + 0.013959 + 0.014125 + 0.011209 + 0.013542 + 0.009458 + 0.008916 + 0.011333 + 0.009 + 0.008833 + 0.008625 + 0.009875 + 0.009917 + 0.009542 + 0.008541 + 0.008458 + 0.009 + 0.0085 + 0.008833 + 0.008542 + 0.009208 + 0.015791 + 0.014333 + 0.012459 + 0.012208 + 0.01 + 0.009291 + 0.019333 + 0.011875 + 0.011583 + 0.012708 + 0.009625 + 0.0095 + 0.009 + 0.012375 + 0.012542 + 0.012291 + 0.010792 + 0.008 + 0.007458 + 0.007708 + 0.007417 + 0.007958 + 0.008083 + 0.008167 + 0.008166 + 0.007792 + 0.009334 + 0.007209 + 0.006833 + 0.006541 + 0.008167 + 0.0085 + 0.008666 + 0.008042 + 0.008375 + 0.008334 + 0.007875 + 0.008209 + 0.008875 + 0.010875 + 0.014959 + 0.011 + 0.019791 + 0.015 + 0.010625 + 0.010834 + 0.009917 + 0.008583 + 0.008625 + 0.008542 + 0.00775 + 0.015917 + 0.00825 + 0.009 + 0.01025 + 0.010292 + 0.010792 + 0.0115 + 0.010333 + 0.010583 + 0.00825 + 0.022458 + 0.008458 + 0.010167 + 0.009125 + 0.008125 + 0.009333 + 0.008416 + 0.008542 + 0.011333 + 0.011042 + 0.010125 + 0.011125 + 0.012125 + 0.011917 + 0.011458 + 0.010792 + 0.010375 + 0.010125 + 0.00975 + 0.008917 + 0.009208 + 0.008292 + 0.008625 + 0.010792 + 0.010291 + 0.010375 + 0.008916 + 0.008667 + 0.008875 + 0.008875 + 0.008709 + 0.009041 + 0.01075 + 0.010125 + 0.009917 + 0.008666 + 0.008584 + 0.01375 + 0.00875 + 0.012208 + 0.0125 + 0.013041 + 0.012292 + 0.012583 + 0.011125 + 0.011709 + 0.012625 + 0.012042 + 0.012291 + 0.011375 + 0.011083 + 0.012666 + 0.0145 + 0.012625 + 0.0125 + 0.008208 + 0.008917 + 0.010042 + 0.00975 + 0.008792 + 0.007959 + 0.008375 + 0.007958 + 0.007625 + 0.0075 + 0.008875 + 0.006791 + 0.006166 + 0.006875 + 0.008542 + 0.008291 + 0.008208 + 0.007792 + 0.010125 + 0.008584 + 0.008334 + 0.008334 + 0.007834 + 0.008292 + 0.007916 + 0.007917 + 0.008041 + 0.008083 + 0.007917 + 0.008667 + 0.00775 + 0.008041 + 0.0085 + 0.008292 + 0.00825 + 0.008583 + 0.008375 + 0.0085 + 0.008708 + 0.007875 + 0.007792 + 0.008792 + 0.008042 + 0.008584 + 0.008708 + 0.00825 + 0.007917 + 0.007959 + 0.00775 + 0.007583 + 0.013417 + 0.011583 + 0.011333 + 0.011959 + 0.011041 + 0.009 + 0.009083 + 0.008833 + 0.011416 + 0.0115 + 0.011541 + 0.009834 + 0.009334 + 0.009083 + 0.012041 + 0.011583 + 0.011083 + 0.010583 + 0.010166 + 0.008875 + 0.00825 + 0.0085 + 0.008291 + 0.008542 + 0.007792 + 0.008125 + 0.008375 + 0.008334 + 0.008416 + 0.008708 + 0.008333 + 0.008458 + 0.008625 + 0.00875 + 0.0085 + 0.012083 + 0.012291 + 0.012125 + 0.011833 + 0.009541 + 0.009125 + 0.011542 + 0.011542 + 0.012333 + 0.012333 + 0.009875 + 0.009292 + 0.008875 + 0.012 + 0.01225 + 0.011583 + 0.009959 + 0.008417 + 0.007208 + 0.007583 + 0.006917 + 0.007709 + 0.007209 + 0.007667 + 0.007166 + 0.008834 + 0.006542 + 0.008333 + 0.006792 + 0.007625 + 0.008375 + 0.008334 + 0.007917 + 0.007917 + 0.007875 + 0.007791 + 0.008292 + 0.008 + 0.008 + 0.008041 + 0.008042 + 0.007625 + 0.008209 + 0.008292 + 0.007958 + 0.007625 + 0.009584 + 0.008459 + 0.008667 + 0.007792 + 0.008125 + 0.0085 + 0.012958 + 0.008208 + 0.00875 + 0.008417 + 0.01625 + 0.008167 + 0.007959 + 0.009166 + 0.008625 + 0.008125 + 0.007833 + 0.008458 + 0.008208 + 0.00925 + 0.010334 + 0.011292 + 0.009666 + 0.008708 + 0.008292 + 0.017334 + 0.011917 + 0.021083 + 0.018959 + 0.011166 + 0.011209 + 0.009583 + 0.011458 + 0.011375 + 0.0115 + 0.0125 + 0.012 + 0.010709 + 0.009375 + 0.014083 + 0.024292 + 0.011583 + 0.011792 + 0.010792 + 0.0095 + 0.009917 + 0.009792 + 0.010041 + 0.009709 + 0.00975 + 0.009083 + 0.009084 + 0.008834 + 0.008625 + 0.009958 + 0.01475 + 0.011709 + 0.011 + 0.009084 + 0.009292 + 0.009917 + 0.008625 + 0.010625 + 0.011458 + 0.011292 + 0.009584 + 0.009292 + 0.009125 + 0.01075 + 0.011375 + 0.008708 + 0.007375 + 0.008042 + 0.007458 + 0.007041 + 0.007292 + 0.007084 + 0.007417 + 0.007125 + 0.008375 + 0.00875 + 0.020417 + 0.006458 + 0.008208 + 0.009 + 0.008125 + 0.007792 + 0.008042 + 0.009584 + 0.008208 + 0.010167 + 0.010875 + 0.010542 + 0.010292 + 0.009375 + 0.008167 + 0.008083 + 0.008125 + 0.008792 + 0.009417 + 0.010292 + 0.010709 + 0.01075 + 0.01075 + 0.0095 + 0.00825 + 0.00825 + 0.008 + 0.008125 + 0.007875 + 0.008 + 0.008167 + 0.008292 + 0.007875 + 0.0085 + 0.007875 + 0.010459 + 0.010459 + 0.00975 + 0.008792 + 0.009208 + 0.009667 + 0.009916 + 0.00975 + 0.0115 + 0.012167 + 0.009333 + 0.009416 + 0.010458 + 0.011125 + 0.010041 + 0.010042 + 0.010042 + 0.010083 + 0.010916 + 0.011333 + 0.010917 + 0.011167 + 0.011209 + 0.010834 + 0.009209 + 0.009541 + 0.015583 + 0.010875 + 0.011125 + 0.012 + 0.011292 + 0.010459 + 0.007958 + 0.00825 + 0.008042 + 0.008541 + 0.0125 + 0.007792 + 0.00975 + 0.008542 + 0.01375 + 0.011375 + 0.01125 + 0.011209 + 0.0095 + 0.0085 + 0.007958 + 0.011625 + 0.011666 + 0.011125 + 0.01075 + 0.011333 + 0.008208 + 0.011042 + 0.01 + 0.00925 + 0.009458 + 0.009125 + 0.006333 + 0.0065 + 0.006541 + 0.007708 + 0.008792 + 0.010334 + 0.010583 + 0.007667 + 0.007125 + 0.005791 + 0.007792 + 0.007125 + 0.0075 + 0.007 + 0.007625 + 0.007125 + 0.007 + 0.00675 + 0.007708 + 0.007666 + 0.007375 + 0.008083 + 0.007834 + 0.006875 + 0.006917 + 0.006791 + 0.006958 + 0.007333 + 0.007542 + 0.007375 + 0.007167 + 0.007208 + 0.007459 + 0.007209 + 0.007792 + 0.007041 + 0.007542 + 0.007209 + 0.00725 + 0.007375 + 0.007167 + 0.0075 + 0.007208 + 0.007042 + 0.007417 + 0.00725 + 0.007959 + 0.007542 + 0.007541 + 0.007542 + 0.008084 + 0.009041 + 0.009083 + 0.010208 + 0.008417 + 0.007417 + 0.008 + 0.007375 + 0.008041 + 0.008209 + 0.008209 + 0.009125 + 0.010708 + 0.012625 + 0.010917 + 0.010416 + 0.010417 + 0.009875 + 0.008709 + 0.0085 + 0.00925 + 0.007833 + 0.01075 + 0.011833 + 0.01175 + 0.009084 + 0.009458 + 0.00875 + 0.008959 + 0.009791 + 0.011 + 0.0105 + 0.013375 + 0.01275 + 0.012833 + 0.013042 + 0.009917 + 0.010416 + 0.008833 + 0.012167 + 0.012875 + 0.011584 + 0.009542 + 0.008708 + 0.009833 + 0.012542 + 0.009292 + 0.009666 + 0.017084 + 0.009125 + 0.008042 + 0.00725 + 0.007625 + 0.007583 + 0.007875 + 0.007875 + 0.009 + 0.006583 + 0.006667 + 0.007208 + 0.007875 + 0.008292 + 0.008041 + 0.008542 + 0.008541 + 0.007958 + 0.008333 + 0.009792 + 0.00925 + 0.008042 + 0.01025 + 0.011541 + 0.011542 + 0.010625 + 0.009208 + 0.008083 + 0.008333 + 0.011583 + 0.009709 + 0.0105 + 0.010583 + 0.011208 + 0.010458 + 0.008625 + 0.007917 + 0.007875 + 0.008208 + 0.00875 + 0.011 + 0.010834 + 0.011125 + 0.01025 + 0.009041 + 0.008208 + 0.008458 + 0.007875 + 0.008291 + 0.009167 + 0.011541 + 0.011417 + 0.009833 + 0.009416 + 0.00975 + 0.013583 + 0.011875 + 0.010166 + 0.009542 + 0.011416 + 0.0105 + 0.00925 + 0.008459 + 0.01 + 0.009583 + 0.008 + 0.009125 + 0.00875 + 0.00825 + 0.008959 + 0.0115 + 0.011083 + 0.010625 + 0.011166 + 0.011458 + 0.008958 + 0.008417 + 0.008167 + 0.008458 + 0.008417 + 0.008208 + 0.008583 + 0.00875 + 0.009416 + 0.009083 + 0.011417 + 0.011042 + 0.011041 + 0.009708 + 0.009583 + 0.009208 + 0.00925 + 0.01125 + 0.010834 + 0.010959 + 0.009375 + 0.009417 + 0.00925 + 0.011166 + 0.009042 + 0.00925 + 0.00775 + 0.007792 + 0.007 + 0.007667 + 0.00725 + 0.007791 + 0.007334 + 0.0085 + 0.00975 + 0.00975 + 0.006417 + 0.006666 + 0.009375 + 0.009041 + 0.009042 + 0.007875 + 0.007958 + 0.008208 + 0.008 + 0.007875 + 0.008417 + 0.008375 + 0.008042 + 0.007916 + 0.00825 + 0.008083 + 0.008459 + 0.008208 + 0.0085 + 0.008416 + 0.008458 + 0.008333 + 0.008042 + 0.008416 + 0.008375 + 0.009333 + 0.00875 + 0.008792 + 0.008417 + 0.008375 + 0.008583 + 0.008292 + 0.008208 + 0.008666 + 0.008417 + 0.008291 + 0.0085 + 0.007958 + 0.008417 + 0.008458 + 0.0085 + 0.008084 + 0.008334 + 0.008584 + 0.008417 + 0.0105 + 0.011042 + 0.011541 + 0.010834 + 0.011292 + 0.009708 + 0.0085 + 0.008458 + 0.008375 + 0.008416 + 0.008125 + 0.008083 + 0.008583 + 0.008917 + 0.008333 + 0.008291 + 0.008292 + 0.008458 + 0.010667 + 0.017667 + 0.010208 + 0.011375 + 0.011458 + 0.009667 + 0.008959 + 0.009 + 0.008209 + 0.009375 + 0.009209 + 0.008875 + 0.011083 + 0.010792 + 0.010875 + 0.009125 + 0.008708 + 0.008042 + 0.009416 + 0.009292 + 0.011041 + 0.010875 + 0.010875 + 0.0095 + 0.008875 + 0.007208 + 0.007292 + 0.007541 + 0.007042 + 0.006791 + 0.007584 + 0.007084 + 0.006791 + 0.007125 + 0.009208 + 0.009583 + 0.010125 + 0.006583 + 0.006583 + 0.008083 + 0.008042 + 0.00825 + 0.008041 + 0.008959 + 0.009833 + 0.009792 + 0.009708 + 0.009166 + 0.007875 + 0.007875 + 0.007708 + 0.007583 + 0.007667 + 0.00825 + 0.00775 + 0.00775 + 0.007917 + 0.007541 + 0.007708 + 0.007875 + 0.007833 + 0.007625 + 0.008083 + 0.008042 + 0.008167 + 0.007875 + 0.007959 + 0.007875 + 0.007792 + 0.007708 + 0.007708 + 0.008166 + 0.007875 + 0.00775 + 0.007709 + 0.008125 + 0.008333 + 0.008292 + 0.008375 + 0.008417 + 0.008041 + 0.007958 + 0.008208 + 0.008417 + 0.008417 + 0.008667 + 0.010834 + 0.010166 + 0.009667 + 0.00825 + 0.008375 + 0.00825 + 0.008666 + 0.008167 + 0.00825 + 0.007917 + 0.008292 + 0.007833 + 0.008083 + 0.008292 + 0.00825 + 0.008083 + 0.008458 + 0.007958 + 0.008166 + 0.007958 + 0.010708 + 0.00975 + 0.010084 + 0.009541 + 0.008792 + 0.008709 + 0.00875 + 0.008833 + 0.011583 + 0.011209 + 0.010792 + 0.008583 + 0.009042 + 0.009125 + 0.00875 + 0.011167 + 0.010917 + 0.010792 + 0.00875 + 0.007042 + 0.007 + 0.006667 + 0.00675 + 0.006916 + 0.006875 + 0.007 + 0.007542 + 0.007458 + 0.007625 + 0.009 + 0.006083 + 0.005833 + 0.006083 + 0.007667 + 0.007458 + 0.007667 + 0.007667 + 0.007583 + 0.007583 + 0.00775 + 0.00775 + 0.007792 + 0.012625 + 0.007916 + 0.007208 + 0.007542 + 0.007625 + 0.006875 + 0.008083 + 0.009375 + 0.008333 + 0.007958 + 0.007166 + 0.007917 + 0.008958 + 0.010125 + 0.010625 + 0.010459 + 0.009459 + 0.009291 + 0.009042 + 0.008833 + 0.009792 + 0.008791 + 0.008875 + 0.009292 + 0.0105 + 0.011125 + 0.010333 + 0.010791 + 0.009 + 0.009084 + 0.0095 + 0.009417 + 0.008833 + 0.00975 + 0.011583 + 0.012083 + 0.023583 + 0.0205 + 0.009917 + 0.009667 + 0.010333 + 0.013958 + 0.011167 + 0.009 + 0.012709 + 0.011334 + 0.012417 + 0.010208 + 0.011833 + 0.011083 + 0.011792 + 0.010083 + 0.01025 + 0.011041 + 0.012292 + 0.011792 + 0.01225 + 0.011334 + 0.01025 + 0.010542 + 0.010208 + 0.008916 + 0.015 + 0.012708 + 0.011041 + 0.01125 + 0.01275 + 0.012875 + 0.015916 + 0.011875 + 0.011459 + 0.013041 + 0.013333 + 0.011042 + 0.010792 + 0.013041 + 0.011875 + 0.010291 + 0.009542 + 0.00875 + 0.008917 + 0.011083 + 0.022334 + 0.011625 + 0.010833 + 0.011083 + 0.009208 + 0.012833 + 0.0125 + 0.011 + 0.009417 + 0.013042 + 0.009375 + 0.009167 + 0.009166 + 0.008417 + 0.008167 + 0.007959 + 0.008125 + 0.008125 + 0.008375 + 0.008459 + 0.00825 + 0.007875 + 0.007708 + 0.008084 + 0.00775 + 0.007667 + 0.008209 + 0.010375 + 0.008125 + 0.008041 + 0.008292 + 0.008291 + 0.008292 + 0.008125 + 0.008209 + 0.007792 + 0.009666 + 0.0085 + 0.007958 + 0.008333 + 0.007875 + 0.008416 + 0.009958 + 0.010708 + 0.0105 + 0.011667 + 0.011292 + 0.010458 + 0.009208 + 0.009125 + 0.009333 + 0.009542 + 0.0085 + 0.009459 + 0.009166 + 0.009625 + 0.011833 + 0.023042 + 0.013875 + 0.014208 + 0.012333 + 0.011625 + 0.010375 + 0.010459 + 0.012375 + 0.0115 + 0.011041 + 0.009458 + 0.008208 + 0.008459 + 0.008416 + 0.008834 + 0.008042 + 0.008916 + 0.011 + 0.011417 + 0.011584 + 0.010459 + 0.011917 + 0.010125 + 0.013792 + 0.010834 + 0.009542 + 0.009041 + 0.009375 + 0.012166 + 0.012875 + 0.009833 + 0.010333 + 0.011625 + 0.011875 + 0.012667 + 0.011 + 0.011917 + 0.011625 + 0.010584 + 0.008875 + 0.00975 + 0.009792 + 0.00975 + 0.009375 + 0.008667 + 0.009291 + 0.01 + 0.008417 + 0.010041 + 0.01 + 0.010333 + 0.007333 + 0.008125 + 0.010916 + 0.010708 + 0.010292 + 0.009542 + 0.010542 + 0.010625 + 0.008834 + 0.008292 + 0.009458 + 0.010458 + 0.011208 + 0.011334 + 0.010458 + 0.011417 + 0.010209 + 0.012584 + 0.010583 + 0.008833 + 0.009041 + 0.009417 + 0.008834 + 0.010166 + 0.00975 + 0.009583 + 0.011167 + 0.009958 + 0.008292 + 0.008084 + 0.008 + 0.008 + 0.008583 + 0.008375 + 0.009416 + 0.010083 + 0.01075 + 0.01125 + 0.010291 + 0.009292 + 0.011084 + 0.009042 + 0.008625 + 0.009583 + 0.011083 + 0.01175 + 0.009417 + 0.009541 + 0.011292 + 0.011667 + 0.011125 + 0.010583 + 0.012 + 0.010125 + 0.009334 + 0.010917 + 0.010791 + 0.011375 + 0.009958 + 0.010042 + 0.008542 + 0.008333 + 0.009 + 0.010791 + 0.009583 + 0.008833 + 0.009083 + 0.0085 + 0.008292 + 0.008792 + 0.008458 + 0.008208 + 0.009584 + 0.013417 + 0.011875 + 0.010584 + 0.00925 + 0.009042 + 0.00925 + 0.010375 + 0.010167 + 0.011667 + 0.00925 + 0.009292 + 0.00875 + 0.009 + 0.0115 + 0.01125 + 0.009125 + 0.008041 + 0.007458 + 0.007542 + 0.00725 + 0.007208 + 0.007334 + 0.00725 + 0.007833 + 0.007791 + 0.009667 + 0.008167 + 0.006458 + 0.009625 + 0.008375 + 0.007917 + 0.007791 + 0.008167 + 0.008625 + 0.008208 + 0.00825 + 0.008083 + 0.007916 + 0.008333 + 0.008208 + 0.008875 + 0.008709 + 0.0085 + 0.007792 + 0.008 + 0.007958 + 0.008208 + 0.008125 + 0.008208 + 0.007875 + 0.008292 + 0.007875 + 0.007667 + 0.008041 + 0.007584 + 0.008292 + 0.008417 + 0.008375 + 0.007917 + 0.007416 + 0.007834 + 0.009209 + 0.009584 + 0.008167 + 0.008625 + 0.008416 + 0.009042 + 0.009 + 0.008958 + 0.010333 + 0.009917 + 0.009584 + 0.009209 + 0.008917 + 0.008916 + 0.00825 + 0.008708 + 0.007958 + 0.008208 + 0.008083 + 0.008875 + 0.008959 + 0.00875 + 0.008125 + 0.008209 + 0.008834 + 0.007959 + 0.011042 + 0.010541 + 0.010042 + 0.010166 + 0.009208 + 0.008542 + 0.008458 + 0.008166 + 0.008708 + 0.010542 + 0.010125 + 0.009375 + 0.010208 + 0.009 + 0.013625 + 0.012083 + 0.011834 + 0.012125 + 0.012542 + 0.012375 + 0.009625 + 0.012666 + 0.010125 + 0.009125 + 0.00925 + 0.009417 + 0.010833 + 0.013041 + 0.009875 + 0.009834 + 0.009792 + 0.008875 + 0.009458 + 0.009459 + 0.008625 + 0.007417 + 0.00725 + 0.007417 + 0.009708 + 0.006875 + 0.006375 + 0.006583 + 0.008292 + 0.008291 + 0.008083 + 0.008542 + 0.00825 + 0.0085 + 0.007916 + 0.008709 + 0.008041 + 0.008292 + 0.007792 + 0.007833 + 0.00825 + 0.008458 + 0.007959 + 0.0085 + 0.008333 + 0.008875 + 0.008417 + 0.008125 + 0.007875 + 0.008042 + 0.008375 + 0.007792 + 0.008375 + 0.008125 + 0.007958 + 0.008166 + 0.007959 + 0.007625 + 0.008292 + 0.008375 + 0.008208 + 0.007917 + 0.009458 + 0.010542 + 0.009792 + 0.009041 + 0.009583 + 0.008833 + 0.009 + 0.009208 + 0.011291 + 0.010375 + 0.009625 + 0.00925 + 0.009 + 0.008708 + 0.010416 + 0.011 + 0.010875 + 0.011083 + 0.009625 + 0.008583 + 0.008791 + 0.008417 + 0.008416 + 0.0085 + 0.008291 + 0.008708 + 0.008709 + 0.007959 + 0.011292 + 0.01125 + 0.011167 + 0.011125 + 0.009584 + 0.008208 + 0.009083 + 0.008875 + 0.007916 + 0.009208 + 0.009209 + 0.008542 + 0.011625 + 0.012209 + 0.011959 + 0.011875 + 0.009417 + 0.009417 + 0.0095 + 0.012 + 0.012083 + 0.011833 + 0.0135 + 0.0095 + 0.012083 + 0.010042 + 0.00975 + 0.009084 + 0.01 + 0.007542 + 0.007542 + 0.00725 + 0.007584 + 0.007375 + 0.009458 + 0.006375 + 0.006416 + 0.006292 + 0.008709 + 0.007958 + 0.007916 + 0.009583 + 0.01075 + 0.010083 + 0.010625 + 0.009917 + 0.007667 + 0.008167 + 0.007458 + 0.007666 + 0.008125 + 0.007625 + 0.007416 + 0.008083 + 0.007625 + 0.008125 + 0.008041 + 0.008042 + 0.007541 + 0.008334 + 0.00775 + 0.007916 + 0.008042 + 0.008417 + 0.007708 + 0.00825 + 0.007709 + 0.007958 + 0.008292 + 0.007791 + 0.008084 + 0.007959 + 0.007834 + 0.008 + 0.008291 + 0.008709 + 0.008375 + 0.008417 + 0.008542 + 0.008 + 0.008042 + 0.007708 + 0.009209 + 0.008459 + 0.008125 + 0.008459 + 0.008292 + 0.008167 + 0.00825 + 0.007833 + 0.008542 + 0.008792 + 0.008167 + 0.00825 + 0.0085 + 0.010125 + 0.011584 + 0.011042 + 0.01075 + 0.01075 + 0.009916 + 0.008459 + 0.008 + 0.007917 + 0.008583 + 0.008708 + 0.009125 + 0.011 + 0.011625 + 0.012792 + 0.012959 + 0.012542 + 0.01175 + 0.009209 + 0.009084 + 0.009125 + 0.008417 + 0.009208 + 0.011666 + 0.011667 + 0.011542 + 0.012041 + 0.009375 + 0.008958 + 0.011916 + 0.009959 + 0.009334 + 0.013542 + 0.009041 + 0.007583 + 0.007417 + 0.007125 + 0.008375 + 0.007792 + 0.009625 + 0.009208 + 0.010125 + 0.007125 + 0.007125 + 0.008417 + 0.008083 + 0.008583 + 0.008708 + 0.008125 + 0.008083 + 0.007958 + 0.008292 + 0.008666 + 0.007667 + 0.008333 + 0.008291 + 0.008209 + 0.009084 + 0.009292 + 0.008917 + 0.008542 + 0.008333 + 0.010125 + 0.01075 + 0.022208 + 0.014125 + 0.011667 + 0.00925 + 0.008459 + 0.008708 + 0.008292 + 0.007833 + 0.008 + 0.00825 + 0.008583 + 0.008042 + 0.008208 + 0.007833 + 0.007959 + 0.008875 + 0.009625 + 0.010459 + 0.009416 + 0.008875 + 0.008958 + 0.008917 + 0.011166 + 0.012959 + 0.010542 + 0.009542 + 0.01 + 0.009959 + 0.009917 + 0.009458 + 0.009125 + 0.009167 + 0.008375 + 0.00875 + 0.00875 + 0.008458 + 0.007833 + 0.008375 + 0.008291 + 0.00825 + 0.00875 + 0.008709 + 0.011125 + 0.010708 + 0.010042 + 0.008459 + 0.008417 + 0.008291 + 0.008625 + 0.008292 + 0.01225 + 0.0105 + 0.011625 + 0.012916 + 0.012083 + 0.011667 + 0.010709 + 0.009709 + 0.008667 + 0.009125 + 0.010875 + 0.011083 + 0.010625 + 0.010834 + 0.011791 + 0.012292 + 0.008708 + 0.008458 + 0.008208 + 0.008417 + 0.009583 + 0.0095 + 0.008125 + 0.0075 + 0.007583 + 0.00825 + 0.009875 + 0.006917 + 0.00675 + 0.006042 + 0.008417 + 0.00825 + 0.010166 + 0.0105 + 0.010875 + 0.010167 + 0.009292 + 0.008334 + 0.00825 + 0.010209 + 0.010375 + 0.0095 + 0.009125 + 0.009125 + 0.008083 + 0.008166 + 0.008084 + 0.008583 + 0.010416 + 0.010583 + 0.0105 + 0.008875 + 0.008875 + 0.00875 + 0.010417 + 0.011084 + 0.009709 + 0.009417 + 0.0085 + 0.007959 + 0.008166 + 0.009292 + 0.0085 + 0.008375 + 0.008125 + 0.00825 + 0.009375 + 0.010166 + 0.008708 + 0.008583 + 0.008667 + 0.008208 + 0.008541 + 0.008625 + 0.011167 + 0.0105 + 0.010042 + 0.007958 + 0.008625 + 0.008167 + 0.0085 + 0.008542 + 0.008709 + 0.0085 + 0.008167 + 0.008541 + 0.008458 + 0.008042 + 0.008459 + 0.008209 + 0.009 + 0.00875 + 0.00825 + 0.008417 + 0.01075 + 0.010792 + 0.010709 + 0.008375 + 0.008709 + 0.008542 + 0.0095 + 0.0095 + 0.009375 + 0.009792 + 0.011791 + 0.011292 + 0.01025 + 0.009875 + 0.008833 + 0.009791 + 0.009417 + 0.012083 + 0.01175 + 0.011458 + 0.012417 + 0.012084 + 0.008125 + 0.007958 + 0.008042 + 0.008667 + 0.016167 + 0.009334 + 0.009542 + 0.009583 + 0.010625 + 0.010209 + 0.011833 + 0.007167 + 0.006625 + 0.0065 + 0.00825 + 0.008 + 0.007875 + 0.007958 + 0.008375 + 0.008125 + 0.008 + 0.008125 + 0.008125 + 0.007792 + 0.007541 + 0.007917 + 0.007792 + 0.007791 + 0.008042 + 0.007875 + 0.008208 + 0.007875 + 0.0085 + 0.008083 + 0.008375 + 0.007958 + 0.008 + 0.00775 + 0.008167 + 0.008208 + 0.007958 + 0.008041 + 0.0075 + 0.007875 + 0.007792 + 0.007958 + 0.007792 + 0.00775 + 0.007875 + 0.008125 + 0.007375 + 0.009 + 0.008375 + 0.00825 + 0.008292 + 0.008708 + 0.009958 + 0.009666 + 0.009042 + 0.009666 + 0.011291 + 0.01225 + 0.01175 + 0.010292 + 0.009208 + 0.011166 + 0.011333 + 0.010167 + 0.011375 + 0.011042 + 0.01075 + 0.010792 + 0.011 + 0.009625 + 0.0095 + 0.010083 + 0.011667 + 0.012125 + 0.011292 + 0.011583 + 0.011083 + 0.008791 + 0.009542 + 0.011334 + 0.011 + 0.014292 + 0.012625 + 0.0095 + 0.009375 + 0.009792 + 0.009042 + 0.012375 + 0.014583 + 0.0115 + 0.011125 + 0.010042 + 0.01075 + 0.012417 + 0.012458 + 0.059791 + 0.020792 + 0.008917 + 0.008375 + 0.0085 + 0.008333 + 0.008666 + 0.007791 + 0.008625 + 0.008125 + 0.008125 + 0.010042 + 0.00725 + 0.007167 + 0.007042 + 0.009 + 0.008459 + 0.00825 + 0.008792 + 0.008917 + 0.008791 + 0.008958 + 0.010875 + 0.011083 + 0.0095 + 0.01025 + 0.011125 + 0.0215 + 0.012916 + 0.01175 + 0.011792 + 0.0095 + 0.008875 + 0.008917 + 0.008792 + 0.007958 + 0.00825 + 0.00775 + 0.010083 + 0.010875 + 0.010666 + 0.010458 + 0.009542 + 0.007958 + 0.008125 + 0.007958 + 0.009667 + 0.014709 + 0.010334 + 0.009916 + 0.009792 + 0.009208 + 0.010916 + 0.010042 + 0.010041 + 0.010375 + 0.010375 + 0.009791 + 0.010375 + 0.013 + 0.012875 + 0.011333 + 0.012875 + 0.011875 + 0.010375 + 0.010584 + 0.009917 + 0.011 + 0.01175 + 0.013834 + 0.049334 + 0.016209 + 0.010667 + 0.010458 + 0.009417 + 0.009583 + 0.009792 + 0.011125 + 0.010042 + 0.010084 + 0.009792 + 0.009542 + 0.030416 + 0.010375 + 0.010833 + 0.00925 + 0.013041 + 0.010459 + 0.009792 + 0.010209 + 0.009084 + 0.009209 + 0.011791 + 0.011667 + 0.012209 + 0.01225 + 0.010417 + 0.009208 + 0.009083 + 0.012541 + 0.012292 + 0.011625 + 0.01025 + 0.009042 + 0.0075 + 0.007792 + 0.007417 + 0.007541 + 0.007459 + 0.007416 + 0.007375 + 0.008917 + 0.009625 + 0.009792 + 0.006416 + 0.006208 + 0.008667 + 0.008 + 0.008208 + 0.009334 + 0.011209 + 0.010583 + 0.010292 + 0.012375 + 0.009 + 0.008333 + 0.008958 + 0.008125 + 0.007708 + 0.007833 + 0.008459 + 0.011625 + 0.010584 + 0.008667 + 0.008625 + 0.00875 + 0.009167 + 0.008459 + 0.009 + 0.0085 + 0.009292 + 0.009292 + 0.010083 + 0.009792 + 0.009542 + 0.009041 + 0.008667 + 0.009541 + 0.0095 + 0.010041 + 0.009542 + 0.008875 + 0.010667 + 0.010166 + 0.0095 + 0.0105 + 0.009834 + 0.009709 + 0.010667 + 0.011041 + 0.01075 + 0.010083 + 0.009542 + 0.010042 + 0.011375 + 0.010041 + 0.009209 + 0.00925 + 0.009042 + 0.010125 + 0.013625 + 0.01 + 0.009167 + 0.009375 + 0.008917 + 0.009625 + 0.009791 + 0.00975 + 0.009708 + 0.009583 + 0.020916 + 0.0115 + 0.009875 + 0.009125 + 0.009584 + 0.011167 + 0.012583 + 0.012208 + 0.011 + 0.011458 + 0.010208 + 0.010541 + 0.010458 + 0.011417 + 0.009875 + 0.011292 + 0.023208 + 0.013167 + 0.011125 + 0.011459 + 0.015667 + 0.0125 + 0.010666 + 0.009959 + 0.009708 + 0.009 + 0.008083 + 0.008583 + 0.008209 + 0.008542 + 0.008167 + 0.008542 + 0.010333 + 0.007292 + 0.007625 + 0.007042 + 0.009334 + 0.00875 + 0.008875 + 0.008417 + 0.0085 + 0.009417 + 0.008667 + 0.009125 + 0.008666 + 0.008875 + 0.008917 + 0.00925 + 0.009333 + 0.0115 + 0.010375 + 0.008833 + 0.0085 + 0.009166 + 0.009375 + 0.008875 + 0.008791 + 0.008459 + 0.008625 + 0.009042 + 0.009542 + 0.009583 + 0.009083 + 0.009209 + 0.008875 + 0.008583 + 0.008792 + 0.008916 + 0.008667 + 0.008875 + 0.00875 + 0.009 + 0.009542 + 0.00925 + 0.009459 + 0.009625 + 0.009292 + 0.00975 + 0.010375 + 0.011375 + 0.011333 + 0.010416 + 0.010375 + 0.009667 + 0.010291 + 0.009625 + 0.010041 + 0.010042 + 0.009208 + 0.011 + 0.009458 + 0.009042 + 0.009334 + 0.009584 + 0.08325 + 0.041666 + 0.011208 + 0.010375 + 0.008875 + 0.008833 + 0.009125 + 0.00875 + 0.008709 + 0.009 + 0.008833 + 0.009167 + 0.017375 + 0.010333 + 0.013791 + 0.010542 + 0.009833 + 0.009167 + 0.015916 + 0.009708 + 0.027667 + 0.027167 + 0.014 + 0.013209 + 0.012 + 0.009584 + 0.009792 + 0.015583 + 0.011333 + 0.010291 + 0.01025 + 0.009375 + 0.008667 + 0.007542 + 0.008041 + 0.007458 + 0.007625 + 0.00725 + 0.012083 + 0.009417 + 0.008125 + 0.009292 + 0.011458 + 0.010458 + 0.012916 + 0.01075 + 0.010833 + 0.008541 + 0.009792 + 0.009875 + 0.02575 + 0.01275 + 0.011625 + 0.010792 + 0.011208 + 0.010875 + 0.01225 + 0.014 + 0.012667 + 0.012541 + 0.010542 + 0.011833 + 0.010458 + 0.012958 + 0.013583 + 0.011791 + 0.010542 + 0.011334 + 0.010917 + 0.010708 + 0.0125 + 0.01225 + 0.012208 + 0.012083 + 0.012625 + 0.012542 + 0.011458 + 0.010375 + 0.012 + 0.011667 + 0.011584 + 0.010167 + 0.01075 + 0.009625 + 0.009584 + 0.010875 + 0.010083 + 0.0105 + 0.011583 + 0.012 + 0.0105 + 0.009 + 0.009667 + 0.010542 + 0.009333 + 0.01 + 0.011875 + 0.010583 + 0.010458 + 0.009833 + 0.009292 + 0.010625 + 0.008958 + 0.009167 + 0.010959 + 0.01325 + 0.012333 + 0.012333 + 0.013666 + 0.009958 + 0.010875 + 0.011959 + 0.01075 + 0.01475 + 0.011583 + 0.01175 + 0.012917 + 0.012708 + 0.012708 + 0.012708 + 0.010792 + 0.009709 + 0.00975 + 0.012584 + 0.012834 + 0.012958 + 0.012875 + 0.009708 + 0.009708 + 0.008208 + 0.007792 + 0.007208 + 0.008083 + 0.007833 + 0.008833 + 0.007917 + 0.008709 + 0.007958 + 0.010958 + 0.0075 + 0.007417 + 0.007625 + 0.0095 + 0.009417 + 0.00925 + 0.009042 + 0.010875 + 0.00875 + 0.007958 + 0.008208 + 0.009375 + 0.010875 + 0.010625 + 0.011625 + 0.009875 + 0.009917 + 0.009875 + 0.008125 + 0.008333 + 0.00825 + 0.008375 + 0.008916 + 0.008167 + 0.008042 + 0.009709 + 0.010875 + 0.011042 + 0.010959 + 0.01025 + 0.008792 + 0.008084 + 0.008333 + 0.008292 + 0.009667 + 0.010625 + 0.011375 + 0.011292 + 0.010125 + 0.008417 + 0.008667 + 0.008416 + 0.009 + 0.008708 + 0.008458 + 0.009167 + 0.008209 + 0.00875 + 0.012209 + 0.011375 + 0.011083 + 0.011 + 0.009833 + 0.009167 + 0.008417 + 0.009125 + 0.008709 + 0.008584 + 0.009083 + 0.008791 + 0.009083 + 0.008667 + 0.009 + 0.0085 + 0.008667 + 0.009166 + 0.009459 + 0.009375 + 0.012792 + 0.011791 + 0.0115 + 0.011541 + 0.009292 + 0.008791 + 0.01075 + 0.010958 + 0.010292 + 0.009875 + 0.013042 + 0.013041 + 0.011792 + 0.012 + 0.009458 + 0.009834 + 0.010625 + 0.014125 + 0.0125 + 0.012958 + 0.012583 + 0.010542 + 0.008375 + 0.009125 + 0.010125 + 0.009208 + 0.009042 + 0.009834 + 0.0225 + 0.016541 + 0.010042 + 0.012125 + 0.01125 + 0.010833 + 0.007083 + 0.007833 + 0.0085 + 0.008458 + 0.008709 + 0.008584 + 0.009209 + 0.00875 + 0.009375 + 0.008375 + 0.008625 + 0.008041 + 0.008667 + 0.0125 + 0.009167 + 0.010542 + 0.009583 + 0.008709 + 0.009416 + 0.008458 + 0.008583 + 0.008625 + 0.008334 + 0.00825 + 0.008792 + 0.007875 + 0.0085 + 0.009875 + 0.007958 + 0.011833 + 0.008292 + 0.008458 + 0.008333 + 0.007625 + 0.008291 + 0.008083 + 0.008875 + 0.0085 + 0.009166 + 0.00925 + 0.01025 + 0.009 + 0.008583 + 0.008125 + 0.008417 + 0.011208 + 0.011708 + 0.010541 + 0.009833 + 0.008458 + 0.008875 + 0.00875 + 0.008583 + 0.009958 + 0.008792 + 0.010292 + 0.009916 + 0.009042 + 0.010708 + 0.01075 + 0.01175 + 0.012334 + 0.011125 + 0.011458 + 0.0125 + 0.00925 + 0.012167 + 0.011666 + 0.011459 + 0.010959 + 0.008334 + 0.011 + 0.015917 + 0.012042 + 0.012208 + 0.009208 + 0.012375 + 0.012709 + 0.013084 + 0.012084 + 0.011125 + 0.013542 + 0.01175 + 0.013125 + 0.012417 + 0.011625 + 0.010417 + 0.011041 + 0.008625 + 0.0085 + 0.008541 + 0.008167 + 0.011542 + 0.010125 + 0.009167 + 0.01025 + 0.020333 + 0.012667 + 0.010791 + 0.007625 + 0.00925 + 0.006542 + 0.008291 + 0.008709 + 0.008208 + 0.00875 + 0.008458 + 0.009 + 0.009709 + 0.008875 + 0.008208 + 0.008584 + 0.008459 + 0.007791 + 0.010375 + 0.00825 + 0.008041 + 0.007833 + 0.008083 + 0.007833 + 0.00825 + 0.008083 + 0.008166 + 0.008167 + 0.008125 + 0.008792 + 0.008458 + 0.008125 + 0.009709 + 0.007959 + 0.007667 + 0.008375 + 0.008291 + 0.008334 + 0.008583 + 0.008666 + 0.008167 + 0.008375 + 0.011625 + 0.00825 + 0.008334 + 0.008875 + 0.010667 + 0.0085 + 0.010375 + 0.010916 + 0.013 + 0.011792 + 0.011625 + 0.01175 + 0.010083 + 0.008959 + 0.011084 + 0.012417 + 0.010625 + 0.01 + 0.011 + 0.011125 + 0.0115 + 0.011791 + 0.011833 + 0.009625 + 0.0115 + 0.011417 + 0.011167], + :filter-tx-naive + [0.027459 + 0.008 + 0.006125 + 0.005792 + 0.005959 + 0.005708 + 0.005791 + 0.005959 + 0.006167 + 0.005708 + 0.006 + 0.005917 + 0.005541 + 0.00575 + 0.005791 + 0.005875 + 0.006041 + 0.00575 + 0.005875 + 0.005917 + 0.006167 + 0.006666 + 0.007292 + 0.007333 + 0.006916 + 0.0085 + 0.00675 + 0.007042 + 0.00675 + 0.007041 + 0.006541 + 0.005541 + 0.005792 + 0.006375 + 0.007 + 0.007 + 0.006583 + 0.008792 + 0.006875 + 0.006417 + 0.0065 + 0.006292 + 0.006459 + 0.007167 + 0.007583 + 0.0075 + 0.014625 + 0.008125 + 0.00775 + 0.007125 + 0.007333 + 0.007208 + 0.008625 + 0.007625 + 0.008583 + 0.006708 + 0.008209 + 0.007333 + 0.007708 + 0.007291 + 0.0065 + 0.006417 + 0.007166 + 0.008583 + 0.009042 + 0.006458 + 0.007125 + 0.006416 + 0.00675 + 0.006791 + 0.007208 + 0.007084 + 0.00925 + 0.01075 + 0.008292 + 0.00825 + 0.006959 + 0.00725 + 0.010125 + 0.0095 + 0.008667 + 0.00875 + 0.0105 + 0.007291 + 0.00725 + 0.008584 + 0.00725 + 0.007458 + 0.007417 + 0.00875 + 0.006125 + 0.006917 + 0.007584 + 0.007334 + 0.00675 + 0.008375 + 0.009125 + 0.00925 + 0.009042 + 0.008833 + 0.008375 + 0.007666 + 0.006542 + 0.006459 + 0.005917 + 0.005916 + 0.007042 + 0.007459 + 0.006584 + 0.006042 + 0.006166 + 0.00675 + 0.006 + 0.007125 + 0.006708 + 0.006541 + 0.007167 + 0.006792 + 0.006375 + 0.006583 + 0.006583 + 0.007042 + 0.006916 + 0.007625 + 0.006916 + 0.006667 + 0.006417 + 0.006666 + 0.006542 + 0.006792 + 0.006292 + 0.007708 + 0.007208 + 0.006875 + 0.007083 + 0.006458 + 0.006125 + 0.007041 + 0.007083 + 0.008375 + 0.006125 + 0.006042 + 0.011 + 0.006375 + 0.006375 + 0.008333 + 0.00875 + 0.009542 + 0.007167 + 0.007166 + 0.00725 + 0.006583 + 0.012375 + 0.013417 + 0.007042 + 0.005625 + 0.006375 + 0.006125 + 0.005166 + 0.005167 + 0.006125 + 0.008291 + 0.008 + 0.009042 + 0.008292 + 0.009292 + 0.008291 + 0.008708 + 0.008208 + 0.008542 + 0.010292 + 0.009042 + 0.00875 + 0.010125 + 0.008917 + 0.00875 + 0.008209 + 0.0095 + 0.010667 + 0.009708 + 0.009417 + 0.007875 + 0.009208 + 0.007417 + 0.007042 + 0.008541 + 0.009 + 0.008334 + 0.007084 + 0.006333 + 0.006333 + 0.006166 + 0.006458 + 0.006417 + 0.005625 + 0.005125 + 0.006334 + 0.007084 + 0.005 + 0.006041 + 0.006333 + 0.005916 + 0.005583 + 0.005667 + 0.00575 + 0.0055 + 0.005375 + 0.005042 + 0.00525 + 0.005792 + 0.006 + 0.005792 + 0.005666 + 0.005084 + 0.008 + 0.004875 + 0.005584 + 0.005542 + 0.005792 + 0.006 + 0.006083 + 0.005083 + 0.005541 + 0.005083 + 0.005 + 0.005291 + 0.007167 + 0.007834 + 0.005625 + 0.005166 + 0.005584 + 0.007292 + 0.007292 + 0.024292 + 0.01625 + 0.0085 + 0.00725 + 0.007042 + 0.007375 + 0.006 + 0.005917 + 0.005792 + 0.0105 + 0.005834 + 0.005917 + 0.008166 + 0.007417 + 0.009584 + 0.008417 + 0.008667 + 0.008709 + 0.008083 + 0.009834 + 0.007792 + 0.007208 + 0.006875 + 0.010584 + 0.006458 + 0.0065 + 0.006916 + 0.00675 + 0.00525 + 0.006 + 0.005 + 0.005292 + 0.005833 + 0.006334 + 0.005459 + 0.005209 + 0.006166 + 0.007583 + 0.00725 + 0.007166 + 0.007042 + 0.009041 + 0.008 + 0.007708 + 0.007166 + 0.006166 + 0.008208 + 0.008333 + 0.006959 + 0.007834 + 0.007459 + 0.006959 + 0.006292 + 0.007792 + 0.00775 + 0.007792 + 0.007417 + 0.006 + 0.005166 + 0.005208 + 0.0055 + 0.005209 + 0.005792 + 0.005875 + 0.0065 + 0.005917 + 0.005166 + 0.005625 + 0.005666 + 0.005333 + 0.005208 + 0.005042 + 0.005417 + 0.004959 + 0.005084 + 0.005458 + 0.004583 + 0.005083 + 0.0055 + 0.0055 + 0.00525 + 0.0055 + 0.005875 + 0.005417 + 0.013916 + 0.005083 + 0.00525 + 0.00525 + 0.004916 + 0.00525 + 0.0055 + 0.005292 + 0.005958 + 0.00625 + 0.006167 + 0.006541 + 0.006333 + 0.006709 + 0.006375 + 0.006 + 0.005417 + 0.005791 + 0.006458 + 0.006167 + 0.007084 + 0.007917 + 0.00775 + 0.007166 + 0.00675 + 0.006458 + 0.00675 + 0.0055 + 0.006708 + 0.00725 + 0.008875 + 0.007834 + 0.00825 + 0.008208 + 0.007959 + 0.007875 + 0.008875 + 0.008791 + 0.007208 + 0.006625 + 0.007125 + 0.007042 + 0.006583 + 0.005292 + 0.00625 + 0.005583 + 0.005458 + 0.005417 + 0.00525 + 0.006208 + 0.007125 + 0.00625 + 0.00525 + 0.006 + 0.005417 + 0.005792 + 0.005417 + 0.0085 + 0.007667 + 0.007667 + 0.007458 + 0.007083 + 0.006291 + 0.0075 + 0.006875 + 0.006583 + 0.007291 + 0.007042 + 0.006375 + 0.006709 + 0.007167 + 0.007834 + 0.007583 + 0.00625 + 0.005542 + 0.004916 + 0.005042 + 0.005041 + 0.014833 + 0.005208 + 0.005292 + 0.005916 + 0.005916 + 0.006917 + 0.006667 + 0.005791 + 0.007 + 0.006708 + 0.006792 + 0.006167 + 0.006709 + 0.006459 + 0.006 + 0.005708 + 0.00525 + 0.004625 + 0.00475 + 0.004959 + 0.005209 + 0.005334 + 0.004666 + 0.004958 + 0.005667 + 0.005625 + 0.005792 + 0.006 + 0.005459 + 0.004917 + 0.005083 + 0.014541 + 0.005167 + 0.004959 + 0.005125 + 0.005167 + 0.006167 + 0.006166 + 0.005375 + 0.004959 + 0.004959 + 0.005292 + 0.005041 + 0.006083 + 0.00625 + 0.005125 + 0.005208 + 0.005583 + 0.005166 + 0.005125 + 0.005125 + 0.005333 + 0.008416 + 0.008125 + 0.007959 + 0.008 + 0.007291 + 0.008292 + 0.01175 + 0.008541 + 0.007208 + 0.006292 + 0.007083 + 0.006708 + 0.006625 + 0.005458 + 0.005458 + 0.005291 + 0.005375 + 0.00575 + 0.005083 + 0.005666 + 0.005208 + 0.0055 + 0.005459 + 0.004958 + 0.006584 + 0.006875 + 0.007084 + 0.00825 + 0.008084 + 0.008083 + 0.00775 + 0.005916 + 0.00775 + 0.009042 + 0.008084 + 0.006625 + 0.007916 + 0.007958 + 0.006458 + 0.007583 + 0.007875 + 0.007625 + 0.008125 + 0.006292 + 0.006084 + 0.006584 + 0.004792 + 0.0045 + 0.004708 + 0.004834 + 0.004625 + 0.005208 + 0.005708 + 0.005083 + 0.0055 + 0.005459 + 0.005125 + 0.00525 + 0.004917 + 0.004583 + 0.004625 + 0.00475 + 0.006125 + 0.006167 + 0.00625 + 0.006167 + 0.006 + 0.006083 + 0.006208 + 0.005833 + 0.005041 + 0.005333 + 0.005208 + 0.005167 + 0.004834 + 0.005 + 0.004625 + 0.004791 + 0.004958 + 0.004625 + 0.004916 + 0.00475 + 0.004667 + 0.004875 + 0.005125 + 0.00525 + 0.005083 + 0.004542 + 0.00475 + 0.004667 + 0.004833 + 0.00575 + 0.005083 + 0.005167 + 0.004916 + 0.005083 + 0.004833 + 0.005209 + 0.005167 + 0.010334 + 0.007334 + 0.008542 + 0.00775 + 0.007416 + 0.007375 + 0.00675 + 0.007666 + 0.008125 + 0.007125 + 0.006833 + 0.006667 + 0.006625 + 0.00625 + 0.005667 + 0.005417 + 0.005583 + 0.005417 + 0.00525 + 0.005041 + 0.005 + 0.004833 + 0.005041 + 0.005167 + 0.004792 + 0.007292 + 0.006875 + 0.0075 + 0.007458 + 0.007875 + 0.00775 + 0.007417 + 0.006333 + 0.008416 + 0.00825 + 0.007917 + 0.006542 + 0.008042 + 0.007458 + 0.007375 + 0.008416 + 0.007958 + 0.00775 + 0.007667 + 0.006292 + 0.006 + 0.005834 + 0.005542 + 0.005041 + 0.004542 + 0.005084 + 0.00575 + 0.006 + 0.006125 + 0.006292 + 0.006458 + 0.006583 + 0.006541 + 0.006333 + 0.005875 + 0.004667 + 0.004833 + 0.0055 + 0.005 + 0.0045 + 0.004875 + 0.005125 + 0.004458 + 0.004625 + 0.004792 + 0.004666 + 0.004792 + 0.005 + 0.005084 + 0.005375 + 0.005292 + 0.004916 + 0.005167 + 0.004875 + 0.004875 + 0.00525 + 0.014458 + 0.005083 + 0.005208 + 0.005125 + 0.005167 + 0.0105 + 0.005542 + 0.004584 + 0.006709 + 0.004833 + 0.004708 + 0.007458 + 0.005875 + 0.0085 + 0.0055 + 0.005083 + 0.007 + 0.004834 + 0.005084 + 0.009959 + 0.007209 + 0.012583 + 0.009 + 0.011458 + 0.008417 + 0.005959 + 0.010167 + 0.010917 + 0.007167 + 0.006959 + 0.01025 + 0.006791 + 0.005083 + 0.008666 + 0.005333 + 0.005375 + 0.010292 + 0.011666 + 0.007833 + 0.012 + 0.0055 + 0.008333 + 0.006125 + 0.005583 + 0.012167 + 0.007375 + 0.011084 + 0.006875 + 0.015292 + 0.007542 + 0.0095 + 0.006958 + 0.008709 + 0.014833 + 0.008667 + 0.009125 + 0.00775 + 0.020666 + 0.011833 + 0.007791 + 0.014542 + 0.008417 + 0.011542 + 0.010209 + 0.005917 + 0.005333 + 0.010166 + 0.005625 + 0.010166 + 0.005458 + 0.005459 + 0.006917 + 0.00525 + 0.005875 + 0.012416 + 0.005625 + 0.015625 + 0.007334 + 0.006292 + 0.009875 + 0.005917 + 0.005208 + 0.009625 + 0.005459 + 0.005084 + 0.008084 + 0.005125 + 0.007208 + 0.005125 + 0.005292 + 0.007709 + 0.005167 + 0.004875 + 0.0075 + 0.004959 + 0.005125 + 0.007791 + 0.005292 + 0.010458 + 0.004458 + 0.004875 + 0.008042 + 0.005041 + 0.005333 + 0.008208 + 0.005417 + 0.006 + 0.006917 + 0.00475 + 0.006708 + 0.005583 + 0.005416 + 0.009583 + 0.005792 + 0.004834 + 0.007042 + 0.005333 + 0.005375 + 0.007375 + 0.007292 + 0.009125 + 0.007708 + 0.0105 + 0.006125 + 0.011167 + 0.008417 + 0.008375 + 0.014667 + 0.010708 + 0.005542 + 0.005166 + 0.0085 + 0.005375 + 0.007375 + 0.005875 + 0.00675 + 0.009417 + 0.005958 + 0.005583 + 0.008083 + 0.005625 + 0.007625 + 0.012791 + 0.006667 + 0.011042 + 0.006875 + 0.006542 + 0.007375 + 0.00625 + 0.008 + 0.009667 + 0.006 + 0.010583 + 0.008042 + 0.010875 + 0.006959 + 0.010334 + 0.007958 + 0.01075 + 0.013083 + 0.009666 + 0.013416 + 0.0075 + 0.005917 + 0.006667 + 0.004875 + 0.005 + 0.008 + 0.004875 + 0.004708 + 0.006833 + 0.005083 + 0.008333 + 0.005333 + 0.005416 + 0.007208 + 0.005375 + 0.005167 + 0.00725 + 0.005292 + 0.009209 + 0.004917 + 0.005167 + 0.007292 + 0.004792 + 0.00475 + 0.005875 + 0.01175 + 0.005 + 0.010292 + 0.004625 + 0.007125 + 0.00475 + 0.004958 + 0.005333 + 0.004916 + 0.005125 + 0.005083 + 0.005 + 0.004708 + 0.004708 + 0.00525 + 0.004542 + 0.005041 + 0.005167 + 0.005042 + 0.005458 + 0.005042 + 0.00475 + 0.004541 + 0.004958 + 0.005083 + 0.005417 + 0.005541 + 0.005375 + 0.005542 + 0.005041 + 0.005417 + 0.004959 + 0.006959 + 0.006292 + 0.005708 + 0.007667 + 0.0075 + 0.007125 + 0.006166 + 0.006375 + 0.007541 + 0.007291 + 0.006125 + 0.006125 + 0.0065 + 0.005542 + 0.006042 + 0.005833 + 0.005292 + 0.005542 + 0.005417 + 0.005375 + 0.005416 + 0.005833 + 0.005542 + 0.005584 + 0.005125 + 0.005542 + 0.006875 + 0.006458 + 0.006375 + 0.00725 + 0.006959 + 0.006667 + 0.006 + 0.006125 + 0.007416 + 0.007708 + 0.007125 + 0.006375 + 0.006542 + 0.006125 + 0.006167 + 0.007416 + 0.007458 + 0.007417 + 0.006958 + 0.005584 + 0.004792 + 0.005125 + 0.004958 + 0.005375 + 0.004792 + 0.004875 + 0.005417 + 0.005708 + 0.005208 + 0.004917 + 0.005333 + 0.005084 + 0.005917 + 0.005833 + 0.004958 + 0.0055 + 0.0055 + 0.0055 + 0.005334 + 0.005459 + 0.005833 + 0.005208 + 0.005208 + 0.005042 + 0.004625 + 0.005166 + 0.0055 + 0.004875 + 0.004791 + 0.004917 + 0.005292 + 0.011459 + 0.005 + 0.005083 + 0.005167 + 0.004834 + 0.004875 + 0.004792 + 0.005417 + 0.005583 + 0.006167 + 0.006667 + 0.006375 + 0.006291 + 0.006416 + 0.006334 + 0.006167 + 0.007458 + 0.005959 + 0.005334 + 0.005334 + 0.005209 + 0.005167 + 0.00475 + 0.005 + 0.009709 + 0.006459 + 0.007375 + 0.007375 + 0.006834 + 0.006125 + 0.005583 + 0.007625 + 0.007209 + 0.006708 + 0.005375 + 0.00575 + 0.005375 + 0.005167 + 0.005291 + 0.00525 + 0.005083 + 0.005333 + 0.005333 + 0.005209 + 0.005375 + 0.00525 + 0.005042 + 0.005625 + 0.005334 + 0.009042 + 0.006583 + 0.006709 + 0.005 + 0.007333 + 0.006209 + 0.006 + 0.006042 + 0.005667 + 0.007584 + 0.007666 + 0.006083 + 0.007291 + 0.00725 + 0.006917 + 0.006625 + 0.008 + 0.007459 + 0.007125 + 0.00675 + 0.005583 + 0.005459 + 0.005084 + 0.004542 + 0.004708 + 0.004584 + 0.004625 + 0.005125 + 0.004458 + 0.004916 + 0.0055 + 0.005125 + 0.005083 + 0.004791 + 0.004958 + 0.004458 + 0.005 + 0.005166 + 0.004958 + 0.004916 + 0.005125 + 0.004541 + 0.004875 + 0.005041 + 0.005042 + 0.004916 + 0.004792 + 0.004666 + 0.004375 + 0.005458 + 0.004583 + 0.00475 + 0.00475 + 0.00475 + 0.005375 + 0.005 + 0.004666 + 0.004917 + 0.005042 + 0.004625 + 0.005125 + 0.005167 + 0.004875 + 0.004875 + 0.004625 + 0.005125 + 0.004708 + 0.005125 + 0.005292 + 0.005333 + 0.00525 + 0.005083 + 0.004875 + 0.005125 + 0.00475 + 0.007375 + 0.005791 + 0.006708 + 0.007291 + 0.006583 + 0.005958 + 0.005875 + 0.007792 + 0.007833 + 0.007292 + 0.006416 + 0.005667 + 0.005042 + 0.005583 + 0.004958 + 0.0055 + 0.005 + 0.005125 + 0.005166 + 0.00525 + 0.005167 + 0.0055 + 0.00625 + 0.006208 + 0.006625 + 0.006292 + 0.006167 + 0.005125 + 0.004958 + 0.005833 + 0.005666 + 0.005792 + 0.005542 + 0.007417 + 0.007625 + 0.007042 + 0.006333 + 0.006917 + 0.006208 + 0.00575 + 0.007209 + 0.00725 + 0.00725 + 0.006917 + 0.006375 + 0.005458 + 0.005625 + 0.005792 + 0.004875 + 0.004417 + 0.004792 + 0.004917 + 0.005542 + 0.004834 + 0.004917 + 0.005084 + 0.006875 + 0.007 + 0.006917 + 0.006542 + 0.006041 + 0.006625 + 0.005916 + 0.005375 + 0.005375 + 0.005541 + 0.004833 + 0.005459 + 0.005917 + 0.006791 + 0.006208 + 0.006375 + 0.006125 + 0.006 + 0.005959 + 0.006208 + 0.004958 + 0.0045 + 0.004833 + 0.004791 + 0.004583 + 0.00475 + 0.004833 + 0.005208 + 0.005041 + 0.004583 + 0.005 + 0.005042 + 0.005083 + 0.004625 + 0.004959 + 0.004709 + 0.004959 + 0.005375 + 0.005625 + 0.005333 + 0.005042 + 0.009834 + 0.006834 + 0.00675 + 0.006625 + 0.006834 + 0.007958 + 0.00675 + 0.006625 + 0.007375 + 0.008834 + 0.008625 + 0.00825 + 0.007042 + 0.006 + 0.00625 + 0.006 + 0.006584 + 0.006084 + 0.005834 + 0.007292 + 0.007417 + 0.009334 + 0.008208 + 0.006917 + 0.006875 + 0.006459 + 0.005875 + 0.005417 + 0.00575 + 0.005875 + 0.006208 + 0.005625 + 0.006417 + 0.006375 + 0.006 + 0.007792 + 0.007875 + 0.0085 + 0.007209 + 0.006625 + 0.006292 + 0.007042 + 0.009167 + 0.0085 + 0.007833 + 0.00775 + 0.007584 + 0.00775 + 0.00575 + 0.005417 + 0.005042 + 0.00475 + 0.005667 + 0.00675 + 0.006667 + 0.00625 + 0.006708 + 0.00575 + 0.00625 + 0.006875 + 0.007 + 0.007167 + 0.007083 + 0.006125 + 0.006209 + 0.006125 + 0.005958 + 0.005208 + 0.005292 + 0.005584 + 0.006625 + 0.006625 + 0.00725 + 0.006625 + 0.006208 + 0.00525 + 0.00575 + 0.005417 + 0.005167 + 0.00575 + 0.005958 + 0.006041 + 0.00625 + 0.006416 + 0.006333 + 0.006041 + 0.005708 + 0.005708 + 0.005125 + 0.005708 + 0.005417 + 0.005917 + 0.006167 + 0.00775 + 0.006917 + 0.00625 + 0.007209 + 0.007 + 0.007583 + 0.005666 + 0.006958 + 0.007042 + 0.006667 + 0.006625 + 0.006541 + 0.006583 + 0.007875 + 0.007041 + 0.00775 + 0.007292 + 0.008584 + 0.008042 + 0.007833 + 0.006542 + 0.005417 + 0.005334 + 0.006208 + 0.007458 + 0.007541 + 0.014125 + 0.008584 + 0.008042 + 0.008041 + 0.007917 + 0.009292 + 0.006583 + 0.0115 + 0.00725 + 0.006833 + 0.007041 + 0.006458 + 0.006333 + 0.007541 + 0.007167 + 0.007042 + 0.007834 + 0.009333 + 0.00825 + 0.008667 + 0.006958 + 0.0055 + 0.0065 + 0.006 + 0.007167 + 0.013416 + 0.007917 + 0.007125 + 0.006708 + 0.006292 + 0.006042 + 0.005458 + 0.005375 + 0.005 + 0.005291 + 0.005458 + 0.005917 + 0.006042 + 0.005083 + 0.005375 + 0.005042 + 0.005541 + 0.00525 + 0.004792 + 0.004834 + 0.005333 + 0.005292 + 0.004959 + 0.005125 + 0.005 + 0.004791 + 0.004625 + 0.004959 + 0.005459 + 0.004834 + 0.004875 + 0.005125 + 0.005458 + 0.004875 + 0.00525 + 0.004959 + 0.004875 + 0.004541 + 0.005041 + 0.005042 + 0.0045 + 0.005125 + 0.005291 + 0.00525 + 0.005041 + 0.005042 + 0.00525 + 0.005167 + 0.004834 + 0.004709 + 0.005292 + 0.005125 + 0.005875 + 0.005291 + 0.005292 + 0.005292 + 0.005209 + 0.009167 + 0.006042 + 0.006084 + 0.006417 + 0.006291 + 0.00625 + 0.006125 + 0.005708 + 0.005834 + 0.007042 + 0.010458 + 0.007084 + 0.006667 + 0.005708 + 0.005417 + 0.005333 + 0.005166 + 0.005791 + 0.005208 + 0.005292 + 0.005459 + 0.00525 + 0.004916 + 0.005375 + 0.005167 + 0.006625 + 0.006375 + 0.006459 + 0.006125 + 0.006459 + 0.005625 + 0.004917 + 0.006584 + 0.006167 + 0.005916 + 0.006125 + 0.008416 + 0.0075 + 0.007125 + 0.006417 + 0.006791 + 0.006042 + 0.00625 + 0.008 + 0.007709 + 0.007709 + 0.006833 + 0.006458 + 0.004875 + 0.004917 + 0.0055 + 0.005333 + 0.005208 + 0.004875 + 0.00475 + 0.005417 + 0.005083 + 0.004458 + 0.005292 + 0.005209 + 0.005334 + 0.004667 + 0.004666 + 0.005208 + 0.005459 + 0.005125 + 0.004833 + 0.005167 + 0.005167 + 0.004916 + 0.004667 + 0.005167 + 0.005083 + 0.005125 + 0.00475 + 0.005 + 0.004541 + 0.005 + 0.004917 + 0.005041 + 0.004292 + 0.004584 + 0.004625 + 0.004667 + 0.00475 + 0.004792 + 0.004792 + 0.005 + 0.005042 + 0.005084 + 0.005167 + 0.005167 + 0.004708 + 0.004833 + 0.005291 + 0.0055 + 0.006 + 0.005834 + 0.004875 + 0.005458 + 0.006791 + 0.006292 + 0.006208 + 0.006334 + 0.006042 + 0.005958 + 0.006458 + 0.00675 + 0.006 + 0.007417 + 0.008292 + 0.007417 + 0.007334 + 0.006125 + 0.00525 + 0.005167 + 0.005416 + 0.005 + 0.005292 + 0.00525 + 0.005042 + 0.005542 + 0.005416 + 0.006333 + 0.006625 + 0.006416 + 0.005958 + 0.005791 + 0.004917 + 0.0055 + 0.005584 + 0.004958 + 0.006458 + 0.006375 + 0.006041 + 0.007084 + 0.007125 + 0.00675 + 0.007292 + 0.005667 + 0.00575 + 0.006125 + 0.00725 + 0.006875 + 0.007083 + 0.007042 + 0.006083 + 0.005958 + 0.005417 + 0.004834 + 0.005292 + 0.004792 + 0.004959 + 0.004917 + 0.005167 + 0.004875 + 0.004959 + 0.005125 + 0.004917 + 0.005167 + 0.004666 + 0.004333 + 0.004917 + 0.005042 + 0.004792 + 0.005125 + 0.005042 + 0.004792 + 0.004833 + 0.004917 + 0.004458 + 0.004834 + 0.005042 + 0.005208 + 0.004708 + 0.004958 + 0.004583 + 0.005416 + 0.00525 + 0.004916 + 0.005292 + 0.004542 + 0.004541 + 0.004375 + 0.004416 + 0.004958 + 0.004666 + 0.00475 + 0.00475 + 0.005125 + 0.005208 + 0.004833 + 0.0045 + 0.004875 + 0.004958 + 0.004958 + 0.005792 + 0.010125 + 0.009542 + 0.006083 + 0.006166 + 0.005875 + 0.005834 + 0.00625 + 0.007333 + 0.007917 + 0.007042 + 0.006584 + 0.007708 + 0.006708 + 0.00675 + 0.006875 + 0.007083 + 0.005958 + 0.005625 + 0.0055 + 0.005416 + 0.00525 + 0.006375 + 0.006375 + 0.006709 + 0.007125 + 0.006875 + 0.007 + 0.006791 + 0.006042 + 0.005375 + 0.005125 + 0.005125 + 0.005083 + 0.005041 + 0.00525 + 0.006125 + 0.006167 + 0.007666 + 0.007875 + 0.007833 + 0.0075 + 0.007625 + 0.007333 + 0.006 + 0.007083 + 0.00825 + 0.007375 + 0.007625 + 0.007 + 0.007792 + 0.008667 + 0.006708 + 0.006042 + 0.006625 + 0.006291 + 0.0065 + 0.006167 + 0.005583 + 0.005584 + 0.0045 + 0.00525 + 0.00525 + 0.00525 + 0.00525 + 0.004791 + 0.005 + 0.004916 + 0.004667 + 0.004625 + 0.004916 + 0.005166 + 0.004583 + 0.004791 + 0.004792 + 0.005083 + 0.004791 + 0.004708 + 0.004708 + 0.00525 + 0.004791 + 0.004792 + 0.004917 + 0.005125 + 0.005209 + 0.004792 + 0.004292 + 0.004584 + 0.004833 + 0.004584 + 0.004333 + 0.00525 + 0.004583 + 0.004792 + 0.004959 + 0.005 + 0.004958 + 0.004791 + 0.005041 + 0.005083 + 0.005166 + 0.005208 + 0.005791 + 0.004958 + 0.00525 + 0.004833 + 0.005375 + 0.005 + 0.005083 + 0.005083 + 0.006125 + 0.005917 + 0.007291 + 0.008041 + 0.008792 + 0.007792 + 0.007459 + 0.006834 + 0.006 + 0.005209 + 0.00525 + 0.005083 + 0.005208 + 0.006583 + 0.006792 + 0.006792 + 0.006834 + 0.006708 + 0.006625 + 0.006458 + 0.005583 + 0.005333 + 0.004917 + 0.005167 + 0.005 + 0.005625 + 0.00625 + 0.00625 + 0.01 + 0.008042 + 0.007584 + 0.007583 + 0.007542 + 0.007 + 0.005667 + 0.00775 + 0.007292 + 0.007334 + 0.007291 + 0.007375 + 0.007291 + 0.008417 + 0.007334 + 0.005875 + 0.006167 + 0.0065 + 0.006666 + 0.006375 + 0.005791 + 0.006166 + 0.005667 + 0.004541 + 0.004583 + 0.004917 + 0.004875 + 0.004791 + 0.0045 + 0.004542 + 0.004875 + 0.004833 + 0.004542 + 0.005125 + 0.004583 + 0.004875 + 0.004708 + 0.004875 + 0.00475 + 0.004833 + 0.004833 + 0.004875 + 0.004959 + 0.005125 + 0.005375 + 0.004541 + 0.013875 + 0.005125 + 0.004625 + 0.004958 + 0.004708 + 0.004958 + 0.006042 + 0.005875 + 0.006167 + 0.005792 + 0.006292 + 0.006625 + 0.007042 + 0.006375 + 0.005875 + 0.006 + 0.007125 + 0.0055 + 0.005375 + 0.004959 + 0.005208 + 0.005 + 0.005166 + 0.004958 + 0.0055 + 0.005333 + 0.006417 + 0.006167 + 0.008125 + 0.00775 + 0.007458 + 0.008416 + 0.00775 + 0.006791 + 0.006333 + 0.007125 + 0.007084 + 0.007 + 0.005375 + 0.006459 + 0.006167 + 0.005666 + 0.006042 + 0.005166 + 0.006209 + 0.005709 + 0.005458 + 0.005792 + 0.005875 + 0.005541 + 0.005458 + 0.006083 + 0.005291 + 0.006875 + 0.006708 + 0.00775 + 0.006334 + 0.006625 + 0.006583 + 0.006667 + 0.006125 + 0.006625 + 0.008083 + 0.006625 + 0.006125 + 0.006167 + 0.006541 + 0.006 + 0.007833 + 0.005625 + 0.005708 + 0.005459 + 0.005625 + 0.005208 + 0.005625 + 0.005458 + 0.005875 + 0.005 + 0.005416 + 0.005125 + 0.005708 + 0.005417 + 0.005417 + 0.004833 + 0.004959 + 0.00525 + 0.005458 + 0.005583 + 0.005166 + 0.011125 + 0.005542 + 0.005084 + 0.005292 + 0.005208 + 0.0055 + 0.005333 + 0.005625 + 0.005208 + 0.005375 + 0.005333 + 0.005084 + 0.00525 + 0.005458 + 0.005417 + 0.0085 + 0.005042 + 0.006208 + 0.005208 + 0.005292 + 0.006167 + 0.005209 + 0.005459 + 0.0065 + 0.005333 + 0.005292 + 0.006333 + 0.005625 + 0.007167 + 0.00625 + 0.005458 + 0.00725 + 0.005584 + 0.005792 + 0.006792 + 0.005292 + 0.005666 + 0.007083 + 0.007875 + 0.007959 + 0.007958 + 0.00775 + 0.0075 + 0.008333 + 0.008125 + 0.006292 + 0.005791 + 0.007292 + 0.005542 + 0.005292 + 0.006958 + 0.005375 + 0.0055 + 0.009042 + 0.006375 + 0.007 + 0.005875 + 0.00575 + 0.007042 + 0.005875 + 0.005792 + 0.006959 + 0.005875 + 0.006792 + 0.006833 + 0.008875 + 0.00675 + 0.007375 + 0.007958 + 0.006459 + 0.008125 + 0.0055 + 0.008875 + 0.008209 + 0.007042 + 0.008209 + 0.006459 + 0.008 + 0.00925 + 0.007084 + 0.006875 + 0.005542 + 0.00575 + 0.006792 + 0.005375 + 0.009584 + 0.0055 + 0.006209 + 0.006209 + 0.005208 + 0.005666 + 0.006708 + 0.005583 + 0.007083 + 0.005125 + 0.005167 + 0.006334 + 0.005333 + 0.005084 + 0.006584 + 0.005209 + 0.005417 + 0.008417 + 0.005459 + 0.006292 + 0.005333 + 0.005458 + 0.006833 + 0.005208 + 0.005125 + 0.006625 + 0.005334 + 0.005333 + 0.0065 + 0.005208 + 0.006083 + 0.005208 + 0.005292 + 0.006667 + 0.00525 + 0.005333 + 0.006666 + 0.005375 + 0.006208 + 0.005292 + 0.005167 + 0.006792 + 0.005667 + 0.006042 + 0.007333 + 0.005625 + 0.005625 + 0.006584 + 0.006166 + 0.006917 + 0.005542 + 0.005541 + 0.007917 + 0.011375 + 0.006833 + 0.007542 + 0.006417 + 0.012041 + 0.008083 + 0.007458 + 0.007208 + 0.005417 + 0.005959 + 0.007167 + 0.005958 + 0.007291 + 0.005584 + 0.005584 + 0.006708 + 0.005958 + 0.005791 + 0.007667 + 0.005834 + 0.007292 + 0.005375 + 0.005459 + 0.008875 + 0.005875 + 0.005792 + 0.009625 + 0.007875 + 0.006875 + 0.007666 + 0.006375 + 0.006208 + 0.009041 + 0.006834 + 0.007375 + 0.006709 + 0.008042 + 0.007875 + 0.006958 + 0.009666 + 0.008916 + 0.006625 + 0.00725 + 0.0055 + 0.005458 + 0.0075 + 0.005334 + 0.005458 + 0.022375 + 0.007541 + 0.005542 + 0.005917 + 0.00625 + 0.005542 + 0.0085 + 0.00625 + 0.006 + 0.005958 + 0.005667 + 0.005583 + 0.006083 + 0.005542 + 0.006083 + 0.005917 + 0.005459 + 0.006334 + 0.008459 + 0.00625 + 0.005916 + 0.006083 + 0.006458 + 0.006167 + 0.006042 + 0.005375 + 0.005125 + 0.005292 + 0.005167 + 0.011167 + 0.005792 + 0.009542 + 0.00625 + 0.00625 + 0.006625 + 0.006792 + 0.006791 + 0.006792 + 0.007083 + 0.006541 + 0.006584 + 0.006208 + 0.00775 + 0.008833 + 0.006459 + 0.005625 + 0.006958 + 0.008584 + 0.007583 + 0.0075 + 0.007583 + 0.008708 + 0.011167 + 0.008958 + 0.008625 + 0.008708 + 0.011208 + 0.008916 + 0.008708 + 0.008542 + 0.006542 + 0.008708 + 0.007375 + 0.006542 + 0.00625 + 0.005333 + 0.0085 + 0.006291 + 0.008125 + 0.006917 + 0.006125 + 0.006583 + 0.008542 + 0.008292 + 0.007291 + 0.006417 + 0.006292 + 0.008166 + 0.009083 + 0.006417 + 0.006459 + 0.006584 + 0.008 + 0.009125 + 0.008833 + 0.010916 + 0.008875 + 0.013 + 0.006667 + 0.006375 + 0.011292 + 0.008416 + 0.008125 + 0.008167 + 0.006834 + 0.006833 + 0.008167 + 0.0055 + 0.005625 + 0.00575 + 0.005209 + 0.006084 + 0.006084 + 0.005041 + 0.004833 + 0.00575 + 0.005833 + 0.005459 + 0.005459 + 0.009084 + 0.005667 + 0.005125 + 0.004958 + 0.007958 + 0.00525 + 0.005541 + 0.005708 + 0.00575 + 0.004792 + 0.00475 + 0.005459 + 0.00525 + 0.005541 + 0.005625 + 0.005917 + 0.005875 + 0.004875 + 0.005125 + 0.004708 + 0.005541 + 0.0055 + 0.006125 + 0.0055 + 0.005667 + 0.005334 + 0.005083 + 0.006292 + 0.005208 + 0.005667 + 0.00525 + 0.005625 + 0.005333 + 0.005333 + 0.005292 + 0.005292 + 0.005625 + 0.0055 + 0.005875 + 0.005833 + 0.005417 + 0.007375 + 0.007 + 0.0085 + 0.008458 + 0.008167 + 0.008417 + 0.007334 + 0.008334 + 0.007792 + 0.007042 + 0.008959 + 0.007042 + 0.007375 + 0.006583 + 0.006125 + 0.005208 + 0.005708 + 0.005208 + 0.004459 + 0.004792 + 0.006125 + 0.006 + 0.005334 + 0.005125 + 0.006 + 0.007917 + 0.007625 + 0.007 + 0.006416 + 0.008667 + 0.007875 + 0.00725 + 0.011375 + 0.005958 + 0.008333 + 0.010666 + 0.007542 + 0.007792 + 0.007625 + 0.007542 + 0.007042 + 0.009458 + 0.008958 + 0.00775 + 0.007416 + 0.005709 + 0.005542 + 0.005625 + 0.005458 + 0.005916 + 0.00625 + 0.005791 + 0.00675 + 0.005625 + 0.006083 + 0.007208 + 0.00825 + 0.007333 + 0.00675 + 0.007791 + 0.007375 + 0.007041 + 0.005792 + 0.006125 + 0.006833 + 0.007833 + 0.007625 + 0.006917 + 0.006708 + 0.007 + 0.006041 + 0.005917 + 0.005333 + 0.004958 + 0.004917 + 0.005125 + 0.00475 + 0.0055 + 0.005375 + 0.005583 + 0.005125 + 0.005 + 0.005167 + 0.005084 + 0.005084 + 0.005417 + 0.005333 + 0.00575 + 0.00525 + 0.005208 + 0.006208 + 0.006166 + 0.007125 + 0.0075 + 0.007084 + 0.007167 + 0.006792 + 0.006125 + 0.005917 + 0.005791 + 0.0085 + 0.007375 + 0.00775 + 0.008209 + 0.00775 + 0.007292 + 0.007667 + 0.010375 + 0.009125 + 0.009 + 0.00775 + 0.009875 + 0.007084 + 0.006542 + 0.007667 + 0.008916 + 0.007209 + 0.0065 + 0.010292 + 0.006791 + 0.006125 + 0.006625 + 0.009 + 0.008333 + 0.009917 + 0.008167 + 0.0065 + 0.006458 + 0.005542 + 0.007375 + 0.007083 + 0.008 + 0.007875 + 0.008542 + 0.008083 + 0.008208 + 0.018625 + 0.007125 + 0.007708 + 0.008709 + 0.011375 + 0.008042 + 0.006667 + 0.013125 + 0.006667 + 0.009458 + 0.008833 + 0.010208 + 0.009417 + 0.007459 + 0.007667 + 0.006458 + 0.007791 + 0.008417 + 0.007041 + 0.007667 + 0.007375 + 0.006417 + 0.008417 + 0.0085 + 0.008708 + 0.007042 + 0.007458 + 0.007791 + 0.006083 + 0.006541 + 0.006792 + 0.006625 + 0.005584 + 0.004959 + 0.005375 + 0.005334 + 0.004792 + 0.005375 + 0.005166 + 0.005541 + 0.005333 + 0.005125 + 0.005166 + 0.005125 + 0.005708 + 0.0065 + 0.006834 + 0.00675 + 0.006209 + 0.010667 + 0.006916 + 0.006917 + 0.005375 + 0.005333 + 0.005709 + 0.005291 + 0.005334 + 0.005834 + 0.005834 + 0.005334 + 0.00575 + 0.007875 + 0.007417 + 0.006667 + 0.006916 + 0.007042 + 0.00725 + 0.007959 + 0.007083 + 0.006208 + 0.00875 + 0.010959 + 0.008292 + 0.008125 + 0.007625 + 0.006208 + 0.009417 + 0.006584 + 0.005916 + 0.006042 + 0.005541 + 0.005583 + 0.005667 + 0.005833 + 0.012666 + 0.007542 + 0.008041 + 0.009917 + 0.006625 + 0.006667 + 0.009667 + 0.006292 + 0.005917 + 0.006333 + 0.006083 + 0.006333 + 0.011458 + 0.007958 + 0.007875 + 0.007541 + 0.007167 + 0.006 + 0.006583 + 0.009209 + 0.006875 + 0.00675 + 0.010375 + 0.007208 + 0.007167 + 0.006083 + 0.005708 + 0.006083 + 0.005416 + 0.005917 + 0.007125 + 0.00725 + 0.006542 + 0.0075 + 0.006125 + 0.006 + 0.005292 + 0.006458 + 0.006458 + 0.005875 + 0.00575 + 0.005833 + 0.006542 + 0.006208 + 0.006416 + 0.006292 + 0.006125 + 0.00625 + 0.005333 + 0.005291 + 0.005667 + 0.005875 + 0.005584 + 0.005667 + 0.005375 + 0.005375 + 0.005042 + 0.005333 + 0.005209 + 0.005292 + 0.004958 + 0.004542 + 0.005125 + 0.004834 + 0.004958 + 0.005125 + 0.005084 + 0.007833 + 0.005167 + 0.005083 + 0.004833 + 0.005125 + 0.004583 + 0.006125 + 0.005708 + 0.007375 + 0.006417 + 0.006541 + 0.006667 + 0.006625 + 0.007042 + 0.00675 + 0.006583 + 0.006375 + 0.006292 + 0.009833 + 0.007375 + 0.007958 + 0.007875 + 0.007875 + 0.006958 + 0.007 + 0.006792 + 0.005417 + 0.005667 + 0.005083 + 0.005334 + 0.005542 + 0.0055 + 0.005541 + 0.006709 + 0.007041 + 0.006667 + 0.010792 + 0.006709 + 0.0065 + 0.006541 + 0.00525 + 0.005541 + 0.006084 + 0.006375 + 0.006375 + 0.011666 + 0.008166 + 0.007917 + 0.007959 + 0.007875 + 0.00675 + 0.007166 + 0.009 + 0.008458 + 0.007417 + 0.006667 + 0.006541 + 0.009958 + 0.006375 + 0.00625 + 0.006125 + 0.005416 + 0.005292 + 0.005333 + 0.005334 + 0.005958 + 0.00575 + 0.005042 + 0.004375 + 0.005125 + 0.0055 + 0.005291 + 0.005375 + 0.004875 + 0.004958 + 0.004875 + 0.0055 + 0.004708 + 0.004875 + 0.004958 + 0.00525 + 0.004458 + 0.004583 + 0.004667 + 0.004625 + 0.004959 + 0.005041 + 0.00475 + 0.005125 + 0.004791 + 0.00475 + 0.004833 + 0.004709 + 0.004958 + 0.004958 + 0.005291 + 0.004875 + 0.005 + 0.004667 + 0.004834 + 0.004875 + 0.005208 + 0.005 + 0.005 + 0.004833 + 0.00475 + 0.005333 + 0.005417 + 0.007334 + 0.006584 + 0.0095 + 0.006708 + 0.006083 + 0.007458 + 0.01225 + 0.0055 + 0.006042 + 0.005958 + 0.007417 + 0.008709 + 0.009542 + 0.008542 + 0.007833 + 0.007375 + 0.006375 + 0.00675 + 0.007 + 0.006541 + 0.006542 + 0.005292 + 0.005041 + 0.011709 + 0.006584 + 0.011834 + 0.006084 + 0.006292 + 0.006833 + 0.006375 + 0.007167 + 0.009041 + 0.006792 + 0.006708 + 0.006208 + 0.006792 + 0.005792 + 0.00625 + 0.007708 + 0.014125 + 0.007708 + 0.007 + 0.0065 + 0.00725 + 0.005875 + 0.007958 + 0.007667 + 0.007458 + 0.007584 + 0.008042 + 0.007167 + 0.006292 + 0.0055 + 0.00575 + 0.00475 + 0.004375 + 0.004875 + 0.004791 + 0.005208 + 0.005042 + 0.005458 + 0.004292 + 0.005209 + 0.004791 + 0.004833 + 0.005042 + 0.004667 + 0.005041 + 0.005042 + 0.004667 + 0.004875 + 0.004541 + 0.005125 + 0.005167 + 0.004833 + 0.0045 + 0.00475 + 0.00475 + 0.004916 + 0.005 + 0.004584 + 0.004375 + 0.004625 + 0.00475 + 0.00475 + 0.004959 + 0.0045 + 0.00475 + 0.00475 + 0.004875 + 0.004917 + 0.004916 + 0.005 + 0.005042 + 0.0045 + 0.004917 + 0.004792 + 0.005041 + 0.004917 + 0.005334 + 0.006458 + 0.006083 + 0.006 + 0.006166 + 0.005667 + 0.00525 + 0.004625 + 0.005083 + 0.005875 + 0.0055 + 0.005916 + 0.007917 + 0.006959 + 0.013167 + 0.006917 + 0.006334 + 0.006042 + 0.006125 + 0.005417 + 0.01025 + 0.004958 + 0.006542 + 0.006791 + 0.006417 + 0.006875 + 0.006792 + 0.011375 + 0.006708 + 0.006791 + 0.006875 + 0.006917 + 0.006542 + 0.005167 + 0.005666 + 0.005458 + 0.006333 + 0.006 + 0.007625 + 0.00725 + 0.006125 + 0.005708 + 0.010417 + 0.005042 + 0.00475 + 0.005625 + 0.0075 + 0.006666 + 0.007 + 0.005875 + 0.005875 + 0.005792 + 0.004917 + 0.004584 + 0.004709 + 0.004542 + 0.004792 + 0.005709 + 0.005584 + 0.00725 + 0.006333 + 0.005458 + 0.005041 + 0.005333 + 0.005417 + 0.005125 + 0.005125 + 0.004834 + 0.004875 + 0.005 + 0.011209 + 0.004709 + 0.005041 + 0.005 + 0.004667 + 0.005666 + 0.005875 + 0.005458 + 0.005875 + 0.005791 + 0.006084 + 0.00575 + 0.006083 + 0.005875 + 0.00625 + 0.005875 + 0.009 + 0.004708 + 0.005 + 0.00475 + 0.004416 + 0.004375 + 0.005125 + 0.004458 + 0.005 + 0.004792 + 0.004917 + 0.00475 + 0.00475 + 0.004584 + 0.005334 + 0.00525 + 0.005292 + 0.005916 + 0.007333 + 0.006291 + 0.006375 + 0.00625 + 0.006 + 0.005542 + 0.012292 + 0.006708 + 0.006916 + 0.006875 + 0.009333 + 0.00825 + 0.00775 + 0.006333 + 0.005458 + 0.005625 + 0.005125 + 0.005417 + 0.005458 + 0.004833 + 0.005125 + 0.00525 + 0.0055 + 0.004875 + 0.004958 + 0.004958 + 0.00875 + 0.0065 + 0.006167 + 0.006458 + 0.006625 + 0.006833 + 0.007708 + 0.006375 + 0.005375 + 0.005958 + 0.007291 + 0.007583 + 0.00775 + 0.007542 + 0.006667 + 0.00775 + 0.007 + 0.007292 + 0.007666 + 0.006917 + 0.007666 + 0.007792 + 0.006625 + 0.006167 + 0.005166 + 0.004709 + 0.004792 + 0.004667 + 0.004416 + 0.005542 + 0.005792 + 0.005166 + 0.004709 + 0.004417 + 0.00525 + 0.004916 + 0.004917 + 0.004542 + 0.004583 + 0.004625 + 0.004833 + 0.004875 + 0.004791 + 0.005 + 0.004833 + 0.005125 + 0.004709 + 0.005209 + 0.005584 + 0.00625 + 0.006 + 0.006042 + 0.005958 + 0.005333 + 0.006041 + 0.006042 + 0.00525 + 0.004708 + 0.004625 + 0.004416 + 0.005209 + 0.0045 + 0.005959 + 0.005959 + 0.006334 + 0.006084 + 0.005375 + 0.005375 + 0.005333 + 0.005083 + 0.005583 + 0.005875 + 0.005791 + 0.005417 + 0.006333 + 0.006375 + 0.00525 + 0.005292 + 0.005708 + 0.006083 + 0.006583 + 0.005666 + 0.005333 + 0.005709 + 0.007167 + 0.008 + 0.007416 + 0.006833 + 0.006666 + 0.006625 + 0.006208 + 0.005875 + 0.004917 + 0.00525 + 0.005 + 0.004875 + 0.005375 + 0.005042 + 0.004917 + 0.004916 + 0.005042 + 0.007084 + 0.0065 + 0.006458 + 0.007833 + 0.006541 + 0.007333 + 0.007625 + 0.005834 + 0.005459 + 0.007042 + 0.007541 + 0.007166 + 0.007333 + 0.006333 + 0.00725 + 0.00725 + 0.007375 + 0.007375 + 0.007625 + 0.007708 + 0.007625 + 0.006875 + 0.006 + 0.005167 + 0.0055 + 0.004959 + 0.004708 + 0.005 + 0.004708 + 0.006833 + 0.005791 + 0.006792 + 0.006125 + 0.006292 + 0.00625 + 0.005875 + 0.005375 + 0.004833 + 0.004625 + 0.004833 + 0.004583 + 0.004708 + 0.005 + 0.004791 + 0.004667 + 0.004959 + 0.004917 + 0.00475 + 0.004791 + 0.004917 + 0.004625 + 0.004916 + 0.004792 + 0.004584 + 0.004416 + 0.004292 + 0.004666 + 0.004708 + 0.004875 + 0.004875 + 0.004625 + 0.00475 + 0.004834 + 0.004875 + 0.004958 + 0.004583 + 0.004959 + 0.005 + 0.005083 + 0.004875 + 0.00525 + 0.006917 + 0.006416 + 0.006541 + 0.0065 + 0.0065 + 0.0065 + 0.006375 + 0.005916 + 0.00575 + 0.005875 + 0.01175 + 0.007792 + 0.007417 + 0.008041 + 0.008042 + 0.014 + 0.012167 + 0.005375 + 0.005125 + 0.005458 + 0.005041 + 0.007083 + 0.006375 + 0.007166 + 0.00725 + 0.016 + 0.008958 + 0.005292 + 0.005708 + 0.004958 + 0.005166 + 0.005709 + 0.005291 + 0.006542 + 0.006625 + 0.035125 + 0.032375 + 0.02625 + 0.009583 + 0.008333 + 0.008458 + 0.006875 + 0.005916 + 0.006375 + 0.011541 + 0.021291 + 0.008333 + 0.0075 + 0.008541 + 0.008458 + 0.009333 + 0.006542 + 0.00625 + 0.005875 + 0.006375 + 0.005708 + 0.005333 + 0.0055 + 0.006292 + 0.005083 + 0.012208 + 0.005417 + 0.005959 + 0.005417 + 0.004791 + 0.005167 + 0.005209 + 0.005084 + 0.004917 + 0.006125 + 0.005375 + 0.0065 + 0.005125 + 0.005167 + 0.005334 + 0.005375 + 0.005792 + 0.005334 + 0.005459 + 0.006084 + 0.004709 + 0.004584 + 0.005 + 0.005 + 0.004916 + 0.00475 + 0.004708 + 0.005292 + 0.0055 + 0.004791 + 0.005292 + 0.004833 + 0.005333 + 0.004792 + 0.005333 + 0.004875 + 0.005167 + 0.004875 + 0.005042 + 0.005375 + 0.00525 + 0.005209 + 0.005375 + 0.005292 + 0.004834 + 0.005084 + 0.005625 + 0.014625 + 0.0095 + 0.006959 + 0.0085 + 0.007833 + 0.007333 + 0.007875 + 0.008458 + 0.007666 + 0.00675 + 0.005542 + 0.007167 + 0.007 + 0.007209 + 0.007042 + 0.0065 + 0.009792 + 0.006666 + 0.010667 + 0.005542 + 0.005334 + 0.005584 + 0.005125 + 0.008042 + 0.005667 + 0.005666 + 0.005709 + 0.006875 + 0.00775 + 0.009375 + 0.007 + 0.006375 + 0.006125 + 0.006292 + 0.006208 + 0.006666 + 0.006 + 0.007167 + 0.007084 + 0.007125 + 0.006125 + 0.009333 + 0.0075 + 0.007125 + 0.006459 + 0.005958 + 0.005708 + 0.005084 + 0.004833 + 0.005042 + 0.004917 + 0.004792 + 0.005583 + 0.004417 + 0.004834 + 0.011125 + 0.005542 + 0.0055 + 0.004792 + 0.00525 + 0.004792 + 0.004958 + 0.005291 + 0.004834 + 0.005375 + 0.005041 + 0.004917 + 0.005208 + 0.005 + 0.004583 + 0.004667 + 0.005167 + 0.005208 + 0.004541 + 0.00525 + 0.004667 + 0.00475 + 0.005042 + 0.004666 + 0.004625 + 0.00425 + 0.004917 + 0.004917 + 0.005333 + 0.005041 + 0.004917 + 0.004792 + 0.005 + 0.005 + 0.005042 + 0.005042 + 0.005 + 0.004958 + 0.005209 + 0.005208 + 0.005291 + 0.005042 + 0.00525 + 0.005333 + 0.004625 + 0.005209 + 0.004708 + 0.006 + 0.007375 + 0.00775 + 0.007 + 0.007083 + 0.006458 + 0.005541 + 0.005334 + 0.005041 + 0.005209 + 0.006916 + 0.006333 + 0.006292 + 0.006291 + 0.009375 + 0.00525 + 0.005209 + 0.004959 + 0.005333 + 0.00525 + 0.005833 + 0.005417 + 0.005041 + 0.00975 + 0.005625 + 0.0055 + 0.006 + 0.007584 + 0.007167 + 0.007291 + 0.007041 + 0.006333 + 0.0055 + 0.011541 + 0.006208 + 0.006625 + 0.006208 + 0.00975 + 0.006125 + 0.006417 + 0.00725 + 0.008166 + 0.006667 + 0.006334 + 0.005833 + 0.005792 + 0.005583 + 0.00475 + 0.005208 + 0.004917 + 0.005875 + 0.005083 + 0.005041 + 0.004959 + 0.005125 + 0.005042 + 0.005125 + 0.004792 + 0.005208 + 0.004875 + 0.0055 + 0.00525 + 0.005041 + 0.005583 + 0.004917 + 0.00525 + 0.004958 + 0.005166 + 0.005084 + 0.0055 + 0.005542 + 0.005458 + 0.004833 + 0.0055 + 0.005 + 0.004625 + 0.004792 + 0.005041 + 0.00475 + 0.005042 + 0.004834 + 0.005208 + 0.005167 + 0.005084 + 0.005459 + 0.005625 + 0.005292 + 0.004625 + 0.005041 + 0.005625 + 0.0055 + 0.005667 + 0.00525 + 0.00575 + 0.004959 + 0.004958 + 0.005 + 0.005208 + 0.011625 + 0.01025 + 0.006417 + 0.005916 + 0.008083 + 0.007458 + 0.007458 + 0.007875 + 0.007334 + 0.006625 + 0.0065 + 0.00675 + 0.005625 + 0.004833 + 0.005833 + 0.009583 + 0.0065 + 0.006041 + 0.006417 + 0.005833 + 0.005084 + 0.005125 + 0.005292 + 0.004834 + 0.004917 + 0.005333 + 0.005084 + 0.00575 + 0.01475 + 0.006416 + 0.006375 + 0.007583 + 0.007292 + 0.006917 + 0.007083 + 0.006708 + 0.005208 + 0.006208 + 0.007208 + 0.007 + 0.006833 + 0.007167 + 0.00625 + 0.005875 + 0.007125 + 0.006167 + 0.005459 + 0.006083 + 0.005791 + 0.005708 + 0.005458 + 0.005417 + 0.006875 + 0.006125 + 0.006083 + 0.006833 + 0.005459 + 0.005125 + 0.005125 + 0.005041 + 0.005291 + 0.005333 + 0.005833 + 0.005833 + 0.005708 + 0.005583 + 0.005584 + 0.005209 + 0.005291 + 0.005084 + 0.005542 + 0.004958 + 0.005583 + 0.005375 + 0.005417 + 0.005208 + 0.005208 + 0.006 + 0.006209 + 0.005833 + 0.005875 + 0.005792 + 0.006083 + 0.005583 + 0.004958 + 0.004959 + 0.005125 + 0.0055 + 0.004791 + 0.005125 + 0.004875 + 0.004791 + 0.005417 + 0.004959 + 0.005083 + 0.005042 + 0.005709 + 0.006084 + 0.005791 + 0.006 + 0.006125 + 0.005375 + 0.005583 + 0.006167 + 0.007083 + 0.007542 + 0.006625 + 0.007458 + 0.006541 + 0.005625 + 0.005166 + 0.005042 + 0.00625 + 0.005667 + 0.005667 + 0.006167 + 0.005917 + 0.005583 + 0.005333 + 0.005458 + 0.005209 + 0.005042 + 0.005208 + 0.005042 + 0.005042 + 0.004917 + 0.005 + 0.004958 + 0.004917 + 0.005917 + 0.0075 + 0.007042 + 0.007125 + 0.007 + 0.006209 + 0.005833 + 0.004708 + 0.006875 + 0.007166 + 0.006875 + 0.006541 + 0.006417 + 0.005666 + 0.006834 + 0.006959 + 0.006167 + 0.00575 + 0.005875 + 0.004959 + 0.004625 + 0.004875 + 0.004667 + 0.004917 + 0.005292 + 0.005042 + 0.004541 + 0.005167 + 0.004791 + 0.004333 + 0.005083 + 0.004667 + 0.004583 + 0.004958 + 0.004417 + 0.0045 + 0.004917 + 0.004791 + 0.004875 + 0.004958 + 0.004834 + 0.004792 + 0.004583 + 0.004958 + 0.004875 + 0.00475 + 0.004875 + 0.005125 + 0.004375 + 0.004833 + 0.004792 + 0.004709 + 0.004375 + 0.004417 + 0.0045 + 0.004292 + 0.004208 + 0.00475 + 0.005 + 0.0045 + 0.004791 + 0.004708 + 0.005042 + 0.004584 + 0.005459 + 0.005334 + 0.004709 + 0.004417 + 0.004875 + 0.0055 + 0.005042 + 0.00475 + 0.005125 + 0.005833 + 0.007125 + 0.006916 + 0.007 + 0.006959 + 0.006917 + 0.005916 + 0.00725 + 0.00625 + 0.005917 + 0.005416 + 0.005583 + 0.005167 + 0.005041 + 0.004708 + 0.004625 + 0.005083 + 0.004875 + 0.005083 + 0.00525 + 0.005083 + 0.004958 + 0.004875 + 0.004834 + 0.00575 + 0.005166 + 0.005042 + 0.007416 + 0.007167 + 0.006834 + 0.007375 + 0.006375 + 0.005667 + 0.007 + 0.007209 + 0.006042 + 0.007041 + 0.006708 + 0.00675 + 0.006833 + 0.008375 + 0.007208 + 0.007 + 0.005125 + 0.005334 + 0.005333 + 0.005583 + 0.007042 + 0.006042 + 0.005583 + 0.006042 + 0.005666 + 0.006042 + 0.00575 + 0.005834 + 0.005958 + 0.005833 + 0.006041 + 0.00625 + 0.006708 + 0.005667 + 0.004625 + 0.004709 + 0.004833 + 0.005042 + 0.005334 + 0.004708 + 0.004708 + 0.005792 + 0.006541 + 0.00675 + 0.006917 + 0.006458 + 0.005583 + 0.005625 + 0.005667 + 0.005584 + 0.005375 + 0.005792 + 0.005375 + 0.005375 + 0.004958 + 0.005625 + 0.005833 + 0.006125 + 0.006042 + 0.005792 + 0.00475 + 0.004292 + 0.004916 + 0.005125 + 0.00525 + 0.005125 + 0.005125 + 0.004917 + 0.005208 + 0.005291 + 0.005333 + 0.005042 + 0.005375 + 0.005208 + 0.008084 + 0.008125 + 0.008042 + 0.00775 + 0.007583 + 0.008292 + 0.008375 + 0.006666 + 0.00675 + 0.006833 + 0.006458 + 0.006458 + 0.006375 + 0.005584 + 0.004958 + 0.004875 + 0.00525 + 0.005292 + 0.004833 + 0.004833 + 0.0055 + 0.005125 + 0.004916 + 0.00525 + 0.004625 + 0.005333 + 0.007875 + 0.008042 + 0.007416 + 0.007292 + 0.007417 + 0.0075 + 0.0085 + 0.008167 + 0.00675 + 0.008125 + 0.007458 + 0.007834 + 0.006583 + 0.007083 + 0.00775 + 0.007208 + 0.00725 + 0.006625 + 0.006125 + 0.006292 + 0.006208 + 0.006208 + 0.005792 + 0.006875 + 0.006625 + 0.006208 + 0.006166 + 0.005917 + 0.005209 + 0.005125 + 0.004542 + 0.0045 + 0.004667 + 0.004542 + 0.004541 + 0.005208 + 0.004458 + 0.004958 + 0.004917 + 0.004916 + 0.004792 + 0.004875 + 0.005125 + 0.004916 + 0.004875 + 0.004792 + 0.00475 + 0.004834 + 0.004875 + 0.005083 + 0.004792 + 0.00475 + 0.004584 + 0.004667 + 0.004666 + 0.004875 + 0.004708 + 0.004708 + 0.004959 + 0.004667 + 0.0045 + 0.00475 + 0.0045 + 0.004875 + 0.005166 + 0.005459 + 0.005334 + 0.004958 + 0.004875 + 0.004833 + 0.005333 + 0.005 + 0.005375 + 0.005583 + 0.00775 + 0.007958 + 0.007709 + 0.007584 + 0.007209 + 0.007375 + 0.008542 + 0.006458 + 0.0065 + 0.006834 + 0.006334 + 0.005292 + 0.0055 + 0.005375 + 0.005541 + 0.005833 + 0.006459 + 0.0065 + 0.006 + 0.006042 + 0.00575 + 0.00525 + 0.005666 + 0.005459 + 0.005958 + 0.005875 + 0.006125 + 0.009333 + 0.007792 + 0.007459 + 0.007209 + 0.007542 + 0.007625 + 0.009084 + 0.006292 + 0.00775 + 0.007167 + 0.007666 + 0.007334 + 0.007542 + 0.008333 + 0.008 + 0.007334 + 0.006375 + 0.006333 + 0.00625 + 0.006084 + 0.00525 + 0.005 + 0.005041 + 0.005209 + 0.0045 + 0.005542 + 0.005292 + 0.006791 + 0.006875 + 0.006583 + 0.006041 + 0.005917 + 0.005959 + 0.006416 + 0.005875 + 0.006875 + 0.006792 + 0.006667 + 0.006333 + 0.006083 + 0.005958 + 0.006125 + 0.004833 + 0.004833 + 0.005 + 0.004791 + 0.005083 + 0.005167 + 0.004791 + 0.005042 + 0.005083 + 0.004834 + 0.004625 + 0.005083 + 0.005 + 0.005166 + 0.004792 + 0.004917 + 0.005583 + 0.006 + 0.005875 + 0.006042 + 0.006084 + 0.006209 + 0.0065 + 0.006292 + 0.006084 + 0.005792 + 0.005333 + 0.005625 + 0.005417 + 0.004958 + 0.005125 + 0.0075 + 0.008042 + 0.007667 + 0.007167 + 0.006541 + 0.007041 + 0.007625 + 0.008333 + 0.00675 + 0.006209 + 0.006959 + 0.006959 + 0.007125 + 0.005875 + 0.005625 + 0.005667 + 0.006084 + 0.006042 + 0.006042 + 0.006083 + 0.005708 + 0.006458 + 0.006667 + 0.008625 + 0.007375 + 0.00975 + 0.006416 + 0.007542 + 0.007 + 0.006458 + 0.006833 + 0.007042 + 0.008334 + 0.008792 + 0.007708 + 0.006708 + 0.006375 + 0.006875 + 0.006042 + 0.008666 + 0.007708 + 0.007708 + 0.007667 + 0.006875 + 0.006542 + 0.006584 + 0.0065 + 0.0065 + 0.006125 + 0.004959 + 0.005791 + 0.005 + 0.006 + 0.006084 + 0.00475 + 0.004917 + 0.00475 + 0.005417 + 0.006041 + 0.007375 + 0.007375 + 0.005875 + 0.007334 + 0.007875 + 0.008 + 0.006958 + 0.00675 + 0.007208 + 0.006875 + 0.006459 + 0.006459 + 0.00675 + 0.006291 + 0.007709 + 0.007917 + 0.006666 + 0.00725 + 0.008208 + 0.006708 + 0.006416 + 0.006292 + 0.006625 + 0.006667 + 0.005625 + 0.005792 + 0.004916 + 0.004958 + 0.005042 + 0.005167 + 0.004792 + 0.004458 + 0.005166 + 0.00525 + 0.005333 + 0.005 + 0.006083 + 0.006375 + 0.005875 + 0.007333 + 0.006375 + 0.006333 + 0.006084 + 0.005792 + 0.00625 + 0.006 + 0.007916 + 0.007542 + 0.007167 + 0.006708 + 0.005541 + 0.005167 + 0.005084 + 0.005709 + 0.0055 + 0.005041 + 0.00525 + 0.005333 + 0.005541 + 0.004917 + 0.005125 + 0.010209 + 0.0065 + 0.006375 + 0.005958 + 0.00625 + 0.0055 + 0.005334 + 0.005959 + 0.006458 + 0.005833 + 0.007417 + 0.007125 + 0.006791 + 0.006875 + 0.00675 + 0.005375 + 0.006125 + 0.007208 + 0.007459 + 0.007542 + 0.007375 + 0.006542 + 0.006083 + 0.005125 + 0.004875 + 0.004875 + 0.004916 + 0.005 + 0.004625 + 0.004958 + 0.005333 + 0.005542 + 0.005666 + 0.004625 + 0.004583 + 0.005458 + 0.005042 + 0.005042 + 0.005083 + 0.00525 + 0.004458 + 0.005125 + 0.005166 + 0.005125 + 0.005 + 0.005292 + 0.004541 + 0.00475 + 0.00475 + 0.005417 + 0.004875 + 0.00525 + 0.004583 + 0.005208 + 0.005 + 0.004792 + 0.004459 + 0.004875 + 0.004917 + 0.004708 + 0.004583 + 0.004875 + 0.004625 + 0.004583 + 0.005 + 0.005125 + 0.004917 + 0.004917 + 0.004583 + 0.004958 + 0.004917 + 0.005333 + 0.005292 + 0.006875 + 0.006458 + 0.005833 + 0.005875 + 0.005791 + 0.005292 + 0.005584 + 0.005334 + 0.005667 + 0.005958 + 0.005625 + 0.007542 + 0.007416 + 0.007042 + 0.007125 + 0.005959 + 0.004667 + 0.005375 + 0.005333 + 0.005208 + 0.005625 + 0.005417 + 0.005375 + 0.005333 + 0.004916 + 0.005958 + 0.006084 + 0.006166 + 0.005833 + 0.005792 + 0.005209 + 0.004541 + 0.005208 + 0.005208 + 0.006 + 0.005833 + 0.005792 + 0.007083 + 0.007458 + 0.006958 + 0.007083 + 0.006541 + 0.006083 + 0.007 + 0.007584 + 0.008167 + 0.007875 + 0.007333 + 0.007709 + 0.007417 + 0.005917 + 0.019792 + 0.006708 + 0.006459 + 0.006541 + 0.005875 + 0.0055 + 0.006125 + 0.006541 + 0.006209 + 0.007542 + 0.00625 + 0.008542 + 0.006292 + 0.005833 + 0.006708 + 0.016708 + 0.005708 + 0.005667 + 0.005917 + 0.0065 + 0.0065 + 0.006 + 0.00575 + 0.012875 + 0.005834 + 0.017125 + 0.006125 + 0.006167 + 0.014625 + 0.006834 + 0.0065 + 0.01025 + 0.006542 + 0.0105 + 0.017417 + 0.008084 + 0.027875 + 0.014292 + 0.009541 + 0.00825 + 0.007 + 0.009208 + 0.006291 + 0.006792 + 0.006 + 0.005875 + 0.006041 + 0.008083 + 0.012542 + 0.006375 + 0.006833 + 0.006208 + 0.009916 + 0.009958 + 0.007208 + 0.006083 + 0.009208 + 0.0125 + 0.008625 + 0.008542 + 0.007041 + 0.009542 + 0.00925 + 0.008584 + 0.010458 + 0.007667 + 0.015334 + 0.006792 + 0.006458 + 0.006875 + 0.006583 + 0.006583 + 0.00575 + 0.007958 + 0.00525 + 0.007417 + 0.006542 + 0.006125 + 0.00625 + 0.005875 + 0.006917 + 0.00625 + 0.0075 + 0.006 + 0.010875 + 0.0085 + 0.007708 + 0.007666 + 0.006708 + 0.006875 + 0.008084 + 0.00875 + 0.010375 + 0.007792 + 0.00675 + 0.010084 + 0.006375 + 0.011042 + 0.00825 + 0.007292 + 0.006667 + 0.005541 + 0.0055 + 0.00475 + 0.020875 + 0.006208 + 0.005125 + 0.007041 + 0.006417 + 0.006 + 0.005209 + 0.00625 + 0.0055 + 0.0055 + 0.00825 + 0.007958 + 0.0065 + 0.007125 + 0.006959 + 0.007167 + 0.006958 + 0.006791 + 0.006292 + 0.005334 + 0.009042 + 0.00575 + 0.0055 + 0.004792 + 0.005375 + 0.005792 + 0.006583 + 0.011083 + 0.007416 + 0.007292 + 0.006667 + 0.006 + 0.005917 + 0.006 + 0.005375 + 0.0085 + 0.006041 + 0.005625 + 0.005625 + 0.005042 + 0.008583 + 0.005542 + 0.004708 + 0.009 + 0.00575 + 0.008333 + 0.008833 + 0.005292 + 0.005542 + 0.005542 + 0.005584 + 0.009125 + 0.005875 + 0.010375 + 0.007292 + 0.007542 + 0.010416 + 0.00625 + 0.006834 + 0.006875 + 0.015709 + 0.007416 + 0.006459 + 0.006166 + 0.005333 + 0.005333 + 0.005583 + 0.009208 + 0.005584 + 0.006083 + 0.006208 + 0.005541 + 0.005583 + 0.005417 + 0.008834 + 0.005834 + 0.009584 + 0.005834 + 0.005792 + 0.005 + 0.010625 + 0.010667 + 0.008375 + 0.007084 + 0.006375 + 0.008958 + 0.007375 + 0.006167 + 0.006834 + 0.008125 + 0.007375 + 0.006167 + 0.006833 + 0.007625 + 0.00725 + 0.007791 + 0.005917 + 0.005584 + 0.005292 + 0.005417 + 0.004583 + 0.005458 + 0.005 + 0.007625 + 0.006459 + 0.004834 + 0.005459 + 0.005209 + 0.00525 + 0.006042 + 0.005375 + 0.005625 + 0.005041 + 0.005292 + 0.008375 + 0.005375 + 0.0055 + 0.005916 + 0.005375 + 0.00575 + 0.005583 + 0.004958 + 0.005458 + 0.004833 + 0.005208 + 0.00525 + 0.008375 + 0.004875 + 0.0055 + 0.005208 + 0.005208 + 0.005167 + 0.008334 + 0.005709 + 0.006709 + 0.004916 + 0.004917 + 0.008375 + 0.005958 + 0.005167 + 0.005791 + 0.005709 + 0.005625 + 0.004959 + 0.005667 + 0.006041 + 0.005542 + 0.005625 + 0.0055 + 0.008458 + 0.006625 + 0.005458 + 0.006 + 0.007542 + 0.010875 + 0.008042 + 0.008083 + 0.009917 + 0.006708 + 0.008208 + 0.008541 + 0.007042 + 0.006125 + 0.006791 + 0.006458 + 0.006042 + 0.0055 + 0.005166 + 0.005292 + 0.005333 + 0.005458 + 0.005291 + 0.008875 + 0.006083 + 0.005416 + 0.005583 + 0.006583 + 0.007583 + 0.007417 + 0.007834 + 0.011458 + 0.007333 + 0.007625 + 0.007667 + 0.007292 + 0.008542 + 0.008166 + 0.007542 + 0.006417 + 0.006875 + 0.00625 + 0.006375 + 0.008125 + 0.007875 + 0.007917 + 0.007667 + 0.00625 + 0.004625 + 0.005167 + 0.004875 + 0.004917 + 0.004958 + 0.005125 + 0.004458 + 0.005875 + 0.005417 + 0.005333 + 0.004625 + 0.00475 + 0.00525 + 0.005209 + 0.005334 + 0.004541 + 0.005083 + 0.005208 + 0.004959 + 0.004542 + 0.00525 + 0.004916 + 0.008584 + 0.004792 + 0.005167 + 0.00525 + 0.004917 + 0.005125 + 0.005417 + 0.005 + 0.004875 + 0.005459 + 0.004875 + 0.004791 + 0.005167 + 0.004875 + 0.004833 + 0.005458 + 0.004625 + 0.005083 + 0.005 + 0.005125 + 0.005083 + 0.005 + 0.005333 + 0.00475 + 0.004916 + 0.005416 + 0.005375 + 0.005666 + 0.00525 + 0.00575 + 0.005667 + 0.010291 + 0.005667 + 0.010041 + 0.0065 + 0.007375 + 0.007208 + 0.006583 + 0.006083 + 0.006084 + 0.007042 + 0.00725 + 0.009209 + 0.006166 + 0.005875 + 0.005708 + 0.005542 + 0.005041 + 0.008208 + 0.005417 + 0.005375 + 0.00525 + 0.005333 + 0.005416 + 0.005375 + 0.005292 + 0.005291 + 0.005875 + 0.007125 + 0.006916 + 0.006333 + 0.006958 + 0.006958 + 0.00725 + 0.006542 + 0.006208 + 0.0075 + 0.008167 + 0.008083 + 0.006625 + 0.006708 + 0.007208 + 0.007625 + 0.008958 + 0.010583 + 0.007833 + 0.007375 + 0.005167 + 0.00475 + 0.004625 + 0.004834 + 0.005125 + 0.004708 + 0.004292 + 0.0055 + 0.004917 + 0.004916 + 0.004334 + 0.005584 + 0.005083 + 0.004833 + 0.007417 + 0.004542 + 0.00475 + 0.004583 + 0.004833 + 0.004708 + 0.005167 + 0.005083 + 0.004875 + 0.004625 + 0.004875 + 0.005 + 0.004333 + 0.004208 + 0.0045 + 0.004375 + 0.004458 + 0.007417 + 0.004375 + 0.004833 + 0.00475 + 0.004583 + 0.00425 + 0.005 + 0.007708 + 0.004667 + 0.004875 + 0.006 + 0.005625 + 0.005375 + 0.007208 + 0.005083 + 0.005042 + 0.004791 + 0.005083 + 0.005541 + 0.004834 + 0.005167 + 0.005583 + 0.006667 + 0.005916 + 0.005792 + 0.007208 + 0.006166 + 0.006667 + 0.006667 + 0.00675 + 0.006584 + 0.005709 + 0.007084 + 0.01025 + 0.006 + 0.005875 + 0.005708 + 0.005375 + 0.005 + 0.005166 + 0.005125 + 0.005375 + 0.004959 + 0.005 + 0.004958 + 0.005084 + 0.007625 + 0.005292 + 0.004833 + 0.005125 + 0.006334 + 0.005959 + 0.005792 + 0.005958 + 0.00675 + 0.005666 + 0.005625 + 0.005 + 0.005292 + 0.006916 + 0.007083 + 0.006209 + 0.006833 + 0.006291 + 0.005834 + 0.008041 + 0.006958 + 0.007208 + 0.006875 + 0.00675 + 0.005541 + 0.005 + 0.00525 + 0.007542 + 0.004041 + 0.004167 + 0.004292 + 0.004584 + 0.0045 + 0.00825 + 0.004541 + 0.005042 + 0.004666 + 0.005125 + 0.004917 + 0.004667 + 0.004458 + 0.004625 + 0.005125 + 0.005041 + 0.004334 + 0.005041 + 0.004875 + 0.004792 + 0.005 + 0.004834 + 0.007833 + 0.0045 + 0.00425 + 0.004541 + 0.004583 + 0.004417 + 0.004042 + 0.004666 + 0.004667 + 0.004458 + 0.004625 + 0.00775 + 0.004875 + 0.004584 + 0.004625 + 0.004792 + 0.004833 + 0.005291 + 0.005875 + 0.005334 + 0.004666 + 0.004875 + 0.005291 + 0.008167 + 0.005541 + 0.004875 + 0.005083 + 0.005125 + 0.00675 + 0.007417 + 0.007 + 0.007292 + 0.0075 + 0.00775 + 0.00725 + 0.007084 + 0.010125 + 0.010667 + 0.006709 + 0.006584 + 0.00575 + 0.006667 + 0.007625 + 0.007292 + 0.006875 + 0.006083 + 0.00575 + 0.006791 + 0.006416 + 0.005917 + 0.005709 + 0.007834 + 0.007209 + 0.006792 + 0.006791 + 0.00675 + 0.005875 + 0.005083 + 0.006375 + 0.005541 + 0.005875 + 0.005958 + 0.00725 + 0.007834 + 0.0075 + 0.006333 + 0.005125 + 0.0065 + 0.007667 + 0.007666 + 0.010083 + 0.008208 + 0.007708 + 0.007667 + 0.006792 + 0.005542 + 0.004959 + 0.004792 + 0.005417 + 0.005167 + 0.005541 + 0.005709 + 0.006417 + 0.006792 + 0.007 + 0.007459 + 0.006459 + 0.006584 + 0.008834 + 0.005959 + 0.00625 + 0.007291 + 0.006417 + 0.005792 + 0.005875 + 0.00875 + 0.006083 + 0.005417 + 0.005125 + 0.006334 + 0.006334 + 0.006625 + 0.007167 + 0.006584 + 0.006209 + 0.005709 + 0.005167 + 0.005083 + 0.006 + 0.00675 + 0.005375 + 0.005291 + 0.006625 + 0.006792 + 0.006375 + 0.006375 + 0.006208 + 0.006958 + 0.006375 + 0.006875 + 0.006083 + 0.005791 + 0.010917 + 0.007 + 0.006917 + 0.006375 + 0.006667 + 0.006709 + 0.006834 + 0.009125 + 0.008417 + 0.005833 + 0.0065 + 0.006417 + 0.006 + 0.0055 + 0.007 + 0.006875 + 0.008584 + 0.008083 + 0.007875 + 0.007959 + 0.007625 + 0.008166 + 0.007333 + 0.008542 + 0.007333 + 0.007416 + 0.007833 + 0.008833 + 0.005792 + 0.006167 + 0.010875 + 0.0075 + 0.007375 + 0.006542 + 0.00625 + 0.005583 + 0.007375 + 0.008041 + 0.0075 + 0.006833 + 0.007542 + 0.007166 + 0.006958 + 0.007542 + 0.006 + 0.00575 + 0.006125 + 0.0075 + 0.007042 + 0.006666 + 0.006167 + 0.00625 + 0.005167 + 0.00475 + 0.005375 + 0.00525 + 0.004959 + 0.004584 + 0.007791 + 0.004417 + 0.005709 + 0.005167 + 0.004625 + 0.004875 + 0.005083 + 0.005417 + 0.005083 + 0.004792 + 0.005041 + 0.004667 + 0.004375 + 0.005042 + 0.008125 + 0.004667 + 0.004917 + 0.004916 + 0.005 + 0.005125 + 0.004875 + 0.004625 + 0.004417 + 0.004958 + 0.005 + 0.004375 + 0.00475 + 0.004709 + 0.007625 + 0.00475 + 0.004875 + 0.004334 + 0.007834 + 0.004666 + 0.005167 + 0.005167 + 0.005333 + 0.004875 + 0.004959 + 0.004625 + 0.005125 + 0.004958 + 0.005417 + 0.0055 + 0.008584 + 0.00525 + 0.005375 + 0.005042 + 0.007333 + 0.006375 + 0.006625 + 0.00625 + 0.007292 + 0.005917 + 0.005542 + 0.005375 + 0.007334 + 0.01025 + 0.007125 + 0.006625 + 0.005625 + 0.005417 + 0.005417 + 0.005416 + 0.004959 + 0.005292 + 0.005291 + 0.005292 + 0.005167 + 0.005417 + 0.00825 + 0.005208 + 0.005333 + 0.007083 + 0.00675 + 0.006209 + 0.005959 + 0.005917 + 0.005625 + 0.005959 + 0.005916 + 0.006 + 0.007041 + 0.006875 + 0.006625 + 0.006708 + 0.008167 + 0.005917 + 0.006166 + 0.0065 + 0.007166 + 0.006708 + 0.009875 + 0.006291 + 0.005333 + 0.004417 + 0.005334 + 0.004834 + 0.005083 + 0.004917 + 0.005042 + 0.005041 + 0.005541 + 0.004292 + 0.00525 + 0.005375 + 0.00475 + 0.005042 + 0.004625 + 0.005042 + 0.005041 + 0.00475 + 0.00475 + 0.004375 + 0.007583 + 0.004667 + 0.005166 + 0.004917 + 0.005166 + 0.004708 + 0.004917 + 0.004916 + 0.004959 + 0.005125 + 0.005125 + 0.004959 + 0.004958 + 0.004458 + 0.004833 + 0.004583 + 0.004834 + 0.0045 + 0.004458 + 0.005042 + 0.004792 + 0.00475 + 0.007916 + 0.005042 + 0.004791 + 0.00475 + 0.004792 + 0.004708 + 0.005292 + 0.005125 + 0.007125 + 0.00575 + 0.006042 + 0.006416 + 0.006458 + 0.005292 + 0.0055 + 0.004916 + 0.005959 + 0.005875 + 0.008584 + 0.007875 + 0.00675 + 0.007667 + 0.006917 + 0.005834 + 0.005375 + 0.005208 + 0.005375 + 0.005125 + 0.005167 + 0.009292 + 0.006125 + 0.00875 + 0.0065 + 0.005958 + 0.004792 + 0.005583 + 0.005166 + 0.005333 + 0.005167 + 0.005083 + 0.005666 + 0.005459 + 0.004958 + 0.006416 + 0.005583 + 0.007417 + 0.007416 + 0.007167 + 0.006625 + 0.005916 + 0.005625 + 0.005958 + 0.007208 + 0.007 + 0.007125 + 0.007 + 0.00675 + 0.0055 + 0.0075 + 0.006084 + 0.006 + 0.006125 + 0.005666 + 0.005 + 0.005084 + 0.004958 + 0.005416 + 0.004542 + 0.005042 + 0.004791 + 0.005 + 0.005083 + 0.004584 + 0.004958 + 0.004541 + 0.00475 + 0.004917 + 0.004959 + 0.004584 + 0.005291 + 0.004708 + 0.004459 + 0.004584 + 0.004916 + 0.005 + 0.005083 + 0.005 + 0.004834 + 0.005 + 0.004709 + 0.004917 + 0.004666 + 0.004542 + 0.004541 + 0.005125 + 0.004583 + 0.004625 + 0.004834 + 0.004333 + 0.004958 + 0.004917 + 0.005084 + 0.004791 + 0.004583 + 0.004291 + 0.004875 + 0.005334 + 0.00525 + 0.005708 + 0.005167 + 0.005083 + 0.005083 + 0.00525 + 0.004666 + 0.0075 + 0.005292 + 0.006041 + 0.006583 + 0.010875 + 0.00725 + 0.007209 + 0.006833 + 0.0065 + 0.005584 + 0.005166 + 0.005084 + 0.005291 + 0.004584 + 0.005125 + 0.006667 + 0.006291 + 0.005833 + 0.006292 + 0.006541 + 0.005417 + 0.005458 + 0.005042 + 0.005333 + 0.00525 + 0.005125 + 0.005042 + 0.005333 + 0.005 + 0.005875 + 0.005709 + 0.0075 + 0.006875 + 0.006875 + 0.006958 + 0.007208 + 0.005 + 0.004792 + 0.005958 + 0.006833 + 0.006583 + 0.006917 + 0.00675 + 0.006417 + 0.005917 + 0.004542 + 0.004958 + 0.00475 + 0.005 + 0.004709 + 0.004875 + 0.004875 + 0.004666 + 0.004625 + 0.004291 + 0.004875 + 0.0045 + 0.004584 + 0.004667 + 0.00475 + 0.004875 + 0.004833 + 0.004875 + 0.00525 + 0.004708 + 0.004625 + 0.004709 + 0.004959 + 0.004583 + 0.005208 + 0.004834 + 0.005125 + 0.004583 + 0.004916 + 0.004875 + 0.00525 + 0.0045 + 0.004667 + 0.004666 + 0.004875 + 0.004083 + 0.004416 + 0.004417 + 0.0045 + 0.004459 + 0.0045 + 0.00475 + 0.004792 + 0.005208 + 0.004583 + 0.005042 + 0.004625 + 0.004917 + 0.005417 + 0.006416 + 0.0065 + 0.006416 + 0.006375 + 0.006333 + 0.004833 + 0.00525 + 0.005334 + 0.005084 + 0.006209 + 0.005417 + 0.005458 + 0.0075 + 0.007084 + 0.007167 + 0.006708 + 0.005708 + 0.004916 + 0.005083 + 0.005416 + 0.004708 + 0.004875 + 0.004667 + 0.005416 + 0.005417 + 0.005208 + 0.005958 + 0.006541 + 0.006042 + 0.006459 + 0.006166 + 0.005375 + 0.004833 + 0.005083 + 0.00525 + 0.00575 + 0.005209 + 0.006209 + 0.007041 + 0.007167 + 0.006459 + 0.007125 + 0.007208 + 0.005917 + 0.006833 + 0.006708 + 0.005417 + 0.0055 + 0.005708 + 0.005958 + 0.00575 + 0.004916 + 0.004625 + 0.00525 + 0.005875 + 0.0055 + 0.005625 + 0.005458 + 0.005375 + 0.0055 + 0.004417 + 0.004666 + 0.004833 + 0.005125 + 0.004625 + 0.004708 + 0.004375 + 0.004166 + 0.004834 + 0.005083 + 0.004708 + 0.00475 + 0.004666 + 0.004542 + 0.0045 + 0.004667 + 0.004417 + 0.005167 + 0.004667 + 0.004916 + 0.004875 + 0.004959 + 0.005125 + 0.005291 + 0.004417 + 0.005 + 0.005042 + 0.004916 + 0.004375 + 0.004667 + 0.004542 + 0.004375 + 0.004708 + 0.004542 + 0.004792 + 0.004583 + 0.004833 + 0.004625 + 0.004875 + 0.005291 + 0.005417 + 0.007041 + 0.006375 + 0.006042 + 0.005833 + 0.006083 + 0.005292 + 0.004917 + 0.005375 + 0.006625 + 0.006459 + 0.005709 + 0.0085 + 0.00725 + 0.008 + 0.007583 + 0.006542 + 0.005709 + 0.005709 + 0.0055 + 0.005167 + 0.005334 + 0.005042 + 0.004875 + 0.005208 + 0.005542 + 0.006541 + 0.006209 + 0.005875 + 0.006375 + 0.005916 + 0.005375 + 0.005292 + 0.006375 + 0.00625 + 0.005084 + 0.006 + 0.005875 + 0.005459 + 0.007959 + 0.007625 + 0.008 + 0.007459 + 0.00625 + 0.007959 + 0.007375 + 0.007833 + 0.0075 + 0.007417 + 0.007292 + 0.007709 + 0.00775 + 0.006166 + 0.005 + 0.004958 + 0.005125 + 0.005083 + 0.0045 + 0.004917 + 0.005958 + 0.004667 + 0.004292 + 0.005 + 0.007917 + 0.005042 + 0.00475 + 0.004875 + 0.004667 + 0.005083 + 0.004792 + 0.004834 + 0.004458 + 0.004958 + 0.004791 + 0.00475 + 0.004792 + 0.00475 + 0.005208 + 0.004333 + 0.004792 + 0.005333 + 0.004958 + 0.0045 + 0.005125 + 0.00475 + 0.004791 + 0.004667 + 0.004583 + 0.005292 + 0.004667 + 0.004375 + 0.0045 + 0.004667 + 0.004667 + 0.005042 + 0.004459 + 0.004458 + 0.004875 + 0.004708 + 0.005208 + 0.005166 + 0.007584 + 0.006666 + 0.006833 + 0.0065 + 0.006291 + 0.006167 + 0.006167 + 0.006541 + 0.006125 + 0.005875 + 0.005916 + 0.008042 + 0.007583 + 0.008041 + 0.007583 + 0.0075 + 0.0065 + 0.006542 + 0.004833 + 0.004583 + 0.004708 + 0.006875 + 0.006625 + 0.006042 + 0.006583 + 0.006708 + 0.006458 + 0.006458 + 0.006125 + 0.006708 + 0.006667 + 0.006584 + 0.00675 + 0.00675 + 0.006625 + 0.006875 + 0.006083 + 0.008292 + 0.007417 + 0.007416 + 0.008292 + 0.008084 + 0.006834 + 0.006834 + 0.007834 + 0.007834 + 0.00725 + 0.00775 + 0.007125 + 0.007 + 0.008083 + 0.006417 + 0.005959 + 0.006375 + 0.00575 + 0.006041 + 0.006417 + 0.006292 + 0.005458 + 0.00525 + 0.004875 + 0.004958 + 0.005083 + 0.005041 + 0.005042 + 0.00525 + 0.00475 + 0.004417 + 0.004958 + 0.004708 + 0.005125 + 0.004458 + 0.007375 + 0.004917 + 0.004834 + 0.004541 + 0.005167 + 0.005167 + 0.004833 + 0.004708 + 0.004583 + 0.004958 + 0.005375 + 0.004708 + 0.004667 + 0.004584 + 0.004709 + 0.004667 + 0.004791 + 0.004875 + 0.004833 + 0.004375 + 0.004708 + 0.005208 + 0.004375 + 0.004875 + 0.004667 + 0.004625 + 0.00475 + 0.00525 + 0.005292 + 0.005083 + 0.004958 + 0.005125 + 0.0055 + 0.005041 + 0.005167 + 0.004958 + 0.005875 + 0.006167 + 0.0075 + 0.008125 + 0.007375 + 0.007167 + 0.006375 + 0.006709 + 0.005625 + 0.005625 + 0.0065 + 0.005834 + 0.005209 + 0.005292 + 0.005166 + 0.005083 + 0.005333 + 0.005583 + 0.005083 + 0.005042 + 0.005791 + 0.005125 + 0.005417 + 0.005125 + 0.004709 + 0.005042 + 0.005292 + 0.006209 + 0.007542 + 0.008083 + 0.007208 + 0.007375 + 0.0075 + 0.010417 + 0.008083 + 0.00675 + 0.008084 + 0.007459 + 0.0075 + 0.007583 + 0.006916 + 0.00725 + 0.007625 + 0.006167 + 0.006 + 0.006375 + 0.006208 + 0.006375 + 0.005625 + 0.004375 + 0.00475 + 0.005292 + 0.004959 + 0.004375 + 0.004709 + 0.005334 + 0.005542 + 0.004708 + 0.005125 + 0.004666 + 0.00475 + 0.004625 + 0.004625 + 0.004875 + 0.005042 + 0.004417 + 0.005083 + 0.004583 + 0.004833 + 0.004958 + 0.005 + 0.004791 + 0.00475 + 0.005 + 0.004959 + 0.005167 + 0.004791 + 0.004542 + 0.005375 + 0.004708 + 0.004708 + 0.004917 + 0.004917 + 0.005 + 0.004583 + 0.004875 + 0.004542 + 0.004625 + 0.004709 + 0.004625 + 0.004417 + 0.004584 + 0.004834 + 0.004791 + 0.005292 + 0.005084 + 0.005042 + 0.005 + 0.00525 + 0.00475 + 0.005417 + 0.006416 + 0.007917 + 0.007834 + 0.007292 + 0.008 + 0.007667 + 0.008042 + 0.005875 + 0.005167 + 0.005 + 0.005291 + 0.007708 + 0.007125 + 0.006375 + 0.006291 + 0.006458 + 0.006333 + 0.007 + 0.005667 + 0.005208 + 0.004875 + 0.004625 + 0.005 + 0.004834 + 0.005084 + 0.005584 + 0.005792 + 0.008333 + 0.008708 + 0.007625 + 0.007541 + 0.007333 + 0.007708 + 0.008 + 0.006125 + 0.007083 + 0.006833 + 0.006583 + 0.005875 + 0.006125 + 0.007791 + 0.007875 + 0.00625 + 0.00625 + 0.011083 + 0.006417 + 0.005833 + 0.004541 + 0.005 + 0.005416 + 0.005417 + 0.008875 + 0.00425 + 0.005125 + 0.005334 + 0.005333 + 0.005166 + 0.005042 + 0.004834 + 0.007917 + 0.005334 + 0.004708 + 0.004458 + 0.00425 + 0.004916 + 0.004834 + 0.008292 + 0.005416 + 0.005958 + 0.005667 + 0.005709 + 0.004875 + 0.005292 + 0.005833 + 0.00675 + 0.006584 + 0.0095 + 0.006666 + 0.00625 + 0.006541 + 0.006417 + 0.006334 + 0.00575 + 0.005833 + 0.0055 + 0.005375 + 0.00425 + 0.004834 + 0.004709 + 0.004584 + 0.005125 + 0.005792 + 0.004833 + 0.004958 + 0.004875 + 0.004958 + 0.005125 + 0.005292 + 0.005375 + 0.007459 + 0.006792 + 0.010375 + 0.015792 + 0.015166 + 0.006667 + 0.007167 + 0.006291 + 0.005542 + 0.005291 + 0.005333 + 0.007083 + 0.007083 + 0.00775 + 0.018084 + 0.007125 + 0.005917 + 0.0065 + 0.005208 + 0.005208 + 0.005292 + 0.005875 + 0.005 + 0.005708 + 0.005292 + 0.005042 + 0.005333 + 0.008416 + 0.006708 + 0.00825 + 0.007541 + 0.006875 + 0.007209 + 0.006875 + 0.006584 + 0.005875 + 0.008792 + 0.007667 + 0.008041 + 0.007834 + 0.006875 + 0.007 + 0.008791 + 0.007583 + 0.005875 + 0.006042 + 0.006 + 0.0055 + 0.005125 + 0.005459 + 0.005792 + 0.005458 + 0.004834 + 0.006 + 0.005791 + 0.005667 + 0.005333 + 0.005375 + 0.00575 + 0.005833 + 0.0055 + 0.005833 + 0.005208 + 0.0055 + 0.004916 + 0.004958 + 0.005334 + 0.006041 + 0.005542 + 0.005917 + 0.004958 + 0.00525 + 0.005542 + 0.005292 + 0.004875 + 0.00475 + 0.004667 + 0.005125 + 0.005459 + 0.005875 + 0.005291 + 0.00575 + 0.005625 + 0.006458 + 0.006333 + 0.0065 + 0.007 + 0.005917 + 0.006 + 0.00625 + 0.006209 + 0.004958 + 0.005958 + 0.005542 + 0.005542 + 0.005292 + 0.005375 + 0.005125 + 0.005083 + 0.005084 + 0.005834 + 0.008125 + 0.010083 + 0.008792 + 0.008042 + 0.007792 + 0.008208 + 0.007292 + 0.011 + 0.006708 + 0.006166 + 0.006375 + 0.006 + 0.005791 + 0.005167 + 0.005125 + 0.008084 + 0.005417 + 0.004958 + 0.00525 + 0.0055 + 0.005333 + 0.004875 + 0.004958 + 0.00475 + 0.005333 + 0.005 + 0.00525 + 0.007167 + 0.007583 + 0.006875 + 0.018792 + 0.006333 + 0.005708 + 0.007875 + 0.006125 + 0.007209 + 0.007083 + 0.006583 + 0.005959 + 0.00625 + 0.0075 + 0.007 + 0.007458 + 0.005583 + 0.005 + 0.0045 + 0.00475 + 0.005084 + 0.004666 + 0.004792 + 0.00575 + 0.004833 + 0.004917 + 0.004542 + 0.004875 + 0.005291 + 0.004875 + 0.005125 + 0.005042 + 0.005125 + 0.005042 + 0.005291 + 0.0045 + 0.005 + 0.004708 + 0.004709 + 0.004625 + 0.0045 + 0.004958 + 0.005 + 0.005375 + 0.004708 + 0.007958 + 0.0055 + 0.004834 + 0.004959 + 0.007709 + 0.004875 + 0.004292 + 0.005084 + 0.007708 + 0.004791 + 0.004959 + 0.004833 + 0.005084 + 0.005458 + 0.00475 + 0.004833 + 0.004917 + 0.005167 + 0.0045 + 0.005584 + 0.005333 + 0.004958 + 0.005042 + 0.004625 + 0.005334 + 0.004667 + 0.004958 + 0.005375 + 0.006958 + 0.00725 + 0.007166 + 0.0075 + 0.006416 + 0.00875 + 0.00725 + 0.007291 + 0.006375 + 0.006333 + 0.005875 + 0.005125 + 0.005375 + 0.004917 + 0.005083 + 0.004958 + 0.005292 + 0.004875 + 0.005542 + 0.004625 + 0.004667 + 0.004916 + 0.004958 + 0.00625 + 0.005834 + 0.005917 + 0.005958 + 0.006916 + 0.005916 + 0.006459 + 0.005833 + 0.007125 + 0.007083 + 0.007166 + 0.006792 + 0.006125 + 0.006 + 0.006334 + 0.00725 + 0.007542 + 0.007 + 0.007125 + 0.006625 + 0.00475 + 0.004709 + 0.005083 + 0.004916 + 0.004667 + 0.005334 + 0.0055 + 0.006542 + 0.005542 + 0.005083 + 0.004875 + 0.004833 + 0.008125 + 0.005291 + 0.004792 + 0.004917 + 0.004667 + 0.004667 + 0.004833 + 0.005208 + 0.005083 + 0.004459 + 0.0055 + 0.004625 + 0.004875 + 0.004625 + 0.004791 + 0.004708 + 0.00475 + 0.004625 + 0.005334 + 0.00475 + 0.005042 + 0.004416 + 0.004708 + 0.007625 + 0.004583 + 0.004584 + 0.008584 + 0.004833 + 0.005167 + 0.005208 + 0.005291 + 0.005 + 0.005292 + 0.005375 + 0.006416 + 0.005542 + 0.005834 + 0.005583 + 0.005333 + 0.005083 + 0.005083 + 0.0075 + 0.006667 + 0.006416 + 0.006917 + 0.007125 + 0.007 + 0.006833 + 0.00625 + 0.006459 + 0.008375 + 0.007833 + 0.0085 + 0.0075 + 0.006583 + 0.007292 + 0.006834 + 0.006041 + 0.008375 + 0.005459 + 0.006375 + 0.007042 + 0.007 + 0.006833 + 0.00575 + 0.005958 + 0.005792 + 0.00525 + 0.006458 + 0.007667 + 0.006417 + 0.006916 + 0.008208 + 0.007458 + 0.007833 + 0.007292 + 0.00725 + 0.007875 + 0.007375 + 0.007 + 0.006291 + 0.007125 + 0.006625 + 0.005542 + 0.006875 + 0.00975 + 0.007125 + 0.007083 + 0.005667 + 0.004459 + 0.004667 + 0.00475 + 0.004375 + 0.004333 + 0.004583 + 0.005458 + 0.005583 + 0.004417 + 0.01675 + 0.005041 + 0.005084 + 0.005 + 0.004792 + 0.004625 + 0.00475 + 0.00475 + 0.004958 + 0.004917 + 0.004541 + 0.004792 + 0.004666 + 0.0045 + 0.004625 + 0.00425 + 0.004959 + 0.004375 + 0.004417 + 0.004792 + 0.00475 + 0.004875 + 0.005 + 0.004583 + 0.004875 + 0.004916 + 0.004583 + 0.004625 + 0.004791 + 0.00525 + 0.004792 + 0.00775 + 0.005209 + 0.005084 + 0.004791 + 0.00475 + 0.004667 + 0.004666 + 0.00475 + 0.005167 + 0.005541 + 0.006084 + 0.005959 + 0.00525 + 0.004875 + 0.004666 + 0.005167 + 0.00525 + 0.00825 + 0.007958 + 0.007333 + 0.007583 + 0.007375 + 0.007875 + 0.00825 + 0.010208 + 0.006667 + 0.006834 + 0.006459 + 0.007334 + 0.009167 + 0.005167 + 0.005083 + 0.005333 + 0.005125 + 0.004834 + 0.005125 + 0.0055 + 0.005167 + 0.005083 + 0.005291 + 0.00525 + 0.005458 + 0.005458 + 0.005583 + 0.007917 + 0.008042 + 0.0075 + 0.007417 + 0.0075 + 0.008459 + 0.009167 + 0.006875 + 0.006542 + 0.007583 + 0.006167 + 0.025375 + 0.008583 + 0.008708 + 0.008708 + 0.007125 + 0.008 + 0.0065 + 0.006875 + 0.006292 + 0.005792 + 0.00575 + 0.005542 + 0.006917 + 0.005291 + 0.006 + 0.005625 + 0.007541 + 0.006125 + 0.005667 + 0.005791 + 0.006125 + 0.005458 + 0.005625 + 0.006666 + 0.007125 + 0.007125 + 0.006792 + 0.006416 + 0.006459 + 0.007166 + 0.007084 + 0.007417 + 0.007416 + 0.006833 + 0.006208 + 0.006458 + 0.006 + 0.0065 + 0.00675 + 0.007084 + 0.006833 + 0.006458 + 0.006083 + 0.005542 + 0.005667 + 0.005583 + 0.005708 + 0.005792 + 0.005541 + 0.0055 + 0.005708 + 0.005875 + 0.006083 + 0.008666 + 0.006042 + 0.005792 + 0.006083 + 0.006416 + 0.006084 + 0.007 + 0.008333 + 0.0075 + 0.013458 + 0.009292 + 0.00725 + 0.006792 + 0.006625 + 0.00675 + 0.008125 + 0.008791 + 0.009708 + 0.009625 + 0.007333 + 0.007167 + 0.007375 + 0.008375 + 0.007542 + 0.007209 + 0.007584 + 0.009459 + 0.008625 + 0.007667 + 0.008791 + 0.007417 + 0.006708 + 0.00725 + 0.006917 + 0.010583 + 0.019875 + 0.016 + 0.011 + 0.009916 + 0.007708 + 0.008583 + 0.009208 + 0.008208 + 0.010292 + 0.00725 + 0.007709 + 0.008834 + 0.0095 + 0.011208 + 0.007125 + 0.007416 + 0.006833 + 0.006875 + 0.006125 + 0.007125 + 0.0065 + 0.006792 + 0.0065 + 0.005959 + 0.007084 + 0.007875 + 0.006792 + 0.007125 + 0.005 + 0.007417 + 0.006625 + 0.006084 + 0.006917 + 0.005458 + 0.005375 + 0.005375 + 0.006625 + 0.007458 + 0.007875 + 0.007833 + 0.006875 + 0.006167 + 0.006541 + 0.007292 + 0.007166 + 0.007917 + 0.007584 + 0.007041 + 0.006375 + 0.006167 + 0.006834 + 0.006334 + 0.008291 + 0.007584 + 0.008083 + 0.008417 + 0.006666 + 0.005958 + 0.005708 + 0.006125 + 0.007166 + 0.007333 + 0.007083 + 0.007583 + 0.007167 + 0.00675 + 0.006167 + 0.006875 + 0.0075 + 0.006667 + 0.005875 + 0.00625 + 0.008167 + 0.007292 + 0.009334 + 0.006709 + 0.006792 + 0.007458 + 0.007 + 0.008 + 0.006291 + 0.008542 + 0.012583 + 0.006583 + 0.009166 + 0.009458 + 0.008667 + 0.010041 + 0.00825 + 0.010125 + 0.009 + 0.009666 + 0.008375 + 0.008375 + 0.007 + 0.008 + 0.007458 + 0.008167 + 0.007375 + 0.007833 + 0.007042 + 0.007916 + 0.007875 + 0.006125 + 0.006125 + 0.008542 + 0.006916 + 0.008125 + 0.007708 + 0.005875 + 0.007375 + 0.0075 + 0.008875 + 0.008292 + 0.008 + 0.008667 + 0.008125 + 0.007584 + 0.006459 + 0.005541 + 0.004917 + 0.005209 + 0.005458 + 0.005583 + 0.004917 + 0.0055 + 0.005708 + 0.005458 + 0.004708 + 0.005292 + 0.005625 + 0.005375 + 0.005083 + 0.00525 + 0.005333 + 0.005667 + 0.005417 + 0.005291 + 0.005 + 0.005583 + 0.004875 + 0.005209 + 0.005334 + 0.006 + 0.005417 + 0.005208 + 0.005292 + 0.004917 + 0.004833 + 0.006125 + 0.005291 + 0.005584 + 0.004875 + 0.004875 + 0.005292 + 0.005042 + 0.005208 + 0.005583 + 0.005417 + 0.00575 + 0.005458 + 0.005166 + 0.005041 + 0.004875 + 0.004875 + 0.006417 + 0.005833 + 0.005916 + 0.005167 + 0.005459 + 0.007291 + 0.006167 + 0.006792 + 0.006791 + 0.011917 + 0.006958 + 0.006417 + 0.005875 + 0.006125 + 0.008 + 0.008041 + 0.007333 + 0.006833 + 0.005833 + 0.0055 + 0.005542 + 0.005166 + 0.005833 + 0.005792 + 0.00575 + 0.005834 + 0.005416 + 0.005458 + 0.005417 + 0.005292 + 0.007 + 0.006917 + 0.00675 + 0.007 + 0.006 + 0.005625 + 0.006209 + 0.006209 + 0.005917 + 0.005875 + 0.008209 + 0.007584 + 0.007417 + 0.007375 + 0.0055 + 0.005958 + 0.006292 + 0.008125 + 0.00775 + 0.006917 + 0.007 + 0.006583 + 0.005417 + 0.005 + 0.005875 + 0.005209 + 0.005166 + 0.005125 + 0.005208 + 0.00575 + 0.005958 + 0.004625 + 0.005 + 0.005625 + 0.005708 + 0.0055 + 0.00525 + 0.005625 + 0.005333 + 0.0055 + 0.005208 + 0.005083 + 0.004958 + 0.004875 + 0.005416 + 0.005417 + 0.004959 + 0.005125 + 0.00525 + 0.005125 + 0.005042 + 0.005166 + 0.005334 + 0.00525 + 0.005083 + 0.005166 + 0.005084 + 0.004708 + 0.004625 + 0.0055 + 0.005041 + 0.004667 + 0.005 + 0.005292 + 0.005333 + 0.004958 + 0.004875 + 0.004875 + 0.00525 + 0.005166 + 0.004917 + 0.005375 + 0.0055 + 0.005208 + 0.005167 + 0.007792 + 0.006416 + 0.00625 + 0.006458 + 0.006666 + 0.006208 + 0.005833 + 0.006292 + 0.005958 + 0.007292 + 0.007458 + 0.007375 + 0.006708 + 0.005416 + 0.00525 + 0.005375 + 0.005708 + 0.005542 + 0.005542 + 0.005791 + 0.005334 + 0.005041 + 0.005209 + 0.00525 + 0.0055 + 0.006791 + 0.006292 + 0.0065 + 0.006125 + 0.006042 + 0.005542 + 0.005167 + 0.006375 + 0.006083 + 0.006 + 0.00625 + 0.007584 + 0.007208 + 0.007333 + 0.006542 + 0.006584 + 0.005917 + 0.006333 + 0.007041 + 0.007625 + 0.007 + 0.00725 + 0.006125 + 0.005 + 0.005125 + 0.004917 + 0.004666 + 0.004417 + 0.004708 + 0.005 + 0.005166 + 0.005167 + 0.00525 + 0.004917 + 0.00525 + 0.005292 + 0.005292 + 0.004833 + 0.004833 + 0.004834 + 0.005 + 0.00475 + 0.00475 + 0.004833 + 0.005166 + 0.004708 + 0.004792 + 0.004708 + 0.004958 + 0.004833 + 0.005042 + 0.004792 + 0.005083 + 0.00475 + 0.004625 + 0.004458 + 0.004667 + 0.004375 + 0.004667 + 0.004791 + 0.004792 + 0.005083 + 0.005041 + 0.004625 + 0.004875 + 0.005083 + 0.004917 + 0.004709 + 0.004833 + 0.005125 + 0.004792 + 0.005125 + 0.005542 + 0.005166 + 0.004959 + 0.006875 + 0.006375 + 0.005666 + 0.006208 + 0.006209 + 0.006209 + 0.006375 + 0.005834 + 0.005709 + 0.007125 + 0.007042 + 0.006792 + 0.007 + 0.005792 + 0.005 + 0.005416 + 0.005083 + 0.004917 + 0.005167 + 0.005125 + 0.0055 + 0.005084 + 0.005167 + 0.006584 + 0.006167 + 0.006 + 0.006 + 0.005833 + 0.00525 + 0.005167 + 0.005 + 0.004666 + 0.005917 + 0.006041 + 0.006208 + 0.007333 + 0.007417 + 0.006625 + 0.006625 + 0.005458 + 0.004541 + 0.006042 + 0.005791 + 0.007 + 0.00725 + 0.007041 + 0.006792 + 0.006542 + 0.004958 + 0.004458 + 0.004875 + 0.00475 + 0.005166 + 0.004959 + 0.004542 + 0.004542 + 0.00525 + 0.005 + 0.004416 + 0.004833 + 0.004417 + 0.005292 + 0.005 + 0.00525 + 0.004917 + 0.005167 + 0.004291 + 0.004792 + 0.004916 + 0.004792 + 0.004708 + 0.004875 + 0.004542 + 0.005 + 0.004708 + 0.004916 + 0.004625 + 0.004875 + 0.004625 + 0.004709 + 0.004625 + 0.004542 + 0.0045 + 0.0045 + 0.00475 + 0.004417 + 0.004375 + 0.004583 + 0.004917 + 0.00475 + 0.004625 + 0.004459 + 0.005042 + 0.004291 + 0.004834 + 0.004625 + 0.005541 + 0.00525 + 0.005 + 0.004958 + 0.007125 + 0.006042 + 0.006083 + 0.005541 + 0.006083 + 0.005625 + 0.006541 + 0.005708 + 0.005917 + 0.006708 + 0.007208 + 0.0065 + 0.0065 + 0.005833 + 0.005 + 0.004833 + 0.004875 + 0.005 + 0.005 + 0.005083 + 0.004792 + 0.005459 + 0.005292 + 0.00675 + 0.005916 + 0.005792 + 0.006 + 0.00625 + 0.005042 + 0.005125 + 0.005 + 0.004958 + 0.006083 + 0.006167 + 0.005917 + 0.0075 + 0.007083 + 0.006666 + 0.006708 + 0.00625 + 0.00525 + 0.005791 + 0.006792 + 0.006959 + 0.006708 + 0.006333 + 0.006583 + 0.005708 + 0.004542 + 0.004625 + 0.005333 + 0.004458 + 0.004667 + 0.004417 + 0.00475 + 0.004292 + 0.00475 + 0.005333 + 0.004458 + 0.005417 + 0.004541 + 0.004459 + 0.004416 + 0.005 + 0.004584 + 0.00475 + 0.005083 + 0.005666 + 0.005917 + 0.005625 + 0.005625 + 0.00525 + 0.004584 + 0.004667 + 0.004916 + 0.005125 + 0.004791 + 0.005292 + 0.004792 + 0.005166 + 0.004583 + 0.005 + 0.004292 + 0.004667 + 0.004542 + 0.004292 + 0.00425 + 0.00475 + 0.004875 + 0.00475 + 0.00475 + 0.00475 + 0.004667 + 0.005208 + 0.004667 + 0.004917 + 0.005 + 0.005333 + 0.006375 + 0.006 + 0.005917 + 0.00575 + 0.005917 + 0.005208 + 0.005333 + 0.005083 + 0.006042 + 0.006 + 0.005917 + 0.006958 + 0.007083 + 0.006625 + 0.006709 + 0.006125 + 0.004958 + 0.004916 + 0.004917 + 0.004916 + 0.004917 + 0.005292 + 0.005417 + 0.004917 + 0.005041 + 0.006625 + 0.006458 + 0.005792 + 0.005084 + 0.005625 + 0.005417 + 0.005166 + 0.00525 + 0.005417 + 0.006291 + 0.006833 + 0.006583 + 0.008708 + 0.00775 + 0.008125 + 0.007292 + 0.007459 + 0.006625 + 0.007167 + 0.00775 + 0.006958 + 0.006083 + 0.006417 + 0.006375 + 0.005375 + 0.006625 + 0.005792 + 0.005833 + 0.005958 + 0.005833 + 0.005666 + 0.006375 + 0.0065 + 0.0065 + 0.005916 + 0.007125 + 0.006792 + 0.006125 + 0.00475 + 0.004542 + 0.005125 + 0.004959 + 0.005125 + 0.004875 + 0.004791 + 0.004709 + 0.004958 + 0.005041 + 0.004667 + 0.005 + 0.005084 + 0.005 + 0.00475 + 0.00475 + 0.004875 + 0.00525 + 0.004875 + 0.004958 + 0.005459 + 0.004917 + 0.004416 + 0.004708 + 0.0045 + 0.004417 + 0.004417 + 0.004458 + 0.004625 + 0.005 + 0.005333 + 0.004708 + 0.00475 + 0.004958 + 0.005125 + 0.004917 + 0.005209 + 0.004959 + 0.004917 + 0.005166 + 0.005208 + 0.005709 + 0.005375 + 0.005333 + 0.005709 + 0.006 + 0.006083 + 0.007958 + 0.007666 + 0.007542 + 0.008125 + 0.007542 + 0.006166 + 0.006084 + 0.005542 + 0.00475 + 0.004625 + 0.004958 + 0.006292 + 0.0065 + 0.006458 + 0.006917 + 0.006834 + 0.006375 + 0.006333 + 0.006 + 0.005166 + 0.005125 + 0.005084 + 0.005 + 0.005292 + 0.004917 + 0.006167 + 0.005959 + 0.007709 + 0.007792 + 0.007792 + 0.007333 + 0.007375 + 0.006167 + 0.007625 + 0.008667 + 0.007708 + 0.007083 + 0.01175 + 0.008083 + 0.006208 + 0.008042 + 0.006792 + 0.006166 + 0.00625 + 0.0065 + 0.006792 + 0.006833 + 0.005958 + 0.005375 + 0.004708 + 0.00525 + 0.004625 + 0.005333 + 0.0055 + 0.00475 + 0.00475 + 0.004833 + 0.005167 + 0.004958 + 0.004584 + 0.004959 + 0.004917 + 0.005042 + 0.005167 + 0.00475 + 0.004916 + 0.004875 + 0.004709 + 0.004584 + 0.005125 + 0.005083 + 0.00475 + 0.0045 + 0.004917 + 0.004667 + 0.004875 + 0.004625 + 0.004709 + 0.00475 + 0.004417 + 0.004417 + 0.004708 + 0.004542 + 0.004916 + 0.00525 + 0.005 + 0.005083 + 0.004625 + 0.005167 + 0.00525 + 0.006041 + 0.005292 + 0.005 + 0.005167 + 0.004958 + 0.005041 + 0.00525 + 0.004833 + 0.006125 + 0.007417 + 0.008333 + 0.007791 + 0.007542 + 0.007125 + 0.007625 + 0.007417 + 0.00625 + 0.005167 + 0.006708 + 0.007125 + 0.006333 + 0.006458 + 0.006333 + 0.006208 + 0.006666 + 0.006125 + 0.00525 + 0.004959 + 0.005084 + 0.005459 + 0.005541 + 0.005375 + 0.005 + 0.006042 + 0.00625 + 0.007709 + 0.008667 + 0.007125 + 0.006958 + 0.006667 + 0.0065 + 0.006292 + 0.005041 + 0.005209 + 0.008292 + 0.008416 + 0.007917 + 0.00725 + 0.007375 + 0.00775 + 0.008625 + 0.006541 + 0.006583 + 0.006 + 0.006458 + 0.006041 + 0.006417 + 0.006209 + 0.004834 + 0.005 + 0.004792 + 0.00525 + 0.00475 + 0.005125 + 0.004833 + 0.005 + 0.004917 + 0.00425 + 0.004708 + 0.005084 + 0.005084 + 0.005208 + 0.00475 + 0.004917 + 0.005041 + 0.005125 + 0.004625 + 0.00475 + 0.005292 + 0.004917 + 0.00475 + 0.00525 + 0.004625 + 0.005458 + 0.00525 + 0.005416 + 0.004625 + 0.005083 + 0.005333 + 0.00475 + 0.004416 + 0.0045 + 0.004792 + 0.005166 + 0.004375 + 0.004625 + 0.004959 + 0.005333 + 0.004834 + 0.005084 + 0.005417 + 0.0055 + 0.004958 + 0.005125 + 0.0055 + 0.005333 + 0.005 + 0.005125 + 0.005375 + 0.006125 + 0.005667 + 0.007791 + 0.008292 + 0.007541 + 0.007208 + 0.007667 + 0.006584 + 0.005917 + 0.005291 + 0.005042 + 0.005 + 0.005125 + 0.007208 + 0.006833 + 0.006792 + 0.007334 + 0.006459 + 0.006667 + 0.006583 + 0.005708 + 0.005167 + 0.005166 + 0.005417 + 0.005209 + 0.005042 + 0.006041 + 0.005833 + 0.007541 + 0.008167 + 0.008084 + 0.007209 + 0.007916 + 0.007708 + 0.006 + 0.007625 + 0.008 + 0.007584 + 0.00775 + 0.007208 + 0.006917 + 0.009292 + 0.006416 + 0.006166 + 0.006083 + 0.0065 + 0.006083 + 0.005958 + 0.006166 + 0.004917 + 0.005167 + 0.005083 + 0.005625 + 0.005875 + 0.005958 + 0.006083 + 0.004958 + 0.004458 + 0.005 + 0.004542 + 0.0045 + 0.004584 + 0.005375 + 0.005917 + 0.006041 + 0.005542 + 0.006 + 0.005292 + 0.00475 + 0.005208 + 0.004958 + 0.004542 + 0.006041 + 0.005917 + 0.005917 + 0.0055 + 0.005375 + 0.004583 + 0.005042 + 0.00575 + 0.007166 + 0.007167 + 0.006709 + 0.006792 + 0.006417 + 0.006333 + 0.005958 + 0.006 + 0.005959 + 0.006125 + 0.007167 + 0.005333 + 0.005333 + 0.004958 + 0.00475 + 0.004959 + 0.0045 + 0.005167 + 0.00525 + 0.005292 + 0.006167 + 0.005958 + 0.007458 + 0.007 + 0.007292 + 0.007542 + 0.007167 + 0.005333 + 0.005208 + 0.006125 + 0.005542 + 0.005291 + 0.006375 + 0.006083 + 0.007625 + 0.00725 + 0.006875 + 0.006791 + 0.006875 + 0.006625 + 0.006875 + 0.005834 + 0.006334 + 0.005584 + 0.005375 + 0.005833 + 0.004541 + 0.006209 + 0.006167 + 0.0075 + 0.007625 + 0.007 + 0.008 + 0.006708 + 0.005417 + 0.005917 + 0.008958 + 0.008375 + 0.007791 + 0.007625 + 0.0065 + 0.006084 + 0.00825 + 0.006292 + 0.006583 + 0.007333 + 0.008375 + 0.006208 + 0.00525 + 0.005416 + 0.007 + 0.005542 + 0.005959 + 0.006292 + 0.006 + 0.006125 + 0.005625 + 0.004792 + 0.005125 + 0.004959 + 0.004917 + 0.0055 + 0.00575 + 0.005 + 0.005458 + 0.005792 + 0.005791 + 0.005583 + 0.0055 + 0.005417 + 0.00525 + 0.00525 + 0.005625 + 0.005542 + 0.006125 + 0.005584 + 0.006584 + 0.006334 + 0.005958 + 0.0055 + 0.00525 + 0.004709 + 0.004834 + 0.0055 + 0.005417 + 0.005708 + 0.0045 + 0.005583 + 0.0055 + 0.005208 + 0.005583 + 0.005625 + 0.005167 + 0.005083 + 0.005667 + 0.005667 + 0.005625 + 0.005375 + 0.006 + 0.006292 + 0.00775 + 0.010292 + 0.009708 + 0.009208 + 0.007833 + 0.008208 + 0.007458 + 0.007291 + 0.006375 + 0.005042 + 0.006583 + 0.006666 + 0.006167 + 0.006417 + 0.006083 + 0.004625 + 0.00525 + 0.005167 + 0.005375 + 0.005083 + 0.005458 + 0.005083 + 0.00525 + 0.005209 + 0.005334 + 0.005333 + 0.005292 + 0.006666 + 0.00775 + 0.007333 + 0.007083 + 0.006958 + 0.006584 + 0.005875 + 0.0055 + 0.008042 + 0.008 + 0.006834 + 0.007084 + 0.006541 + 0.006375 + 0.007791 + 0.007292 + 0.00675 + 0.00625 + 0.005875 + 0.004584 + 0.005041 + 0.005084 + 0.004916 + 0.004833 + 0.005167 + 0.0045 + 0.004792 + 0.005 + 0.005042 + 0.005291 + 0.005 + 0.00525 + 0.004666 + 0.004708 + 0.004542 + 0.004959 + 0.004458 + 0.004583 + 0.004917 + 0.005083 + 0.004459 + 0.004792 + 0.00475 + 0.005 + 0.004875 + 0.004708 + 0.004958 + 0.005125 + 0.004708 + 0.004833 + 0.005083 + 0.005083 + 0.004667 + 0.004667 + 0.005 + 0.005167 + 0.004916 + 0.005041 + 0.004625 + 0.004583 + 0.004291 + 0.005167 + 0.004916 + 0.0045 + 0.005333 + 0.005542 + 0.005208 + 0.004458 + 0.004541 + 0.005083 + 0.005417 + 0.005041 + 0.005083 + 0.006125 + 0.008125 + 0.0075 + 0.007125 + 0.006959 + 0.006542 + 0.006 + 0.007416 + 0.006625 + 0.006333 + 0.005875 + 0.006083 + 0.005458 + 0.005125 + 0.004958 + 0.005291 + 0.005 + 0.005125 + 0.004708 + 0.004833 + 0.005375 + 0.004917 + 0.004667 + 0.004958 + 0.005 + 0.005 + 0.004792 + 0.007333 + 0.007791 + 0.006875 + 0.006958 + 0.006292 + 0.006 + 0.006958 + 0.007375 + 0.006209 + 0.007459 + 0.00625 + 0.005833 + 0.006125 + 0.007875 + 0.006959 + 0.006917 + 0.005916 + 0.00525 + 0.004958 + 0.004875 + 0.00525 + 0.005042 + 0.004625 + 0.004458 + 0.004584 + 0.005042 + 0.004666 + 0.005042 + 0.005208 + 0.004792 + 0.005167 + 0.004583 + 0.0045 + 0.004708 + 0.004542 + 0.004334 + 0.004708 + 0.004458 + 0.004458 + 0.004167 + 0.004833 + 0.0045 + 0.004708 + 0.004916 + 0.005084 + 0.004875 + 0.004875 + 0.004834 + 0.004917 + 0.005625 + 0.00525 + 0.005792 + 0.005375 + 0.005041 + 0.0045 + 0.005083 + 0.004792 + 0.004709 + 0.005166 + 0.005042 + 0.004542 + 0.0045 + 0.005 + 0.004542 + 0.003958 + 0.004958 + 0.005375 + 0.005 + 0.005417 + 0.004834 + 0.004791 + 0.005208 + 0.005709 + 0.005375 + 0.009709 + 0.008041 + 0.007417 + 0.007042 + 0.006375 + 0.00625 + 0.008042 + 0.006084 + 0.006625 + 0.006333 + 0.006458 + 0.005709 + 0.005417 + 0.005166 + 0.005875 + 0.005625 + 0.0055 + 0.005167 + 0.0055 + 0.004792 + 0.005292 + 0.005166 + 0.005125 + 0.005334 + 0.006292 + 0.005958 + 0.008416 + 0.007958 + 0.007667 + 0.007166 + 0.00625 + 0.006125 + 0.007834 + 0.007458 + 0.006625 + 0.007375 + 0.00625 + 0.006084 + 0.005709 + 0.007291 + 0.00725 + 0.007041 + 0.005542 + 0.005625 + 0.004625 + 0.004458 + 0.004833 + 0.005333 + 0.005791 + 0.006459 + 0.006834 + 0.006625 + 0.006 + 0.007125 + 0.006875 + 0.006542 + 0.006 + 0.005625 + 0.005583 + 0.004959 + 0.004792 + 0.004583 + 0.004666 + 0.004542 + 0.004709 + 0.004625 + 0.004792 + 0.004458 + 0.00475 + 0.005 + 0.005042 + 0.004583 + 0.00475 + 0.004875 + 0.004791 + 0.004667 + 0.004916 + 0.004667 + 0.004542 + 0.004792 + 0.004917 + 0.004958 + 0.004542 + 0.005125 + 0.004791 + 0.005208 + 0.004833 + 0.004709 + 0.004875 + 0.004417 + 0.0055 + 0.005334 + 0.005042 + 0.004375 + 0.004792 + 0.004875 + 0.004625 + 0.004625 + 0.004458 + 0.005333 + 0.007834 + 0.009959 + 0.007584 + 0.007042 + 0.006166 + 0.005792 + 0.00775 + 0.0065 + 0.005917 + 0.006292 + 0.006083 + 0.006291 + 0.005708 + 0.004917 + 0.005208 + 0.005083 + 0.004875 + 0.004666 + 0.00525 + 0.004375 + 0.005 + 0.004833 + 0.005167 + 0.004958 + 0.00475 + 0.005208 + 0.005917 + 0.008708 + 0.007375 + 0.0075 + 0.007416 + 0.007542 + 0.007167 + 0.010916 + 0.00775 + 0.008875 + 0.007584 + 0.007834 + 0.006916 + 0.007166 + 0.008625 + 0.008666 + 0.007167 + 0.005791 + 0.00575 + 0.005541 + 0.005292 + 0.005416 + 0.006042 + 0.0055 + 0.005875 + 0.005833 + 0.006875 + 0.006667 + 0.006875 + 0.00625 + 0.005875 + 0.005375 + 0.005625 + 0.005292 + 0.005416 + 0.005625 + 0.005792 + 0.005417 + 0.006166 + 0.006042 + 0.006542 + 0.006625 + 0.007042 + 0.006625 + 0.006 + 0.005584 + 0.005125 + 0.005375 + 0.005166 + 0.004792 + 0.004667 + 0.004708 + 0.004916 + 0.005083 + 0.006208 + 0.00675 + 0.006917 + 0.006917 + 0.006 + 0.006625 + 0.005833 + 0.006 + 0.006958 + 0.007083 + 0.006125 + 0.006209 + 0.006334 + 0.006375 + 0.006333 + 0.005917 + 0.006625 + 0.006834 + 0.006041 + 0.007 + 0.008625 + 0.008875 + 0.007875 + 0.006875 + 0.006167 + 0.007 + 0.006583 + 0.006541 + 0.006833 + 0.00675 + 0.009667 + 0.006917 + 0.006958 + 0.006666 + 0.006542 + 0.006416 + 0.006708 + 0.006625 + 0.006541 + 0.006542 + 0.007125 + 0.006084 + 0.006 + 0.006334 + 0.006334 + 0.007125 + 0.006208 + 0.006417 + 0.007959 + 0.007875 + 0.007042 + 0.006584 + 0.006042 + 0.005583 + 0.005125 + 0.007375 + 0.008416 + 0.011875 + 0.009958 + 0.010458 + 0.010875 + 0.011292 + 0.009875 + 0.008375 + 0.007042 + 0.005958 + 0.005167 + 0.005375 + 0.00625 + 0.006167 + 0.005583 + 0.005541 + 0.005625 + 0.006416 + 0.006 + 0.005917 + 0.0065 + 0.005916 + 0.005792 + 0.006833 + 0.005417 + 0.005084 + 0.004708 + 0.004958 + 0.004833 + 0.00475 + 0.0045 + 0.004542 + 0.0055 + 0.005 + 0.004417 + 0.004625 + 0.004667 + 0.006 + 0.005875 + 0.006041 + 0.004917 + 0.005208 + 0.004375 + 0.0045 + 0.004875 + 0.004875 + 0.004542 + 0.004958 + 0.004917 + 0.005125 + 0.004667 + 0.004875 + 0.004584 + 0.004959 + 0.00475 + 0.005334 + 0.005334 + 0.005292 + 0.005041 + 0.004875 + 0.00475 + 0.004416 + 0.004833 + 0.004833 + 0.005666 + 0.007708 + 0.007625 + 0.006666 + 0.006583 + 0.00675 + 0.006833 + 0.009125 + 0.007041 + 0.00625 + 0.007334 + 0.005917 + 0.005625 + 0.005417 + 0.005625 + 0.005125 + 0.005083 + 0.005125 + 0.005417 + 0.005042 + 0.005417 + 0.00525 + 0.005291 + 0.004875 + 0.004875 + 0.004291 + 0.006084 + 0.007916 + 0.007458 + 0.007583 + 0.007708 + 0.007333 + 0.007042 + 0.008291 + 0.007708 + 0.006792 + 0.00775 + 0.007667 + 0.007042 + 0.007833 + 0.008541 + 0.008375 + 0.008125 + 0.006834 + 0.006375 + 0.006084 + 0.005959 + 0.005 + 0.0045 + 0.004917 + 0.0055 + 0.005542 + 0.005708 + 0.004792 + 0.005042 + 0.005833 + 0.005167 + 0.004958 + 0.005125 + 0.00475 + 0.004708 + 0.005416 + 0.005084 + 0.005 + 0.005125 + 0.004792 + 0.005291 + 0.005 + 0.004875 + 0.004791 + 0.005083 + 0.005041 + 0.0045 + 0.005042 + 0.00475 + 0.00475 + 0.004542 + 0.005 + 0.004875 + 0.004875 + 0.00475 + 0.005042 + 0.004667 + 0.004458 + 0.00525 + 0.005375 + 0.005125 + 0.005416 + 0.006125 + 0.006458 + 0.006083 + 0.006458 + 0.006958 + 0.007041 + 0.006208 + 0.005083 + 0.00525 + 0.00525 + 0.004667 + 0.0055 + 0.0065 + 0.008792 + 0.008584 + 0.008 + 0.007583 + 0.007875 + 0.007375 + 0.008959 + 0.007375 + 0.006375 + 0.006916 + 0.006917 + 0.006667 + 0.00625 + 0.005959 + 0.0055 + 0.005542 + 0.005458 + 0.005167 + 0.005083 + 0.005625 + 0.005041 + 0.00525 + 0.005167 + 0.00525 + 0.005417 + 0.005416 + 0.007833 + 0.008125 + 0.007667 + 0.007416 + 0.007875 + 0.008 + 0.009084 + 0.007833 + 0.007125 + 0.008084 + 0.007375 + 0.008125 + 0.006833 + 0.007875 + 0.007791 + 0.007583 + 0.006459 + 0.006167 + 0.006083 + 0.0065 + 0.005292 + 0.004416 + 0.0045 + 0.005916 + 0.006042 + 0.005333 + 0.004667 + 0.005333 + 0.005333 + 0.004875 + 0.004542 + 0.005 + 0.005 + 0.004709 + 0.004959 + 0.004791 + 0.004917 + 0.004917 + 0.005125 + 0.005 + 0.004791 + 0.005084 + 0.004917 + 0.005 + 0.005167 + 0.004916 + 0.005 + 0.004792 + 0.00475 + 0.004791 + 0.004875 + 0.005084 + 0.005 + 0.00525 + 0.005042 + 0.004875 + 0.004833 + 0.004708 + 0.005125 + 0.00525 + 0.005 + 0.005084 + 0.004834 + 0.004959 + 0.004959 + 0.00525 + 0.004792 + 0.005291 + 0.005667 + 0.005208 + 0.005375 + 0.005459 + 0.005334 + 0.005125 + 0.006583 + 0.008625 + 0.007959 + 0.007584 + 0.007666 + 0.00775 + 0.00775 + 0.006458 + 0.006042 + 0.006208 + 0.007333 + 0.006916 + 0.007542 + 0.00675 + 0.006625 + 0.006708 + 0.007875 + 0.006542 + 0.005208 + 0.005416 + 0.005292 + 0.0055 + 0.0055 + 0.005625 + 0.005583 + 0.006583 + 0.006709 + 0.008333 + 0.00875 + 0.00775 + 0.008167 + 0.007292 + 0.006958 + 0.007875 + 0.00625 + 0.00775 + 0.00725 + 0.006917 + 0.007083 + 0.006333 + 0.00625 + 0.0075 + 0.006792 + 0.006208 + 0.006083 + 0.006834 + 0.006459 + 0.006209 + 0.00625 + 0.006334 + 0.006709 + 0.004958 + 0.004833 + 0.004708 + 0.005584 + 0.004959 + 0.004917 + 0.004708 + 0.00475 + 0.004666 + 0.005209 + 0.005875 + 0.005625 + 0.005958 + 0.005041 + 0.005209 + 0.004625 + 0.004667 + 0.005125 + 0.005167 + 0.004708 + 0.0045 + 0.00475 + 0.00425 + 0.004209 + 0.004709 + 0.004959 + 0.004666 + 0.004792 + 0.004459 + 0.005 + 0.004542 + 0.004708 + 0.004292 + 0.004791 + 0.004875 + 0.004959 + 0.004666 + 0.004833 + 0.004666 + 0.005042 + 0.00525 + 0.005417 + 0.005 + 0.005333 + 0.005083 + 0.004958 + 0.004959 + 0.004875 + 0.004917 + 0.005584 + 0.005916 + 0.007542 + 0.006958 + 0.006375 + 0.007209 + 0.00675 + 0.006041 + 0.005458 + 0.00525 + 0.006084 + 0.006084 + 0.005792 + 0.006333 + 0.005792 + 0.005583 + 0.004958 + 0.005209 + 0.005375 + 0.005125 + 0.005375 + 0.005333 + 0.005 + 0.005042 + 0.004875 + 0.005 + 0.004875 + 0.006333 + 0.007083 + 0.007 + 0.007334 + 0.006834 + 0.006125 + 0.025083 + 0.008875 + 0.008625 + 0.00675 + 0.006875 + 0.006333 + 0.009875 + 0.006584 + 0.007084 + 0.007 + 0.007708 + 0.005708 + 0.005458 + 0.005458 + 0.005666 + 0.006208 + 0.005709 + 0.006 + 0.005833 + 0.005167 + 0.005583 + 0.004667 + 0.008459 + 0.006042 + 0.005542 + 0.005584 + 0.005291 + 0.005875 + 0.005792 + 0.005416 + 0.005333 + 0.005583 + 0.005625 + 0.005542 + 0.005583 + 0.005667 + 0.005333 + 0.00575 + 0.005458 + 0.005833 + 0.006 + 0.005292 + 0.005458 + 0.005708 + 0.005333 + 0.005583 + 0.006 + 0.00575 + 0.005416 + 0.005792 + 0.0055 + 0.005458 + 0.005417 + 0.005291 + 0.005459 + 0.0055 + 0.005708 + 0.005166 + 0.00775 + 0.006167 + 0.005625 + 0.005709 + 0.005959 + 0.005334 + 0.005584 + 0.005875 + 0.005709 + 0.012 + 0.007167 + 0.006791 + 0.006583 + 0.007083 + 0.006708 + 0.006917 + 0.009042 + 0.008167 + 0.005708 + 0.007416 + 0.005917 + 0.005834 + 0.006084 + 0.005792 + 0.0055 + 0.01275 + 0.007416 + 0.0105 + 0.005958 + 0.005791 + 0.006125 + 0.005958 + 0.006 + 0.005958 + 0.00575 + 0.007375 + 0.0125 + 0.007584 + 0.007375 + 0.006375 + 0.006542 + 0.006208 + 0.006375 + 0.009625 + 0.0055 + 0.006209 + 0.005875 + 0.00625 + 0.005958 + 0.006542 + 0.009625 + 0.007042 + 0.005458 + 0.005292 + 0.005125 + 0.004833 + 0.005 + 0.005125 + 0.005167 + 0.005375 + 0.005417 + 0.00575 + 0.004792 + 0.00525 + 0.025584 + 0.022375 + 0.006458 + 0.005833 + 0.006041 + 0.005667 + 0.005875 + 0.00625 + 0.00575 + 0.00525 + 0.005833 + 0.0055 + 0.006083 + 0.005709 + 0.006 + 0.005709 + 0.005834 + 0.006042 + 0.006541 + 0.005666 + 0.006417 + 0.005958 + 0.005292 + 0.004958 + 0.00525 + 0.005334 + 0.005666 + 0.005875 + 0.005583 + 0.00575 + 0.005625 + 0.007333 + 0.00675 + 0.006375 + 0.006709 + 0.008 + 0.010333 + 0.01125 + 0.007541 + 0.007458 + 0.007125 + 0.006916 + 0.006458 + 0.007375 + 0.00675 + 0.009334 + 0.013667 + 0.01025 + 0.010375 + 0.008583 + 0.006666 + 0.0075 + 0.00825 + 0.008416 + 0.007208 + 0.008416 + 0.007333 + 0.007333 + 0.007833 + 0.010958 + 0.007208 + 0.007625 + 0.00825 + 0.007542 + 0.006416 + 0.006542 + 0.006292 + 0.007833 + 0.007875 + 0.006541 + 0.007833 + 0.008208 + 0.01025 + 0.009083 + 0.0135 + 0.009958 + 0.0085 + 0.00775 + 0.007333 + 0.006792 + 0.007209 + 0.007875 + 0.006542 + 0.011916 + 0.008917 + 0.008125 + 0.007917 + 0.008167 + 0.00725 + 0.00575 + 0.006084 + 0.007083 + 0.019208 + 0.013416 + 0.008541 + 0.008292 + 0.009125 + 0.006792 + 0.007042 + 0.008334 + 0.007916 + 0.006792 + 0.006833 + 0.006959 + 0.00625 + 0.006417 + 0.006375 + 0.006584 + 0.007 + 0.00675 + 0.006542 + 0.007042 + 0.006333 + 0.007416 + 0.007708 + 0.007875 + 0.006541 + 0.006917 + 0.007334 + 0.007625 + 0.00625 + 0.006208 + 0.007375 + 0.007791 + 0.007917 + 0.007375 + 0.007292 + 0.006292 + 0.006666 + 0.006958 + 0.030458 + 0.007 + 0.006583 + 0.006084 + 0.006166 + 0.006042 + 0.006708 + 0.007792 + 0.007375 + 0.006792 + 0.007166 + 0.011833 + 0.008458 + 0.007083 + 0.006833 + 0.007833 + 0.009583 + 0.007667 + 0.007958 + 0.007959 + 0.008625 + 0.007583 + 0.007083 + 0.006708 + 0.006916 + 0.007708 + 0.006542 + 0.0065 + 0.00625 + 0.006292 + 0.006 + 0.006125 + 0.006042 + 0.005833 + 0.00675 + 0.006375 + 0.010541 + 0.007208 + 0.007041 + 0.0075 + 0.0185 + 0.006792 + 0.005458 + 0.007083 + 0.006333 + 0.006583 + 0.006584 + 0.008333 + 0.008709 + 0.008292 + 0.007209 + 0.00875 + 0.008375 + 0.007916 + 0.008083 + 0.0075 + 0.007166 + 0.007625 + 0.007333 + 0.005875 + 0.005208 + 0.005833 + 0.006 + 0.005875 + 0.005916 + 0.005583 + 0.0065 + 0.006958 + 0.00825 + 0.007125 + 0.020583 + 0.009708 + 0.006542 + 0.005625 + 0.005417 + 0.006834 + 0.006917 + 0.006708 + 0.005542 + 0.005292 + 0.005917 + 0.00675 + 0.006708 + 0.006833 + 0.006875 + 0.006 + 0.005875 + 0.005625 + 0.006375 + 0.005583 + 0.005791 + 0.006083 + 0.005417 + 0.006084 + 0.006709 + 0.007209 + 0.007709 + 0.006542 + 0.006833 + 0.006375 + 0.006084 + 0.005292 + 0.005041 + 0.005333 + 0.00525 + 0.0055 + 0.005917 + 0.006875 + 0.006042 + 0.006916 + 0.007083 + 0.009375 + 0.007584 + 0.013084 + 0.007416 + 0.00675 + 0.007417 + 0.007 + 0.006792 + 0.006833 + 0.008416 + 0.008666 + 0.008083 + 0.007583 + 0.006583 + 0.006375 + 0.006167 + 0.005792 + 0.006083 + 0.006417 + 0.007459 + 0.008167 + 0.009458 + 0.008416 + 0.007208 + 0.006875 + 0.006708 + 0.007583 + 0.007958 + 0.008125 + 0.008209 + 0.00825 + 0.006875 + 0.007417 + 0.007709 + 0.007833 + 0.009125 + 0.007791 + 0.006875 + 0.007708 + 0.007292 + 0.005625 + 0.008542 + 0.007042 + 0.009208 + 0.008541 + 0.008958 + 0.007542 + 0.00825 + 0.006917 + 0.008417 + 0.008833 + 0.007958 + 0.007 + 0.006792 + 0.006375 + 0.007459 + 0.0085 + 0.007041 + 0.006292 + 0.007542 + 0.006792 + 0.006708 + 0.005916 + 0.006334 + 0.005834 + 0.006 + 0.006416 + 0.006834 + 0.007625 + 0.007542 + 0.00725 + 0.006958 + 0.006083 + 0.007125 + 0.005666 + 0.005667 + 0.006 + 0.005833 + 0.007083 + 0.006042 + 0.0055 + 0.006208 + 0.006833 + 0.006792 + 0.006666 + 0.006333 + 0.005708 + 0.005333 + 0.00575 + 0.006083 + 0.00575 + 0.00675 + 0.007208 + 0.006792 + 0.007167 + 0.006583 + 0.007333 + 0.007333 + 0.006708 + 0.007333 + 0.01 + 0.008792 + 0.007208 + 0.007208 + 0.00725 + 0.007875 + 0.00725 + 0.007375 + 0.007333 + 0.00925 + 0.0065 + 0.007709 + 0.007167 + 0.006583 + 0.006042 + 0.007042 + 0.006917 + 0.006084 + 0.005375 + 0.005458 + 0.005667 + 0.005709 + 0.005541 + 0.010166 + 0.007125 + 0.006708 + 0.006792 + 0.005917 + 0.005458 + 0.005208 + 0.006125 + 0.006375 + 0.006459 + 0.006542 + 0.006333 + 0.008333 + 0.007917 + 0.007625 + 0.007833 + 0.006917 + 0.005541 + 0.006875 + 0.006209 + 0.007459 + 0.007834 + 0.007417 + 0.007708 + 0.006792 + 0.005292 + 0.005333 + 0.005084 + 0.004792 + 0.005458 + 0.005583 + 0.005292 + 0.004916 + 0.005833 + 0.005834 + 0.005042 + 0.004875 + 0.005583 + 0.005 + 0.005333 + 0.004875 + 0.005584 + 0.005417 + 0.005333 + 0.005541 + 0.005459 + 0.005167 + 0.005708 + 0.005042 + 0.005875 + 0.005208 + 0.005625 + 0.0055 + 0.005417 + 0.004833 + 0.005417 + 0.005 + 0.005292 + 0.005042 + 0.005417 + 0.005041 + 0.005083 + 0.005084 + 0.0055 + 0.004958 + 0.005667 + 0.0055 + 0.00475 + 0.005459 + 0.005666 + 0.005167 + 0.004791 + 0.005708 + 0.0055 + 0.005875 + 0.006 + 0.005833 + 0.005584 + 0.007209 + 0.006042 + 0.006708 + 0.006125 + 0.006583 + 0.006375 + 0.006041 + 0.006125 + 0.006041 + 0.007125 + 0.007708 + 0.007542 + 0.006709 + 0.006 + 0.005875 + 0.005417 + 0.005375 + 0.005334 + 0.005708 + 0.005625 + 0.005209 + 0.005459 + 0.005375 + 0.005542 + 0.004792 + 0.006875 + 0.00675 + 0.0065 + 0.006208 + 0.0065 + 0.005875 + 0.005708 + 0.005833 + 0.006083 + 0.006542 + 0.008167 + 0.007541 + 0.00725 + 0.007167 + 0.00625 + 0.007166 + 0.007958 + 0.009875 + 0.008416 + 0.008042 + 0.007625 + 0.007625 + 0.006417 + 0.006916 + 0.006583 + 0.006625 + 0.00625 + 0.006917 + 0.006833 + 0.006166 + 0.006334 + 0.007041 + 0.006542 + 0.006875 + 0.006292 + 0.007041 + 0.007125 + 0.006542 + 0.006166 + 0.005583 + 0.005708 + 0.005167 + 0.006209 + 0.006125 + 0.006667 + 0.005875 + 0.00675 + 0.005875 + 0.005666 + 0.0055 + 0.005708 + 0.005583 + 0.005958 + 0.00525 + 0.005625 + 0.005625 + 0.005625 + 0.005291 + 0.005167 + 0.005667 + 0.006375 + 0.006084 + 0.0065 + 0.006792 + 0.016541 + 0.006792 + 0.006209 + 0.006417 + 0.006833 + 0.006041 + 0.006625 + 0.007541 + 0.008083 + 0.00675 + 0.007458 + 0.006875 + 0.006291 + 0.006417 + 0.006417 + 0.005375 + 0.006042 + 0.005833 + 0.006041 + 0.007958 + 0.007417 + 0.007208 + 0.006125 + 0.005333 + 0.005583 + 0.005333 + 0.005083 + 0.005125 + 0.005333 + 0.005083 + 0.004834 + 0.004875 + 0.006917 + 0.007917 + 0.006125 + 0.005459 + 0.005459 + 0.005209 + 0.005333 + 0.005042 + 0.005375 + 0.005875 + 0.005208 + 0.006208 + 0.00625 + 0.006291 + 0.008542 + 0.008417 + 0.007916 + 0.007833 + 0.00625 + 0.008125 + 0.006792 + 0.007916 + 0.007917 + 0.007875 + 0.008 + 0.007833 + 0.007541 + 0.005917 + 0.004875 + 0.005083 + 0.004833 + 0.004834 + 0.005375 + 0.00525 + 0.005417 + 0.005417 + 0.005417 + 0.004209 + 0.005208 + 0.005333 + 0.004958 + 0.005458 + 0.004666 + 0.004417 + 0.00475 + 0.005125 + 0.0045 + 0.004833 + 0.004959 + 0.005 + 0.004584 + 0.005125 + 0.005 + 0.005 + 0.005166 + 0.00575 + 0.005 + 0.005333 + 0.004792 + 0.00525 + 0.004792 + 0.005042 + 0.004958 + 0.004958 + 0.005 + 0.00475 + 0.004959 + 0.005125 + 0.004667 + 0.005167 + 0.004541 + 0.00475 + 0.005042 + 0.004709 + 0.005291 + 0.005417 + 0.008125 + 0.006917 + 0.0065 + 0.006792 + 0.00675 + 0.006542 + 0.006708 + 0.006041 + 0.006042 + 0.00625 + 0.006209 + 0.009084 + 0.007917 + 0.008042 + 0.008333 + 0.00825 + 0.007125 + 0.006375 + 0.005666 + 0.005708 + 0.005583 + 0.006417 + 0.006458 + 0.006292 + 0.006666 + 0.006833 + 0.006167 + 0.00575 + 0.00625 + 0.005958 + 0.005125 + 0.005334 + 0.005083 + 0.005625 + 0.005625 + 0.005917 + 0.005666 + 0.008208 + 0.007958 + 0.007542 + 0.00725 + 0.008 + 0.006792 + 0.005709 + 0.00625 + 0.007875 + 0.00725 + 0.007833 + 0.007708 + 0.007792 + 0.007459 + 0.006041 + 0.005083 + 0.01075 + 0.005584 + 0.005 + 0.005458 + 0.005417 + 0.005041 + 0.005333 + 0.005042 + 0.00525 + 0.00475 + 0.005208 + 0.005125 + 0.005166 + 0.004666 + 0.005 + 0.004708 + 0.004625 + 0.004417 + 0.004875 + 0.004958 + 0.00475 + 0.004917 + 0.005166 + 0.005292 + 0.00525 + 0.005375 + 0.005208 + 0.004792 + 0.00525 + 0.00475 + 0.005208 + 0.004625 + 0.004583 + 0.004625 + 0.004583 + 0.004625 + 0.004584 + 0.004834 + 0.004916 + 0.004917 + 0.004625 + 0.004542 + 0.005042 + 0.004875 + 0.004708 + 0.004583 + 0.005083 + 0.006542 + 0.006458 + 0.006166 + 0.006666 + 0.0065 + 0.006375 + 0.006375 + 0.006 + 0.005083 + 0.006167 + 0.006167 + 0.006 + 0.00775 + 0.008084 + 0.007709 + 0.007583 + 0.006708 + 0.00625 + 0.006666 + 0.005042 + 0.005334 + 0.00575 + 0.005208 + 0.005083 + 0.004833 + 0.005291 + 0.005334 + 0.00525 + 0.005042 + 0.006917 + 0.006458 + 0.007292 + 0.006959 + 0.006584 + 0.006917 + 0.008 + 0.006417 + 0.005959 + 0.005916 + 0.007958 + 0.007959 + 0.00775 + 0.0075 + 0.00675 + 0.007708 + 0.007 + 0.007708 + 0.00825 + 0.007209 + 0.00825 + 0.007542 + 0.006416 + 0.006208 + 0.004625 + 0.005416 + 0.005084 + 0.004958 + 0.004667 + 0.004458 + 0.005917 + 0.004791 + 0.004708 + 0.005583 + 0.005375 + 0.00525 + 0.004834 + 0.004459 + 0.004792 + 0.004541 + 0.005208 + 0.004834 + 0.004792 + 0.004875 + 0.004834 + 0.004375 + 0.005 + 0.00475 + 0.006209 + 0.006375 + 0.006708 + 0.006417 + 0.00625 + 0.006375 + 0.00675 + 0.006416 + 0.006625 + 0.005958 + 0.006083 + 0.005625 + 0.004959 + 0.004959 + 0.005125 + 0.005 + 0.00525 + 0.004792 + 0.004625 + 0.004791 + 0.004458 + 0.004792 + 0.005916 + 0.005125 + 0.004833 + 0.005166 + 0.00525 + 0.006625 + 0.006417 + 0.006625 + 0.006791 + 0.006583 + 0.008042 + 0.007666 + 0.006292 + 0.005875 + 0.007 + 0.0085 + 0.00775 + 0.006958 + 0.006458 + 0.007209 + 0.006375 + 0.005542 + 0.005125 + 0.005208 + 0.005083 + 0.005208 + 0.005792 + 0.005416 + 0.005167 + 0.007666 + 0.007292 + 0.006917 + 0.006458 + 0.006834 + 0.007208 + 0.007041 + 0.005958 + 0.007208 + 0.007417 + 0.006875 + 0.007167 + 0.008292 + 0.008416 + 0.008083 + 0.006667 + 0.008125 + 0.007917 + 0.007917 + 0.008333 + 0.008125 + 0.007958 + 0.007541 + 0.007917 + 0.006334 + 0.006292 + 0.00575 + 0.004959 + 0.005375 + 0.006209 + 0.005208 + 0.005625 + 0.005875 + 0.005875 + 0.006459 + 0.005666 + 0.005417 + 0.005291 + 0.005375 + 0.005041 + 0.005167 + 0.0045 + 0.005917 + 0.0055 + 0.005458 + 0.005208 + 0.00575 + 0.005042 + 0.005125 + 0.005292 + 0.005167 + 0.005708 + 0.005666 + 0.0055 + 0.005292 + 0.006416 + 0.006083 + 0.005417 + 0.006125 + 0.00675 + 0.007667 + 0.032541 + 0.015083 + 0.007166 + 0.00625 + 0.00525 + 0.005542 + 0.005708 + 0.0055 + 0.004792 + 0.005542 + 0.006042 + 0.007291 + 0.005583 + 0.005084 + 0.005875 + 0.00825 + 0.006833 + 0.006708 + 0.006417 + 0.005917 + 0.007166 + 0.006333 + 0.006625 + 0.006333 + 0.008125 + 0.008334 + 0.008167 + 0.008333 + 0.007125 + 0.007 + 0.005833 + 0.006042 + 0.005334 + 0.005584 + 0.005792 + 0.005333 + 0.007375 + 0.006958 + 0.008 + 0.007166 + 0.006958 + 0.006584 + 0.007 + 0.006125 + 0.006083 + 0.006875 + 0.007 + 0.008167 + 0.008166 + 0.007667 + 0.010292 + 0.0075 + 0.007292 + 0.007041 + 0.006584 + 0.006709 + 0.00625 + 0.008375 + 0.007416 + 0.007417 + 0.007417 + 0.006833 + 0.005875 + 0.005167 + 0.004875 + 0.005333 + 0.005292 + 0.004958 + 0.005375 + 0.005083 + 0.005917 + 0.005709 + 0.004833 + 0.004958 + 0.006083 + 0.005625 + 0.005041 + 0.004958 + 0.004917 + 0.005458 + 0.004792 + 0.004875 + 0.00475 + 0.004916 + 0.005125 + 0.004666 + 0.004833 + 0.01475 + 0.005041 + 0.004792 + 0.00575 + 0.005917 + 0.005416 + 0.005292 + 0.0055 + 0.005167 + 0.004958 + 0.005167 + 0.004833 + 0.005208 + 0.005208 + 0.005625 + 0.005125 + 0.006 + 0.0065 + 0.006417 + 0.006417 + 0.0065 + 0.006625 + 0.0065 + 0.005417 + 0.005708 + 0.010125 + 0.006916 + 0.006875 + 0.006875 + 0.00675 + 0.006792 + 0.006292 + 0.006041 + 0.006542 + 0.006708 + 0.007958 + 0.009291 + 0.008708 + 0.008625 + 0.008291 + 0.007792 + 0.00725 + 0.006625 + 0.005417 + 0.005416 + 0.006292 + 0.007875 + 0.00775 + 0.006583 + 0.005958 + 0.005792 + 0.005834 + 0.005584 + 0.005375 + 0.005416 + 0.005125 + 0.005542 + 0.004875 + 0.005417 + 0.00525 + 0.006167 + 0.006 + 0.008792 + 0.008416 + 0.00775 + 0.007791 + 0.008 + 0.007416 + 0.006458 + 0.009292 + 0.007916 + 0.007083 + 0.006625 + 0.006791 + 0.006834 + 0.007625 + 0.006625 + 0.006416 + 0.006292 + 0.005334 + 0.00575 + 0.006209 + 0.00625 + 0.006 + 0.005833 + 0.006083 + 0.00575 + 0.0065 + 0.006292 + 0.013333 + 0.005375 + 0.004666 + 0.004792 + 0.004708 + 0.005 + 0.004834 + 0.005125 + 0.005375 + 0.004833 + 0.004458 + 0.004583 + 0.005 + 0.005417 + 0.006333 + 0.005708 + 0.00575 + 0.006125 + 0.006417 + 0.006333 + 0.00625 + 0.005834 + 0.0065 + 0.006416 + 0.005583 + 0.005916 + 0.006625 + 0.00675 + 0.006125 + 0.005958 + 0.00575 + 0.00525 + 0.005583 + 0.006209 + 0.006375 + 0.008417 + 0.007542 + 0.007333 + 0.007541 + 0.007 + 0.006084 + 0.005667 + 0.005291 + 0.005917 + 0.007041 + 0.008083 + 0.008 + 0.008541 + 0.007292 + 0.007917 + 0.006833 + 0.00675 + 0.005583 + 0.006167 + 0.005791 + 0.005792 + 0.005625 + 0.005292 + 0.010166 + 0.006666 + 0.006458 + 0.006708 + 0.006375 + 0.005708 + 0.005625 + 0.006042 + 0.005625 + 0.005416 + 0.005167 + 0.005542 + 0.005416 + 0.006 + 0.006083 + 0.007042 + 0.007125 + 0.015625 + 0.006542 + 0.006166 + 0.006083 + 0.005292 + 0.008541 + 0.007708 + 0.008041 + 0.007417 + 0.007375 + 0.008167 + 0.009167 + 0.006333 + 0.004958 + 0.004708 + 0.005125 + 0.004875 + 0.004791 + 0.004584 + 0.006042 + 0.005542 + 0.005584 + 0.004958 + 0.005458 + 0.005333 + 0.004916 + 0.004708 + 0.005208 + 0.005208 + 0.004542 + 0.00525 + 0.013125 + 0.005333 + 0.004917 + 0.004917 + 0.004958 + 0.004917 + 0.004875 + 0.004875 + 0.004875 + 0.004792 + 0.004667 + 0.005083 + 0.004625 + 0.005041 + 0.004917 + 0.005041 + 0.004917 + 0.004458 + 0.004917 + 0.005333 + 0.00525 + 0.004917 + 0.005041 + 0.005125 + 0.00475 + 0.00475 + 0.005459 + 0.004917 + 0.005166 + 0.005 + 0.0055 + 0.005083 + 0.005292 + 0.00475 + 0.005125 + 0.004875 + 0.004833 + 0.005208 + 0.005917 + 0.006041 + 0.005625 + 0.007542 + 0.007375 + 0.00675 + 0.007166 + 0.00675 + 0.005375 + 0.005292 + 0.005125 + 0.005125 + 0.006042 + 0.006458 + 0.005875 + 0.00725 + 0.007083 + 0.006666 + 0.006125 + 0.00675 + 0.006875 + 0.006834 + 0.006709 + 0.006292 + 0.005292 + 0.005167 + 0.005292 + 0.005083 + 0.006 + 0.005709 + 0.007375 + 0.007292 + 0.006458 + 0.005583 + 0.005834 + 0.005125 + 0.006583 + 0.007959 + 0.007667 + 0.007375 + 0.007583 + 0.007375 + 0.007875 + 0.008917 + 0.006459 + 0.005959 + 0.006083 + 0.006209 + 0.006 + 0.005792 + 0.005291 + 0.005667 + 0.005417 + 0.005667 + 0.08675 + 0.007458 + 0.00675 + 0.015834 + 0.008375 + 0.007458 + 0.006833 + 0.007125 + 0.006542 + 0.006958 + 0.005291 + 0.005833 + 0.004875 + 0.005542 + 0.005458 + 0.00525 + 0.005375 + 0.005291 + 0.005 + 0.005083 + 0.004791 + 0.004875 + 0.005125 + 0.004542 + 0.004959 + 0.005333 + 0.004875 + 0.004958 + 0.004916 + 0.004792 + 0.004709 + 0.004959 + 0.005459 + 0.004959 + 0.005166 + 0.004583 + 0.005333 + 0.004792 + 0.006209 + 0.005375 + 0.00625 + 0.006792 + 0.007375 + 0.006917 + 0.006291 + 0.005834 + 0.005584 + 0.006833 + 0.00675 + 0.008542 + 0.008125 + 0.005959 + 0.006875 + 0.006333 + 0.006334 + 0.005625 + 0.005875 + 0.007667 + 0.007292 + 0.007041 + 0.00675 + 0.005792 + 0.005417 + 0.006 + 0.006292 + 0.005833 + 0.005042 + 0.005917 + 0.005375 + 0.005625 + 0.005875 + 0.0075 + 0.007833 + 0.006833 + 0.00725 + 0.009541 + 0.008291 + 0.006875 + 0.00675 + 0.0075 + 0.007167 + 0.00625 + 0.009208 + 0.009042 + 0.007334 + 0.00725 + 0.007459 + 0.0075 + 0.009125 + 0.008709 + 0.006834 + 0.00625 + 0.006125 + 0.006458 + 0.006083 + 0.006083 + 0.006458 + 0.007417 + 0.007042 + 0.006625 + 0.005583 + 0.006083 + 0.005875 + 0.006583 + 0.006541 + 0.006584 + 0.0065 + 0.00725 + 0.0075 + 0.006334 + 0.006 + 0.006292 + 0.006125 + 0.006667 + 0.006666 + 0.006833 + 0.006 + 0.006375 + 0.006 + 0.00725 + 0.029958 + 0.006417 + 0.005916 + 0.005667 + 0.006209 + 0.012666 + 0.007166 + 0.017959 + 0.00975 + 0.006792 + 0.005834 + 0.028667 + 0.007666 + 0.056416 + 0.010875 + 0.006958 + 0.006375 + 0.0055 + 0.006917 + 0.006 + 0.005792 + 0.005958 + 0.005583 + 0.005958 + 0.005875 + 0.00525 + 0.005125 + 0.010375 + 0.015625 + 0.008833 + 0.008167 + 0.007084 + 0.007334 + 0.008292 + 0.010042 + 0.007584 + 0.007209 + 0.006167 + 0.005125 + 0.005 + 0.005083 + 0.005291 + 0.005334 + 0.005584 + 0.005459 + 0.005584 + 0.009459 + 0.006334 + 0.005334 + 0.005792 + 0.005292 + 0.005708 + 0.006458 + 0.006875 + 0.011083 + 0.009125 + 0.00925 + 0.008834 + 0.007209 + 0.0065 + 0.00725 + 0.00775 + 0.0065 + 0.007042 + 0.006792 + 0.007292 + 0.007459 + 0.006834 + 0.009458 + 0.009125 + 0.00675 + 0.00625 + 0.0065 + 0.00625 + 0.006416 + 0.00625 + 0.006125 + 0.007084 + 0.006042 + 0.006458 + 0.006208 + 0.006875 + 0.006625 + 0.006625 + 0.006458 + 0.006291 + 0.006917 + 0.007125 + 0.00675 + 0.005917 + 0.006083 + 0.005792 + 0.006167 + 0.006083 + 0.005583 + 0.005958 + 0.006584 + 0.006666 + 0.006167 + 0.00575 + 0.006083 + 0.005917 + 0.005541 + 0.005334 + 0.005959 + 0.006083 + 0.005958 + 0.006083 + 0.006042 + 0.006542 + 0.005833 + 0.006125 + 0.0065 + 0.006125 + 0.005625 + 0.006875 + 0.007083 + 0.006542 + 0.006625 + 0.008459 + 0.009334 + 0.00775 + 0.006625 + 0.005916 + 0.006084 + 0.00625 + 0.006917 + 0.007167 + 0.007375 + 0.007875 + 0.009292 + 0.009417 + 0.008583 + 0.006916 + 0.006875 + 0.00625 + 0.006458 + 0.009125 + 0.010833 + 0.009542 + 0.009584 + 0.011 + 0.009333 + 0.008208 + 0.008041 + 0.008125 + 0.0075 + 0.00625 + 0.006417 + 0.006417 + 0.007375 + 0.007542 + 0.007208 + 0.007291 + 0.00825 + 0.008708 + 0.011167 + 0.010042 + 0.008959 + 0.00825 + 0.008417 + 0.006708 + 0.014833 + 0.00925 + 0.008459 + 0.008708 + 0.00775 + 0.007375 + 0.007042 + 0.008291 + 0.006208 + 0.0055 + 0.005667 + 0.006208 + 0.006042 + 0.006084 + 0.005584 + 0.00625 + 0.006416 + 0.005334 + 0.005584 + 0.005708 + 0.005458 + 0.005958 + 0.006042 + 0.005833 + 0.005375 + 0.004791 + 0.005167 + 0.005416 + 0.005625 + 0.005792 + 0.005667 + 0.005208 + 0.006083 + 0.005 + 0.00525 + 0.005333 + 0.005208 + 0.005541 + 0.005917 + 0.005416 + 0.006 + 0.005083 + 0.005208 + 0.005041 + 0.0055 + 0.005375 + 0.005583 + 0.00525 + 0.005084 + 0.005 + 0.005625 + 0.005333 + 0.007958 + 0.00525 + 0.005792 + 0.005375 + 0.005667 + 0.005583 + 0.005708 + 0.005416 + 0.005333 + 0.005875 + 0.005791 + 0.005875 + 0.005458 + 0.005583 + 0.006916 + 0.006292 + 0.011208 + 0.008666 + 0.008125 + 0.0085 + 0.007875 + 0.007417 + 0.00625 + 0.005375 + 0.006083 + 0.006167 + 0.006083 + 0.007708 + 0.007042 + 0.007333 + 0.007166 + 0.006083 + 0.007208 + 0.007375 + 0.006541 + 0.006375 + 0.005416 + 0.005333 + 0.0055 + 0.00575 + 0.006291 + 0.008292 + 0.006959 + 0.009459 + 0.023375 + 0.012583 + 0.007416 + 0.007167 + 0.006 + 0.006334 + 0.008542 + 0.007709 + 0.0075 + 0.007375 + 0.00725 + 0.006833 + 0.007542 + 0.007041 + 0.006042 + 0.005833 + 0.006875 + 0.006916 + 0.007375 + 0.006875 + 0.007792 + 0.006417 + 0.006625 + 0.006291 + 0.00675 + 0.00575 + 0.005083 + 0.005166 + 0.004917 + 0.005166 + 0.005167 + 0.005375 + 0.005667 + 0.005708 + 0.006292 + 0.00625 + 0.005167 + 0.005166 + 0.005708 + 0.005542 + 0.005292 + 0.005125 + 0.005084 + 0.005208 + 0.00475 + 0.004792 + 0.005125 + 0.005334 + 0.004542 + 0.004875 + 0.005125 + 0.005 + 0.005042 + 0.005208 + 0.005167 + 0.005167 + 0.005041 + 0.005209 + 0.005166 + 0.00475 + 0.005458 + 0.00525 + 0.006 + 0.005917 + 0.005375 + 0.005667 + 0.005708 + 0.005459 + 0.00525 + 0.005542 + 0.006208 + 0.006833 + 0.011084 + 0.007625 + 0.007125 + 0.008083 + 0.006583 + 0.006833 + 0.005625 + 0.005458 + 0.006834 + 0.0065 + 0.006333 + 0.005708 + 0.005334 + 0.005667 + 0.005708 + 0.005667 + 0.0055 + 0.005334 + 0.008292 + 0.005292 + 0.00525 + 0.005792 + 0.006042 + 0.005584 + 0.00525 + 0.006084 + 0.008542 + 0.00775 + 0.007584 + 0.007542 + 0.019375 + 0.006583 + 0.0065 + 0.00825 + 0.008583 + 0.007834 + 0.008042 + 0.008333 + 0.007958 + 0.008625 + 0.008334 + 0.007625 + 0.005708 + 0.005291 + 0.005917 + 0.018666 + 0.008959 + 0.005916 + 0.005542 + 0.006458 + 0.005625 + 0.006459 + 0.00675 + 0.006875 + 0.017917 + 0.011375 + 0.006875 + 0.005584 + 0.005625 + 0.005625 + 0.005125 + 0.005125 + 0.005 + 0.004625 + 0.0055 + 0.005875 + 0.007333 + 0.006834 + 0.006791 + 0.0055 + 0.005875 + 0.005625 + 0.005375 + 0.005291 + 0.023584 + 0.006708 + 0.006083 + 0.005625 + 0.006791 + 0.006792 + 0.006 + 0.005625 + 0.006375 + 0.005333 + 0.005166 + 0.005083 + 0.005542 + 0.00525 + 0.005041 + 0.006084 + 0.005584 + 0.005084 + 0.005125 + 0.005333 + 0.0055 + 0.00575 + 0.006542 + 0.006708 + 0.00875 + 0.010167 + 0.008667 + 0.008083 + 0.007375 + 0.007667 + 0.006833 + 0.007833 + 0.00775 + 0.006417 + 0.007208 + 0.007167 + 0.007 + 0.0065 + 0.005167 + 0.005208 + 0.005666 + 0.005625 + 0.005208 + 0.00575 + 0.005709 + 0.005292 + 0.005041 + 0.005792 + 0.005458 + 0.005292 + 0.005333 + 0.007958 + 0.026125 + 0.008417 + 0.01275 + 0.008708 + 0.008334 + 0.007833 + 0.006833 + 0.008625 + 0.008667 + 0.008334 + 0.008042 + 0.0085 + 0.007542 + 0.006958 + 0.006958 + 0.006958 + 0.005375 + 0.005625 + 0.006 + 0.005417 + 0.005958 + 0.00575 + 0.006 + 0.005833 + 0.00575 + 0.006708 + 0.0065 + 0.009875 + 0.006167 + 0.006791 + 0.006667 + 0.005833 + 0.006333 + 0.006208 + 0.006042 + 0.006166 + 0.006625 + 0.006208 + 0.006 + 0.006 + 0.006416 + 0.005708 + 0.005917 + 0.006625 + 0.006041 + 0.006125 + 0.005833 + 0.0055 + 0.005667 + 0.00575 + 0.0115 + 0.006167 + 0.005208 + 0.011417 + 0.005541 + 0.006458 + 0.006417 + 0.006833 + 0.007333 + 0.006833 + 0.007 + 0.006875 + 0.006917 + 0.008875 + 0.007875 + 0.007333 + 0.007084 + 0.006875 + 0.006542 + 0.006209 + 0.005875 + 0.005834 + 0.007 + 0.008833 + 0.006875 + 0.006833 + 0.008625 + 0.015375 + 0.007959 + 0.007791 + 0.007375 + 0.005875 + 0.008041 + 0.00625 + 0.006083 + 0.006542 + 0.0055 + 0.005917 + 0.006125 + 0.007125 + 0.011625 + 0.007416 + 0.012792 + 0.006833 + 0.007208 + 0.006667 + 0.006416 + 0.005959 + 0.007584 + 0.006 + 0.006375 + 0.00675 + 0.008125 + 0.008208 + 0.007542 + 0.007791 + 0.007958 + 0.006667 + 0.006959 + 0.007291 + 0.008 + 0.007166 + 0.00775 + 0.006625 + 0.007083 + 0.005666 + 0.006125 + 0.006125 + 0.00725 + 0.00775 + 0.006584 + 0.006834 + 0.007209 + 0.007792 + 0.005667 + 0.005667 + 0.007459 + 0.007334 + 0.007292 + 0.006916 + 0.006833 + 0.006792 + 0.006167 + 0.006625 + 0.006708 + 0.007125 + 0.006125 + 0.005459 + 0.006375 + 0.005208 + 0.00475 + 0.005584 + 0.005125 + 0.00575 + 0.005916 + 0.005667 + 0.005667 + 0.005083 + 0.00525 + 0.005166 + 0.004958 + 0.00525 + 0.005666 + 0.005542 + 0.005208 + 0.005291 + 0.00525 + 0.005417 + 0.005375 + 0.005542 + 0.005459 + 0.005583 + 0.004958 + 0.005458 + 0.005417 + 0.005375 + 0.005959 + 0.005708 + 0.008083 + 0.006667 + 0.006167 + 0.006458 + 0.006292 + 0.006625 + 0.006459 + 0.006625 + 0.006875 + 0.00775 + 0.007583 + 0.007625 + 0.006625 + 0.006625 + 0.005209 + 0.005792 + 0.005833 + 0.005833 + 0.005416 + 0.00575 + 0.0055 + 0.006167 + 0.005 + 0.005875 + 0.00625 + 0.010291 + 0.005958 + 0.006458 + 0.006583 + 0.007083 + 0.005667 + 0.005125 + 0.006958 + 0.006 + 0.005625 + 0.006583 + 0.007583 + 0.007375 + 0.007833 + 0.00675 + 0.007042 + 0.006208 + 0.006708 + 0.007417 + 0.0075 + 0.007 + 0.008 + 0.006791 + 0.005167 + 0.005125 + 0.005167 + 0.005208 + 0.005 + 0.005458 + 0.005167 + 0.0055 + 0.005875 + 0.005084 + 0.004875 + 0.006 + 0.0055 + 0.005208 + 0.004917 + 0.005 + 0.005542 + 0.005583 + 0.005583 + 0.005042 + 0.005 + 0.005125 + 0.005208 + 0.005375 + 0.00575 + 0.00575 + 0.005708 + 0.00525 + 0.005209 + 0.005334 + 0.00525 + 0.005042 + 0.0055 + 0.005375 + 0.005291 + 0.004958 + 0.014542 + 0.005208 + 0.004958 + 0.004791 + 0.005375 + 0.005416 + 0.004667 + 0.005167 + 0.00525 + 0.005334 + 0.0055 + 0.006167 + 0.0065 + 0.006875 + 0.006875 + 0.007459 + 0.008292 + 0.006875 + 0.00575 + 0.006084 + 0.006584 + 0.007916 + 0.007833 + 0.0085 + 0.007792 + 0.009166 + 0.008167 + 0.008166 + 0.00825 + 0.006875 + 0.006708 + 0.006791 + 0.006791 + 0.006625 + 0.006917 + 0.006333 + 0.007083 + 0.009792 + 0.010959 + 0.006792 + 0.006792 + 0.00775 + 0.008459 + 0.007541 + 0.006834 + 0.006584 + 0.006208 + 0.005458 + 0.007291 + 0.006583 + 0.007625 + 0.010791 + 0.008625 + 0.007625 + 0.006917 + 0.005916 + 0.00575 + 0.007083 + 0.009042 + 0.009042 + 0.008625 + 0.007041 + 0.00725 + 0.007375 + 0.006208 + 0.006625 + 0.005459 + 0.005625 + 0.005375 + 0.005666 + 0.005875 + 0.005416 + 0.006084 + 0.005292 + 0.00525 + 0.005333 + 0.00525 + 0.005292 + 0.005167 + 0.004875 + 0.004917 + 0.005292 + 0.005083 + 0.004708 + 0.005416 + 0.004917 + 0.005125 + 0.004625 + 0.005209 + 0.005417 + 0.005667 + 0.005083 + 0.005167 + 0.005042 + 0.005459 + 0.005125 + 0.005208 + 0.005208 + 0.005292 + 0.0045 + 0.004917 + 0.005416 + 0.006417 + 0.005875 + 0.006625 + 0.006334 + 0.005917 + 0.005541 + 0.005667 + 0.005875 + 0.00525 + 0.005208 + 0.006583 + 0.010959 + 0.009083 + 0.008208 + 0.007625 + 0.0075 + 0.00625 + 0.006916 + 0.006792 + 0.006666 + 0.007167 + 0.0075 + 0.009875 + 0.009458 + 0.007042 + 0.007417 + 0.007375 + 0.007208 + 0.006458 + 0.0075 + 0.006958 + 0.007167 + 0.007416 + 0.00925 + 0.007833 + 0.008125 + 0.008292 + 0.007 + 0.007083 + 0.007 + 0.008 + 0.008208 + 0.00725 + 0.006125 + 0.007041 + 0.007042 + 0.00825 + 0.007208 + 0.00825 + 0.009166 + 0.007292 + 0.007208 + 0.007708 + 0.007666 + 0.006292 + 0.08975 + 0.078167 + 0.013 + 0.007542 + 0.007 + 0.006459 + 0.009 + 0.006458 + 0.005458 + 0.005666 + 0.005417 + 0.00575 + 0.004958 + 0.005083 + 0.011459 + 0.005167 + 0.005583 + 0.004917 + 0.004917 + 0.005958 + 0.005 + 0.005042 + 0.00475 + 0.005166 + 0.004541 + 0.0045 + 0.004708 + 0.005166 + 0.005334 + 0.004917 + 0.005 + 0.005083 + 0.005125 + 0.0055 + 0.005333 + 0.005166 + 0.005083 + 0.005459 + 0.00475 + 0.005042 + 0.005125 + 0.004834 + 0.00525 + 0.00475 + 0.004958 + 0.004917 + 0.005291 + 0.004584 + 0.00475 + 0.0055 + 0.004959 + 0.004958 + 0.004708 + 0.005 + 0.004625 + 0.00525 + 0.00525 + 0.004959 + 0.005125 + 0.005125 + 0.005125 + 0.005125 + 0.005041 + 0.005083 + 0.006083 + 0.0105 + 0.006375 + 0.008625 + 0.00875 + 0.007625 + 0.008083 + 0.007958 + 0.007625 + 0.007209 + 0.007584 + 0.006875 + 0.006792 + 0.007 + 0.008292 + 0.006958 + 0.00675 + 0.0065 + 0.005458 + 0.005375 + 0.005 + 0.005291 + 0.005834 + 0.005625 + 0.006292 + 0.006875 + 0.008084 + 0.008209 + 0.007125 + 0.008333 + 0.007708 + 0.00725 + 0.007125 + 0.006583 + 0.007042 + 0.006125 + 0.008167 + 0.008083 + 0.008292 + 0.007625 + 0.006667 + 0.008834 + 0.010209 + 0.006625 + 0.006583 + 0.006833 + 0.006917 + 0.006666 + 0.006375 + 0.00675 + 0.00725 + 0.006708 + 0.004917 + 0.00475 + 0.005417 + 0.005625 + 0.004958 + 0.005292 + 0.005042 + 0.004708 + 0.011334 + 0.004958 + 0.004875 + 0.004541 + 0.005083 + 0.005083 + 0.005125 + 0.005083 + 0.005208 + 0.004875 + 0.005 + 0.005125 + 0.005541 + 0.005833 + 0.006458 + 0.007208 + 0.007292 + 0.006959 + 0.006041 + 0.005708 + 0.00575 + 0.006041 + 0.005625 + 0.00625 + 0.00675 + 0.006834 + 0.006292 + 0.006458 + 0.005166 + 0.005417 + 0.005375 + 0.007125 + 0.008042 + 0.006292 + 0.007 + 0.0095 + 0.022167 + 0.012542 + 0.007958 + 0.006625 + 0.006416 + 0.006792 + 0.010958 + 0.012083 + 0.009959 + 0.010209 + 0.009542 + 0.010375 + 0.00775 + 0.007416 + 0.007083 + 0.00925 + 0.007792 + 0.007417 + 0.007542 + 0.0065 + 0.007125 + 0.006542 + 0.01275 + 0.00725 + 0.007375 + 0.006625 + 0.007875 + 0.005709 + 0.005709 + 0.00575 + 0.005542 + 0.005125 + 0.006625 + 0.008333 + 0.007958 + 0.007291 + 0.007709 + 0.007916 + 0.008167 + 0.006375 + 0.008625 + 0.007792 + 0.007666 + 0.007458 + 0.007292 + 0.0065 + 0.008375 + 0.007834 + 0.006709 + 0.006 + 0.006208 + 0.005417 + 0.0055 + 0.005417 + 0.005417 + 0.006042 + 0.005417 + 0.005833 + 0.0055 + 0.005792 + 0.005625 + 0.004791 + 0.005583 + 0.005458 + 0.005875 + 0.005792 + 0.005667 + 0.005083 + 0.005 + 0.005083 + 0.005166 + 0.005375 + 0.005666 + 0.005917 + 0.005 + 0.00525 + 0.00475 + 0.005583 + 0.005542 + 0.005708 + 0.005125 + 0.005625 + 0.005209 + 0.005 + 0.007333 + 0.005458 + 0.005417 + 0.006417 + 0.006541 + 0.006292 + 0.005375 + 0.005083 + 0.00525 + 0.005125 + 0.005042 + 0.00525 + 0.006042 + 0.005542 + 0.005625 + 0.005208 + 0.0055 + 0.005792 + 0.005583 + 0.005667 + 0.006208 + 0.006292 + 0.011084 + 0.007542 + 0.0075 + 0.006917 + 0.007166 + 0.006833 + 0.008375 + 0.012666 + 0.006458 + 0.006041 + 0.005458 + 0.00525 + 0.00575 + 0.005958 + 0.005708 + 0.005042 + 0.00575 + 0.005125 + 0.005709 + 0.006209 + 0.007 + 0.006625 + 0.006625 + 0.007333 + 0.007125 + 0.006958 + 0.008875 + 0.008792 + 0.007458 + 0.007458 + 0.007708 + 0.00775 + 0.008708 + 0.006209 + 0.0055 + 0.007125 + 0.007375 + 0.006959 + 0.007834 + 0.008042 + 0.009583 + 0.0075 + 0.005875 + 0.005875 + 0.00575 + 0.0055 + 0.005791 + 0.005583 + 0.00575 + 0.006792 + 0.006833 + 0.006375 + 0.006625 + 0.007125 + 0.006875 + 0.006917 + 0.007166 + 0.005834 + 0.005416 + 0.005375 + 0.00525 + 0.005166 + 0.0055 + 0.0055 + 0.005125 + 0.005292 + 0.005375 + 0.005167 + 0.005208 + 0.005375 + 0.005208 + 0.004917 + 0.004958 + 0.004833 + 0.005292 + 0.004958 + 0.004708 + 0.0055 + 0.005042 + 0.005333 + 0.01075 + 0.006667 + 0.005291 + 0.005583 + 0.006 + 0.006333 + 0.006416 + 0.005917 + 0.005291 + 0.004875 + 0.005417 + 0.005542 + 0.005458 + 0.006166 + 0.005542 + 0.004833 + 0.005292 + 0.005292 + 0.005583 + 0.004792 + 0.00625 + 0.011208 + 0.00725 + 0.007292 + 0.007708 + 0.007292 + 0.0065 + 0.005083 + 0.004958 + 0.006292 + 0.006584 + 0.006417 + 0.006542 + 0.006334 + 0.006375 + 0.005791 + 0.005375 + 0.00525 + 0.004958 + 0.005042 + 0.005417 + 0.011666 + 0.005042 + 0.006125 + 0.00625 + 0.006458 + 0.006375 + 0.008334 + 0.008125 + 0.007792 + 0.007541 + 0.007792 + 0.007917 + 0.010041 + 0.007542 + 0.008167 + 0.008083 + 0.007541 + 0.008 + 0.006792 + 0.008458 + 0.007791 + 0.005917 + 0.005875 + 0.010459 + 0.004875 + 0.005084 + 0.00475 + 0.004583 + 0.00475 + 0.0055 + 0.004459 + 0.00475 + 0.005042 + 0.005292 + 0.005209 + 0.005042 + 0.007 + 0.005083 + 0.005417 + 0.005959 + 0.006 + 0.00475 + 0.004834 + 0.005167 + 0.008916 + 0.004958 + 0.004792 + 0.005041 + 0.005334 + 0.005291 + 0.004709 + 0.005125 + 0.005083 + 0.005166 + 0.004709 + 0.004917 + 0.00475 + 0.004875 + 0.00475 + 0.004833 + 0.005125 + 0.005417 + 0.005458 + 0.005333 + 0.004917 + 0.004958 + 0.005792 + 0.006125 + 0.006042 + 0.007167 + 0.007959 + 0.006834 + 0.007041 + 0.006667 + 0.006375 + 0.006208 + 0.005666 + 0.005 + 0.005417 + 0.006292 + 0.010875 + 0.007959 + 0.007584 + 0.007417 + 0.0085 + 0.007708 + 0.006042 + 0.00525 + 0.0055 + 0.007167 + 0.006625 + 0.007333 + 0.00725 + 0.006834 + 0.006459 + 0.006584 + 0.005917 + 0.005166 + 0.01125 + 0.005458 + 0.005 + 0.005083 + 0.004916 + 0.005083 + 0.005459 + 0.004292 + 0.006 + 0.007958 + 0.007667 + 0.008125 + 0.007584 + 0.007542 + 0.007667 + 0.00575 + 0.007209 + 0.008083 + 0.00775 + 0.0075 + 0.007625 + 0.00825 + 0.008292 + 0.007334 + 0.005834 + 0.006 + 0.006583 + 0.006208 + 0.005958 + 0.005541 + 0.004792 + 0.005958 + 0.005125 + 0.00475 + 0.005292 + 0.005083 + 0.005167 + 0.004875 + 0.005083 + 0.004916 + 0.004625 + 0.004791 + 0.005 + 0.004583 + 0.00525 + 0.005292 + 0.005667 + 0.005583 + 0.005041 + 0.004917 + 0.004917 + 0.004708 + 0.005125 + 0.004708 + 0.005 + 0.004959 + 0.004709 + 0.004875 + 0.004708 + 0.004667 + 0.004917 + 0.004958 + 0.004792 + 0.004792 + 0.004791 + 0.004917 + 0.005125 + 0.004875 + 0.005041 + 0.00475 + 0.004916 + 0.005209 + 0.005334 + 0.005375 + 0.005167 + 0.005041 + 0.0055 + 0.004833 + 0.004875 + 0.005084 + 0.004667 + 0.005916 + 0.00825 + 0.008042 + 0.008 + 0.00775 + 0.008208 + 0.0105 + 0.00925 + 0.007167 + 0.006667 + 0.007167 + 0.006791 + 0.007041 + 0.006583 + 0.0055 + 0.005542 + 0.005292 + 0.005167 + 0.005208 + 0.00525 + 0.005292 + 0.005583 + 0.0055 + 0.005 + 0.004834 + 0.005166 + 0.005542 + 0.011292 + 0.007708 + 0.00775 + 0.013375 + 0.007459 + 0.00725 + 0.008 + 0.006958 + 0.007625 + 0.007583 + 0.007625 + 0.007709 + 0.00675 + 0.008208 + 0.008083 + 0.007792 + 0.006291 + 0.005333 + 0.005333 + 0.005625 + 0.005541 + 0.006458 + 0.006125 + 0.006625 + 0.006417 + 0.004958 + 0.005166 + 0.005708 + 0.005917 + 0.004917 + 0.005458 + 0.005208 + 0.005 + 0.004792 + 0.004916 + 0.005084 + 0.004667 + 0.005083 + 0.005 + 0.004625 + 0.004625 + 0.005 + 0.005 + 0.00475 + 0.00475 + 0.004792 + 0.004958 + 0.005125 + 0.005041 + 0.005042 + 0.004458 + 0.004833 + 0.004875 + 0.004792 + 0.004916 + 0.004958 + 0.004875 + 0.004875 + 0.005333 + 0.005042 + 0.004792 + 0.004875 + 0.005083 + 0.005042 + 0.00525 + 0.005542 + 0.005542 + 0.005166 + 0.005334 + 0.004959 + 0.005042 + 0.005 + 0.005 + 0.011417 + 0.008125 + 0.0075 + 0.007583 + 0.007292 + 0.008292 + 0.006625 + 0.007666 + 0.0075 + 0.0065 + 0.006667 + 0.006625 + 0.006625 + 0.006625 + 0.005625 + 0.005417 + 0.005041 + 0.005333 + 0.00525 + 0.005125 + 0.00575 + 0.005291 + 0.004959 + 0.005417 + 0.004875 + 0.00525 + 0.00525 + 0.0075 + 0.008 + 0.007959 + 0.007334 + 0.007625 + 0.007542 + 0.008875 + 0.008167 + 0.006791 + 0.007917 + 0.008084 + 0.007209 + 0.007 + 0.007542 + 0.007916 + 0.007333 + 0.006667 + 0.006542 + 0.006583 + 0.006292 + 0.004916 + 0.004916 + 0.004625 + 0.004417 + 0.005167 + 0.005083 + 0.0045 + 0.005166 + 0.0055 + 0.004917 + 0.005166 + 0.004875 + 0.004791 + 0.00525 + 0.004834 + 0.006584 + 0.005209 + 0.005292 + 0.005416 + 0.005 + 0.005125 + 0.005083 + 0.004958 + 0.004833 + 0.005333 + 0.005584 + 0.0065 + 0.00475 + 0.005209 + 0.005333 + 0.005 + 0.005333 + 0.004708 + 0.004875 + 0.005625 + 0.005084 + 0.005041 + 0.005084 + 0.004917 + 0.005166 + 0.0125 + 0.00975 + 0.009166 + 0.005084 + 0.00525 + 0.005125 + 0.005084 + 0.005667 + 0.005083 + 0.005125 + 0.005542 + 0.006458 + 0.00525 + 0.005166 + 0.008334 + 0.008417 + 0.01825 + 0.00825 + 0.007792 + 0.006167 + 0.008042 + 0.007208 + 0.007292 + 0.007791 + 0.007958 + 0.007542 + 0.006625 + 0.005792 + 0.0075 + 0.00725 + 0.0335 + 0.007083 + 0.006166 + 0.008583 + 0.006541 + 0.006167 + 0.006083 + 0.006125 + 0.006375 + 0.006542 + 0.014541 + 0.007292 + 0.007416 + 0.007833 + 0.0075 + 0.006458 + 0.008 + 0.008458 + 0.006541 + 0.008375 + 0.006 + 0.021708 + 0.0075 + 0.008917 + 0.00825 + 0.007875 + 0.007667 + 0.006958 + 0.007042 + 0.006791 + 0.006667 + 0.005334 + 0.005292 + 0.006833 + 0.006041 + 0.005625 + 0.005375 + 0.004584 + 0.005417 + 0.005083 + 0.004958 + 0.005166 + 0.005 + 0.005 + 0.005375 + 0.005166 + 0.005542 + 0.004708 + 0.005041 + 0.005 + 0.004917 + 0.005083 + 0.00525 + 0.005292 + 0.005292 + 0.00475 + 0.005083 + 0.005 + 0.005042 + 0.005125 + 0.00525 + 0.005208 + 0.005041 + 0.004708 + 0.005334 + 0.005209 + 0.005 + 0.005166 + 0.005334 + 0.005291 + 0.005375 + 0.005125 + 0.004958 + 0.00525 + 0.004958 + 0.00525 + 0.005625 + 0.005125 + 0.005 + 0.004833 + 0.005084 + 0.005625 + 0.005791 + 0.005959 + 0.007625 + 0.0075 + 0.007166 + 0.007333 + 0.006667 + 0.006292 + 0.007667 + 0.006542 + 0.006042 + 0.005583 + 0.00525 + 0.005958 + 0.006416 + 0.006 + 0.005083 + 0.005375 + 0.005583 + 0.004916 + 0.005334 + 0.00525 + 0.005958 + 0.006 + 0.005333 + 0.006209 + 0.007209 + 0.007042 + 0.006375 + 0.009041 + 0.007792 + 0.007125 + 0.006292 + 0.005584 + 0.006 + 0.007042 + 0.007209 + 0.007834 + 0.007875 + 0.008333 + 0.00775 + 0.00725 + 0.009333 + 0.008417 + 0.007709 + 0.006 + 0.011208 + 0.005917 + 0.005625 + 0.005083 + 0.00575 + 0.006167 + 0.007416 + 0.006417 + 0.005916 + 0.005833 + 0.005417 + 0.005834 + 0.005625 + 0.005459 + 0.005417 + 0.004583 + 0.00475 + 0.004667 + 0.005125 + 0.005 + 0.006042 + 0.00625 + 0.006542 + 0.006583 + 0.006625 + 0.006875 + 0.006542 + 0.005584 + 0.00575 + 0.004958 + 0.005542 + 0.005084 + 0.005 + 0.005292 + 0.005708 + 0.005417 + 0.005917 + 0.00625 + 0.006625 + 0.006125 + 0.006458 + 0.006792 + 0.00525 + 0.005208 + 0.005167 + 0.006292 + 0.006833 + 0.0075 + 0.006292 + 0.00675 + 0.006792 + 0.006458 + 0.006583 + 0.006041 + 0.006042 + 0.006583 + 0.01 + 0.00675 + 0.006166 + 0.00575 + 0.005375 + 0.007917 + 0.008708 + 0.0085 + 0.007083 + 0.006958 + 0.007 + 0.006708 + 0.006708 + 0.006125 + 0.005208 + 0.005125 + 0.005375 + 0.005166 + 0.006 + 0.005 + 0.006 + 0.005667 + 0.005208 + 0.006708 + 0.007125 + 0.006416 + 0.005792 + 0.007417 + 0.006625 + 0.005584 + 0.006209 + 0.00575 + 0.007584 + 0.007125 + 0.005958 + 0.007375 + 0.007166 + 0.006209 + 0.00625 + 0.008083 + 0.00825 + 0.007541 + 0.007417 + 0.009333 + 0.005458 + 0.005958 + 0.006083 + 0.006292 + 0.006125 + 0.006375 + 0.0065 + 0.005583 + 0.005167 + 0.004542 + 0.006 + 0.005084 + 0.005291 + 0.006083 + 0.005 + 0.005041 + 0.005583 + 0.00525 + 0.005375 + 0.00525 + 0.005291 + 0.005792 + 0.005375 + 0.005209 + 0.005166 + 0.005292 + 0.005125 + 0.004792 + 0.004916 + 0.005084 + 0.004708 + 0.004583 + 0.005041 + 0.004584 + 0.004584 + 0.004625 + 0.005459 + 0.00575 + 0.005625 + 0.006041 + 0.006667 + 0.006625 + 0.006417 + 0.006542 + 0.00625 + 0.006459 + 0.005875 + 0.005667 + 0.005375 + 0.004916 + 0.005167 + 0.005167 + 0.005 + 0.005167 + 0.009791 + 0.006083 + 0.006833 + 0.00675 + 0.007084 + 0.006166 + 0.006167 + 0.006875 + 0.007208 + 0.017083 + 0.005708 + 0.005 + 0.00575 + 0.005333 + 0.005333 + 0.005291 + 0.005375 + 0.005334 + 0.005625 + 0.005375 + 0.005375 + 0.005458 + 0.009208 + 0.005292 + 0.005041 + 0.006375 + 0.006666 + 0.006458 + 0.006083 + 0.00725 + 0.007375 + 0.006833 + 0.007333 + 0.007542 + 0.00875 + 0.00725 + 0.007083 + 0.00725 + 0.007958 + 0.007208 + 0.007583 + 0.009 + 0.00725 + 0.006625 + 0.007083 + 0.006875 + 0.006333 + 0.006541 + 0.006042 + 0.0065 + 0.006542 + 0.006208 + 0.007083 + 0.006291 + 0.006875 + 0.005875 + 0.006542 + 0.006666 + 0.006917 + 0.006042 + 0.006125 + 0.005042 + 0.00525 + 0.005334 + 0.005416 + 0.005667 + 0.005458 + 0.006416 + 0.006292 + 0.006209 + 0.006375 + 0.006167 + 0.006792 + 0.006541 + 0.00625 + 0.006166 + 0.006625 + 0.00575 + 0.00675 + 0.006083 + 0.005167 + 0.004708 + 0.005333 + 0.00725 + 0.006708 + 0.006583 + 0.006208 + 0.005791 + 0.005667 + 0.005625 + 0.005292 + 0.00575 + 0.005334 + 0.0055 + 0.006541 + 0.005958 + 0.006167 + 0.006666 + 0.007708 + 0.007583 + 0.007 + 0.007042 + 0.006291 + 0.006958 + 0.006792 + 0.005917 + 0.006291 + 0.006625 + 0.009167 + 0.008542 + 0.007708 + 0.006916 + 0.007333 + 0.007167 + 0.005917 + 0.005791 + 0.005542 + 0.005416 + 0.005833 + 0.0055 + 0.005416 + 0.005042 + 0.005541 + 0.005083 + 0.005375 + 0.004917 + 0.006833 + 0.007084 + 0.006625 + 0.008083 + 0.007583 + 0.007583 + 0.00725 + 0.006333 + 0.008875 + 0.00825 + 0.007792 + 0.006375 + 0.006292 + 0.006416 + 0.006542 + 0.008375 + 0.008042 + 0.008042 + 0.008416 + 0.007208 + 0.006833 + 0.006125 + 0.006708 + 0.006375 + 0.005375 + 0.005 + 0.006417 + 0.00625 + 0.004959 + 0.005 + 0.005167 + 0.00475 + 0.004792 + 0.004875 + 0.00475 + 0.005041 + 0.005 + 0.005125 + 0.004958 + 0.004833 + 0.005041 + 0.005084 + 0.005166 + 0.005042 + 0.005291 + 0.005083 + 0.008083 + 0.005584 + 0.0055 + 0.006542 + 0.006541 + 0.007333 + 0.00675 + 0.018208 + 0.023709 + 0.008292 + 0.031375 + 0.00625 + 0.054875 + 0.008209 + 0.005625 + 0.005833 + 0.005833 + 0.005708 + 0.00575 + 0.005833 + 0.005166 + 0.006083 + 0.006625 + 0.005625 + 0.006042 + 0.005625 + 0.006041 + 0.006417 + 0.006 + 0.008667 + 0.008333 + 0.009 + 0.007958 + 0.008791 + 0.035167 + 0.009208 + 0.011958 + 0.03175 + 0.008708 + 0.006833 + 0.007417 + 0.006166 + 0.005667 + 0.005917 + 0.006208 + 0.005958 + 0.006125 + 0.006083 + 0.005333 + 0.005709 + 0.006791 + 0.007 + 0.006833 + 0.0065 + 0.006 + 0.007084 + 0.007 + 0.006416 + 0.009875 + 0.008625 + 0.007667 + 0.007916 + 0.008458 + 0.007 + 0.007958 + 0.0065 + 0.006625 + 0.006417 + 0.006375 + 0.006208 + 0.005833 + 0.007333 + 0.008083 + 0.007625 + 0.006958 + 0.006625 + 0.006542 + 0.006333 + 0.005208 + 0.005292 + 0.00525 + 0.006292 + 0.005041 + 0.005292 + 0.005666 + 0.005625 + 0.005042 + 0.004792 + 0.005334 + 0.005291 + 0.006292 + 0.007042 + 0.007416 + 0.00675 + 0.006625 + 0.00725 + 0.006959 + 0.006209 + 0.00625 + 0.006709 + 0.006208 + 0.0055 + 0.005125 + 0.005416 + 0.005667 + 0.005667 + 0.005333 + 0.004958 + 0.006458 + 0.006166 + 0.006125 + 0.006625 + 0.005916 + 0.007917 + 0.006375 + 0.0065 + 0.006291 + 0.006167 + 0.006083 + 0.006 + 0.005958 + 0.005916 + 0.006833 + 0.006042 + 0.00575 + 0.006625 + 0.006542 + 0.006416 + 0.006083 + 0.006667 + 0.007 + 0.038542 + 0.010334 + 0.007625 + 0.007875 + 0.007667 + 0.007959 + 0.00825 + 0.007792 + 0.00675 + 0.006375 + 0.006709 + 0.006625 + 0.005917 + 0.006916 + 0.006583 + 0.006667 + 0.006583 + 0.007041 + 0.007584 + 0.007542 + 0.010416 + 0.007083 + 0.007792 + 0.009709 + 0.008333 + 0.008125 + 0.006083 + 0.008 + 0.006958 + 0.007083 + 0.00725 + 0.007208 + 0.008041 + 0.007458 + 0.006125 + 0.006833 + 0.007625 + 0.007875 + 0.008375 + 0.011791 + 0.010625 + 0.009292 + 0.0105 + 0.007667 + 0.008041 + 0.007667 + 0.007416 + 0.0065 + 0.007 + 0.005875 + 0.009125 + 0.008 + 0.008042 + 0.00725 + 0.00725 + 0.00775 + 0.009167 + 0.0095 + 0.008708 + 0.007 + 0.008042 + 0.006041 + 0.006083 + 0.00625 + 0.006083 + 0.006083 + 0.005667 + 0.005458 + 0.005417 + 0.005625 + 0.006375 + 0.007083 + 0.007208 + 0.007083 + 0.0065 + 0.005375 + 0.006042 + 0.005917 + 0.005709 + 0.005959 + 0.005542 + 0.00575 + 0.005666 + 0.00525 + 0.005208 + 0.006 + 0.005458 + 0.005709 + 0.005541 + 0.005875 + 0.005084 + 0.006334 + 0.005625 + 0.005584 + 0.005583 + 0.005542 + 0.005792 + 0.005542 + 0.0075 + 0.006834 + 0.008334 + 0.007875 + 0.008417 + 0.008833 + 0.007583 + 0.008917 + 0.008292 + 0.008166 + 0.007583 + 0.005833 + 0.00725 + 0.006875 + 0.005625 + 0.005792 + 0.005125 + 0.00575 + 0.005792 + 0.006333 + 0.005833 + 0.006416 + 0.005708 + 0.007292 + 0.007167 + 0.00725 + 0.006959 + 0.006959 + 0.007667 + 0.007708 + 0.006791 + 0.006083 + 0.006375 + 0.008417 + 0.00825 + 0.008084 + 0.008167 + 0.007166 + 0.008458 + 0.006792 + 0.008208 + 0.00825 + 0.008042 + 0.008208 + 0.008375 + 0.007542 + 0.00575 + 0.005333 + 0.005917 + 0.00575 + 0.006042 + 0.006958 + 0.007666 + 0.007917 + 0.008166 + 0.006792 + 0.006875 + 0.007667 + 0.006584 + 0.007 + 0.00575 + 0.005 + 0.005667 + 0.005459 + 0.005834 + 0.005209 + 0.005542 + 0.00525 + 0.005333 + 0.005125 + 0.005791 + 0.006125 + 0.005541 + 0.005667 + 0.005291 + 0.005333 + 0.004917 + 0.005334 + 0.0055 + 0.005458 + 0.0055 + 0.006333 + 0.005125 + 0.004833 + 0.005125 + 0.005667 + 0.008041 + 0.006917 + 0.006542 + 0.006458 + 0.02175 + 0.0085 + 0.006792 + 0.008333 + 0.007917 + 0.005959 + 0.005792 + 0.00575 + 0.007667 + 0.006334 + 0.006417 + 0.006542 + 0.007166 + 0.007375 + 0.006458 + 0.006292 + 0.006792 + 0.008375 + 0.008042 + 0.008209 + 0.007916 + 0.006792 + 0.006792 + 0.007375 + 0.006792 + 0.006083 + 0.006042 + 0.005625 + 0.005917 + 0.00575 + 0.006125 + 0.006708 + 0.006334 + 0.005459 + 0.007416 + 0.025667 + 0.009209 + 0.008291 + 0.008708 + 0.011667 + 0.007542 + 0.006583 + 0.006291 + 0.006834 + 0.008333 + 0.008291 + 0.007708 + 0.009083 + 0.009125 + 0.006375 + 0.006625 + 0.008291 + 0.008 + 0.008125 + 0.007709 + 0.009167 + 0.006709 + 0.005709 + 0.005625 + 0.005917 + 0.005708 + 0.005917 + 0.00575 + 0.006959 + 0.005958 + 0.005958 + 0.007542 + 0.008875 + 0.006875 + 0.006542 + 0.005375 + 0.005334 + 0.005791 + 0.00575 + 0.005458 + 0.005958 + 0.005708 + 0.005417 + 0.005333 + 0.006416 + 0.00525 + 0.005167 + 0.005542 + 0.005792 + 0.00575 + 0.005875 + 0.005709 + 0.0055 + 0.005334 + 0.005334 + 0.005667 + 0.005667 + 0.005709 + 0.005708 + 0.005834 + 0.005583 + 0.005792 + 0.005584 + 0.005542 + 0.005417 + 0.005417 + 0.00525 + 0.005167 + 0.0065 + 0.0085 + 0.005667 + 0.005917 + 0.006209 + 0.00625 + 0.005708 + 0.005709 + 0.007083 + 0.008542 + 0.009584 + 0.007459 + 0.006834 + 0.006834 + 0.0065 + 0.008083 + 0.008042 + 0.008542 + 0.006542 + 0.0075 + 0.006083 + 0.006 + 0.006458 + 0.005833 + 0.005584 + 0.005875 + 0.005917 + 0.005667 + 0.006 + 0.006292 + 0.005542 + 0.005834 + 0.005583 + 0.007625 + 0.007 + 0.007125 + 0.00625 + 0.008 + 0.006667 + 0.005375 + 0.006 + 0.006583 + 0.008375 + 0.008083 + 0.006916 + 0.007625 + 0.0065 + 0.00625 + 0.0065 + 0.009833 + 0.008 + 0.007833 + 0.007166 + 0.005542 + 0.005166 + 0.005125 + 0.00525 + 0.005375 + 0.005583 + 0.005584 + 0.005708 + 0.00575 + 0.005084 + 0.005584 + 0.005709 + 0.005292 + 0.00525 + 0.005042 + 0.004875 + 0.005417 + 0.005375 + 0.005709 + 0.004625 + 0.004917 + 0.006 + 0.005834 + 0.006292 + 0.005625 + 0.00575 + 0.0055 + 0.005625 + 0.005125 + 0.00525 + 0.005375 + 0.00675 + 0.007667 + 0.007458 + 0.006916 + 0.005667 + 0.006542 + 0.00575 + 0.005625 + 0.00625 + 0.00625 + 0.006333 + 0.006375 + 0.005583 + 0.005458 + 0.005875 + 0.005167 + 0.006208 + 0.005708 + 0.006166 + 0.005208 + 0.005583 + 0.005625 + 0.005542 + 0.005875 + 0.007709 + 0.007417 + 0.007875 + 0.007458 + 0.007583 + 0.007334 + 0.006916 + 0.0095 + 0.008542 + 0.009459 + 0.007375 + 0.005792 + 0.006792 + 0.006333 + 0.006833 + 0.006292 + 0.006833 + 0.006417 + 0.006958 + 0.007542 + 0.00625 + 0.006542 + 0.009375 + 0.008125 + 0.006042 + 0.0075 + 0.007542 + 0.008209 + 0.01225 + 0.0095 + 0.008875 + 0.008625 + 0.008875 + 0.008791 + 0.009125 + 0.008166 + 0.007583 + 0.00725 + 0.007958 + 0.00825 + 0.007292 + 0.00875 + 0.008209 + 0.008209 + 0.007875 + 0.006833 + 0.007 + 0.006125 + 0.005958 + 0.005167 + 0.005292 + 0.005167 + 0.005083 + 0.005958 + 0.005083 + 0.005708 + 0.005917 + 0.005458 + 0.005959 + 0.005375 + 0.006 + 0.00575 + 0.0065 + 0.006375 + 0.00725 + 0.006791 + 0.006458 + 0.006166 + 0.005417 + 0.005708 + 0.005667 + 0.00625 + 0.006459 + 0.007667 + 0.007958 + 0.008208 + 0.007083 + 0.007542 + 0.006916 + 0.006625 + 0.006125 + 0.006625 + 0.007125 + 0.007083 + 0.006667 + 0.006334 + 0.006834 + 0.007792 + 0.007209 + 0.008125 + 0.007167 + 0.00725 + 0.007083 + 0.00775 + 0.007958 + 0.006833 + 0.007625 + 0.006459 + 0.006792 + 0.00625 + 0.007541 + 0.007959 + 0.007709 + 0.006959 + 0.007833 + 0.007333 + 0.00725 + 0.008583 + 0.008291 + 0.007417 + 0.006833 + 0.005333 + 0.006667 + 0.007083 + 0.006917 + 0.006792 + 0.007 + 0.006625 + 0.044792 + 0.012042 + 0.007458 + 0.006542 + 0.005875 + 0.006208 + 0.008875 + 0.006292 + 0.008125 + 0.007833 + 0.007625 + 0.008584 + 0.007084 + 0.006834 + 0.006875 + 0.008083 + 0.008708 + 0.007583 + 0.007375 + 0.0065 + 0.007084 + 0.007041 + 0.006875 + 0.007917 + 0.007417 + 0.00825 + 0.007667 + 0.006042 + 0.006417 + 0.005708 + 0.005833 + 0.00625 + 0.006458 + 0.006458 + 0.006084 + 0.0075 + 0.007208 + 0.006667 + 0.007041 + 0.006917 + 0.007709 + 0.006833 + 0.007041 + 0.007 + 0.006458 + 0.006375 + 0.006834 + 0.006875 + 0.0065 + 0.005958 + 0.006375 + 0.006667 + 0.005458 + 0.005958 + 0.005834 + 0.006208 + 0.005458 + 0.00625 + 0.006 + 0.005792 + 0.005708 + 0.006958 + 0.006958 + 0.007625 + 0.007125 + 0.006667 + 0.0065 + 0.006375 + 0.006125 + 0.005833 + 0.00625 + 0.0065 + 0.006333 + 0.006458 + 0.006375 + 0.006916 + 0.007 + 0.006708 + 0.00675 + 0.007041 + 0.00675 + 0.006333 + 0.0075 + 0.007416 + 0.008375 + 0.009459 + 0.009 + 0.007792 + 0.00775 + 0.007417 + 0.009625 + 0.009167 + 0.007333 + 0.00775 + 0.008292 + 0.006875 + 0.007459 + 0.082834 + 0.014 + 0.008708 + 0.007875 + 0.007208 + 0.007791 + 0.007292 + 0.007542 + 0.018417 + 0.041584 + 0.042666 + 0.008791 + 0.006042 + 0.006791 + 0.008834 + 0.008625 + 0.006917 + 0.007 + 0.00725 + 0.007833 + 0.008708 + 0.008292 + 0.008 + 0.008 + 0.006875 + 0.007333 + 0.00775 + 0.007125 + 0.007166 + 0.006875 + 0.00625 + 0.005875 + 0.005792 + 0.005583 + 0.00575 + 0.005292 + 0.005917 + 0.006209 + 0.006334 + 0.005417 + 0.005416 + 0.006334 + 0.006292 + 0.00625 + 0.0065 + 0.006833 + 0.007458 + 0.006083 + 0.006625 + 0.006583 + 0.0065 + 0.006541 + 0.0065 + 0.006333 + 0.006 + 0.005833 + 0.006 + 0.005958 + 0.005625 + 0.00575 + 0.006375 + 0.005417 + 0.005584 + 0.005709 + 0.005125 + 0.005042 + 0.004875 + 0.004916 + 0.0055 + 0.005334 + 0.005667 + 0.006541 + 0.006917 + 0.006666 + 0.007167 + 0.007167 + 0.006375 + 0.005792 + 0.007375 + 0.0075 + 0.007292 + 0.006833 + 0.007 + 0.00725 + 0.007334 + 0.0085 + 0.009459 + 0.009291 + 0.010208 + 0.010167 + 0.010375 + 0.008833 + 0.007708 + 0.009583 + 0.009 + 0.009042 + 0.007958 + 0.009 + 0.008166 + 0.007375 + 0.007291 + 0.006625 + 0.007167 + 0.006792 + 0.007458 + 0.008292 + 0.007292 + 0.008125 + 0.007 + 0.006875 + 0.007208 + 0.009125 + 0.008584 + 0.007875 + 0.006333 + 0.011917 + 0.005916 + 0.00625 + 0.006292 + 0.006542 + 0.008583 + 0.008042 + 0.006625 + 0.00725 + 0.006875 + 0.006416 + 0.006 + 0.008417 + 0.008459 + 0.008042 + 0.007208 + 0.006666 + 0.005209 + 0.005541 + 0.004917 + 0.005334 + 0.005333 + 0.006041 + 0.00575 + 0.005459 + 0.0065 + 0.005333 + 0.005458 + 0.005375 + 0.005083 + 0.005166 + 0.005375 + 0.006292 + 0.005459 + 0.005708 + 0.005083 + 0.004875 + 0.005625 + 0.0055 + 0.005958 + 0.005792 + 0.005042 + 0.004917 + 0.005167 + 0.005458 + 0.0055 + 0.00625 + 0.007334 + 0.007084 + 0.006375 + 0.007125 + 0.0065 + 0.006917 + 0.007209 + 0.007584 + 0.007417 + 0.007625 + 0.007 + 0.006417 + 0.005959 + 0.004917 + 0.004875 + 0.005334 + 0.005584 + 0.005542 + 0.005667 + 0.005209 + 0.005625 + 0.005542 + 0.005959 + 0.005666 + 0.0075 + 0.006666 + 0.007792 + 0.007 + 0.007125 + 0.005875 + 0.006 + 0.007834 + 0.008 + 0.007583 + 0.00625 + 0.005542 + 0.005292 + 0.006166 + 0.005209 + 0.005542 + 0.00575 + 0.005625 + 0.005666 + 0.0055 + 0.005708 + 0.005667 + 0.006709 + 0.007375 + 0.006917 + 0.007292 + 0.005833 + 0.005667 + 0.005292 + 0.006958 + 0.006583 + 0.00775 + 0.00875 + 0.008333 + 0.007416 + 0.007583 + 0.00675 + 0.010875 + 0.008875 + 0.007625 + 0.009166 + 0.007542 + 0.008167 + 0.008125 + 0.00775 + 0.006959 + 0.006375 + 0.007458 + 0.007292 + 0.005375 + 0.004834 + 0.004792 + 0.004791 + 0.006125 + 0.004833 + 0.005291 + 0.005917 + 0.005666 + 0.005458 + 0.00525 + 0.005083 + 0.005083 + 0.004917 + 0.005083 + 0.004958 + 0.0055 + 0.005708 + 0.005125 + 0.005 + 0.005 + 0.005166 + 0.005292 + 0.006167 + 0.006375 + 0.00575 + 0.005291 + 0.005208 + 0.005084 + 0.005041 + 0.007833 + 0.005167 + 0.004833 + 0.006083 + 0.00575 + 0.005791 + 0.005083 + 0.005334 + 0.005042 + 0.005417 + 0.005334 + 0.00525 + 0.004917 + 0.005083 + 0.005333 + 0.00625 + 0.005833 + 0.0065 + 0.007792 + 0.00725 + 0.006708 + 0.0065 + 0.00625 + 0.004917 + 0.007208 + 0.007 + 0.006375 + 0.005792 + 0.007708 + 0.009625 + 0.007167 + 0.00625 + 0.0055 + 0.005583 + 0.005708 + 0.00525 + 0.005125 + 0.005166 + 0.005333 + 0.005209 + 0.007959 + 0.005625 + 0.005542 + 0.006208 + 0.007458 + 0.006917 + 0.006541 + 0.007 + 0.007041 + 0.006916 + 0.007708 + 0.007333 + 0.006584 + 0.006209 + 0.0075 + 0.007833 + 0.007583 + 0.007625 + 0.0065 + 0.008042 + 0.007125 + 0.007833 + 0.007791 + 0.00775 + 0.008042 + 0.007792 + 0.006666 + 0.006042 + 0.005042 + 0.004833 + 0.005083 + 0.004875 + 0.005083 + 0.004625 + 0.005417 + 0.005584 + 0.004833 + 0.005042 + 0.005 + 0.005125 + 0.004958 + 0.004875 + 0.005125 + 0.004958 + 0.004958 + 0.004791 + 0.00525 + 0.005 + 0.005208 + 0.004875 + 0.004708 + 0.005125 + 0.00525 + 0.005667 + 0.005666 + 0.004666 + 0.004875 + 0.004708 + 0.005166 + 0.004958 + 0.005459 + 0.005042 + 0.004917 + 0.004916 + 0.004708 + 0.005042 + 0.005417 + 0.00475 + 0.005 + 0.005 + 0.004833 + 0.004958 + 0.004833 + 0.004791 + 0.005292 + 0.005334 + 0.005208 + 0.0055 + 0.005416 + 0.006667 + 0.0065 + 0.006583 + 0.0065 + 0.006708 + 0.008334 + 0.007584 + 0.006167 + 0.005958 + 0.007167 + 0.008667 + 0.007708 + 0.006958 + 0.006666 + 0.006834 + 0.006458 + 0.005 + 0.006833 + 0.006833 + 0.006708 + 0.006625 + 0.006541 + 0.006625 + 0.006458 + 0.00525 + 0.00725 + 0.007333 + 0.006583 + 0.006083 + 0.0065 + 0.006875 + 0.005542 + 0.006667 + 0.009875 + 0.00625 + 0.005958 + 0.007208 + 0.007833 + 0.007666 + 0.006375 + 0.007541 + 0.007459 + 0.007167 + 0.007458 + 0.007583 + 0.007666 + 0.007625 + 0.007416 + 0.006 + 0.006041 + 0.005208 + 0.0045 + 0.004583 + 0.004542 + 0.004792 + 0.005583 + 0.004625 + 0.004875 + 0.005584 + 0.005125 + 0.005208 + 0.004833 + 0.00525 + 0.00475 + 0.004833 + 0.005208 + 0.004875 + 0.004875 + 0.004958 + 0.004958 + 0.004791 + 0.004917 + 0.005083 + 0.006 + 0.006125 + 0.006375 + 0.006709 + 0.005709 + 0.00575 + 0.005375 + 0.00625 + 0.006209 + 0.006792 + 0.006541 + 0.005458 + 0.005583 + 0.005292 + 0.005167 + 0.004875 + 0.005167 + 0.005208 + 0.004667 + 0.004709 + 0.004958 + 0.004708 + 0.005 + 0.005208 + 0.005167 + 0.005042 + 0.005166 + 0.006583 + 0.007 + 0.00625 + 0.006291 + 0.006125 + 0.007417 + 0.007666 + 0.006083 + 0.006084 + 0.007708 + 0.011291 + 0.007708 + 0.008083 + 0.006917 + 0.0065 + 0.005333 + 0.005333 + 0.005083 + 0.010417 + 0.005292 + 0.005083 + 0.006667 + 0.007083 + 0.006416 + 0.006958 + 0.006875 + 0.006541 + 0.00675 + 0.005833 + 0.005459 + 0.005166 + 0.005375 + 0.005958 + 0.0055 + 0.005542 + 0.007541 + 0.007792 + 0.007542 + 0.007333 + 0.006667 + 0.0075 + 0.006958 + 0.00725 + 0.007875 + 0.007542 + 0.00775 + 0.007667 + 0.0075 + 0.005917 + 0.004875 + 0.004625 + 0.004541 + 0.00475 + 0.004875 + 0.004958 + 0.004875 + 0.004625 + 0.00475 + 0.004792 + 0.005375 + 0.005042 + 0.004708 + 0.004667 + 0.004708 + 0.004875 + 0.005042 + 0.005 + 0.004708 + 0.004792 + 0.004917 + 0.004708 + 0.004792 + 0.004834 + 0.004916 + 0.004792 + 0.004875 + 0.004333 + 0.004333 + 0.004625 + 0.004709 + 0.00475 + 0.004875 + 0.004667 + 0.005125 + 0.004791 + 0.005042 + 0.004875 + 0.005 + 0.004708 + 0.004708 + 0.004833 + 0.005084 + 0.004708 + 0.00475 + 0.00475 + 0.004708 + 0.005 + 0.007041 + 0.007167 + 0.006667 + 0.006791 + 0.006667 + 0.0065 + 0.007 + 0.005333 + 0.005916 + 0.005625 + 0.005875 + 0.007333 + 0.007416 + 0.007583 + 0.008292 + 0.007666 + 0.007042 + 0.006292 + 0.005292 + 0.005042 + 0.004958 + 0.007125 + 0.006792 + 0.006375 + 0.006709 + 0.007125 + 0.007167 + 0.007 + 0.006125 + 0.005375 + 0.005084 + 0.005042 + 0.00475 + 0.005667 + 0.005333 + 0.006166 + 0.006083 + 0.008042 + 0.008042 + 0.008125 + 0.007792 + 0.00875 + 0.095458 + 0.043958 + 0.021167 + 0.008 + 0.00725 + 0.007875 + 0.007542 + 0.015666 + 0.007875 + 0.00725 + 0.005917 + 0.005875 + 0.006333 + 0.005375 + 0.006208 + 0.005584 + 0.00625 + 0.005417 + 0.005208 + 0.006083 + 0.005792 + 0.007875 + 0.005458 + 0.005875 + 0.005833 + 0.005625 + 0.005625 + 0.005709 + 0.005334 + 0.005334 + 0.005291 + 0.006333 + 0.005708 + 0.006083 + 0.005292 + 0.005458 + 0.005584 + 0.006375 + 0.0055 + 0.005375 + 0.00525 + 0.005667 + 0.005 + 0.005375 + 0.005542 + 0.0055 + 0.00525 + 0.0055 + 0.005167 + 0.005167 + 0.005291 + 0.005 + 0.005042 + 0.005209 + 0.00525 + 0.004959 + 0.005291 + 0.007083 + 0.005792 + 0.005666 + 0.006292 + 0.005834 + 0.005542 + 0.005833 + 0.005583 + 0.005667 + 0.006709 + 0.008083 + 0.00975 + 0.00675 + 0.006541 + 0.005917 + 0.006333 + 0.006375 + 0.005958 + 0.005834 + 0.007291 + 0.005792 + 0.007834 + 0.005792 + 0.005583 + 0.005417 + 0.005208 + 0.006416 + 0.006209 + 0.005458 + 0.005666 + 0.005583 + 0.005417 + 0.005375 + 0.005917 + 0.005458 + 0.006 + 0.007375 + 0.012833 + 0.00725 + 0.006334 + 0.006292 + 0.006166 + 0.006542 + 0.008708 + 0.005542 + 0.006375 + 0.005917 + 0.00625 + 0.006041 + 0.006292 + 0.008709 + 0.0065 + 0.005 + 0.004833 + 0.005167 + 0.005375 + 0.005125 + 0.005417 + 0.004958 + 0.00525 + 0.005833 + 0.004958 + 0.00475 + 0.005042 + 0.005916 + 0.005208 + 0.004958 + 0.005208 + 0.005375 + 0.005 + 0.005166 + 0.005125 + 0.005291 + 0.005042 + 0.005125 + 0.005333 + 0.005375 + 0.00525 + 0.005 + 0.005208 + 0.005084 + 0.004792 + 0.005208 + 0.005208 + 0.005667 + 0.004959 + 0.005083 + 0.004791 + 0.004542 + 0.005125 + 0.005375 + 0.00525 + 0.005667 + 0.005875 + 0.005875 + 0.02675 + 0.007958 + 0.004833 + 0.00475 + 0.004833 + 0.005292 + 0.006334 + 0.00525 + 0.005625 + 0.005166 + 0.00525 + 0.005 + 0.004875 + 0.004708 + 0.005583 + 0.007167 + 0.006 + 0.008583 + 0.0075 + 0.006875 + 0.007041 + 0.006 + 0.005292 + 0.005875 + 0.005334 + 0.005 + 0.005209 + 0.005292 + 0.006458 + 0.006333 + 0.006375 + 0.006875 + 0.005791 + 0.004959 + 0.00525 + 0.005292 + 0.005167 + 0.00525 + 0.005041 + 0.005083 + 0.005125 + 0.00475 + 0.006375 + 0.00575 + 0.006917 + 0.007417 + 0.00725 + 0.007 + 0.00625 + 0.005291 + 0.006208 + 0.006959 + 0.007292 + 0.007125 + 0.007375 + 0.006541 + 0.006084 + 0.007334 + 0.006583 + 0.005791 + 0.006083 + 0.006292 + 0.005584 + 0.005084 + 0.005125 + 0.004917 + 0.00475 + 0.004542 + 0.005 + 0.00525 + 0.005375 + 0.004958 + 0.004917 + 0.005292 + 0.005 + 0.004709 + 0.004917 + 0.004958 + 0.005 + 0.005292 + 0.00475 + 0.005292 + 0.005167 + 0.005333 + 0.004792 + 0.0055 + 0.004292 + 0.005125 + 0.005291 + 0.005584 + 0.005042 + 0.004584 + 0.004625 + 0.004542 + 0.005041 + 0.004667 + 0.005125 + 0.005375 + 0.005208 + 0.005334 + 0.005625 + 0.004917 + 0.005167 + 0.005084 + 0.00525 + 0.005541 + 0.005833 + 0.006125 + 0.005541 + 0.00525 + 0.005333 + 0.004958 + 0.005208 + 0.0055 + 0.005416 + 0.006 + 0.006291 + 0.008333 + 0.006833 + 0.006708 + 0.011042 + 0.006417 + 0.006166 + 0.006792 + 0.006041 + 0.006833 + 0.008458 + 0.006708 + 0.00625 + 0.005916 + 0.005375 + 0.006084 + 0.00575 + 0.005167 + 0.005458 + 0.005375 + 0.004958 + 0.005125 + 0.004792 + 0.005125 + 0.005542 + 0.005041 + 0.006417 + 0.008125 + 0.006958 + 0.006708 + 0.006875 + 0.006417 + 0.00575 + 0.004709 + 0.007083 + 0.007042 + 0.006917 + 0.006833 + 0.006583 + 0.006916 + 0.0085 + 0.0075 + 0.006625 + 0.006334 + 0.006875 + 0.006083 + 0.006333 + 0.005458 + 0.005 + 0.004833 + 0.004875 + 0.005792 + 0.006084 + 0.007041 + 0.006084 + 0.005958 + 0.006625 + 0.006542 + 0.006291 + 0.005459 + 0.006416 + 0.006125 + 0.006291 + 0.006916 + 0.007708 + 0.007 + 0.006 + 0.005958 + 0.004625 + 0.005 + 0.004708 + 0.00525 + 0.004958 + 0.00525 + 0.005166 + 0.00475 + 0.004666 + 0.005833 + 0.006291 + 0.00675 + 0.006666 + 0.005584 + 0.005791 + 0.005667 + 0.005708 + 0.005417 + 0.005166 + 0.005459 + 0.005375 + 0.004625 + 0.005458 + 0.0055 + 0.005667 + 0.004666 + 0.005167 + 0.005375 + 0.005375 + 0.004875 + 0.005583 + 0.006542 + 0.008334 + 0.008209 + 0.008459 + 0.00725 + 0.006667 + 0.006167 + 0.007834 + 0.007208 + 0.006542 + 0.005542 + 0.004959 + 0.005542 + 0.005208 + 0.005334 + 0.005291 + 0.005542 + 0.005209 + 0.005625 + 0.005417 + 0.005458 + 0.004833 + 0.005416 + 0.004708 + 0.004917 + 0.005333 + 0.005291 + 0.007625 + 0.01175 + 0.007708 + 0.008125 + 0.007834 + 0.007292 + 0.007958 + 0.007833 + 0.006791 + 0.007875 + 0.008375 + 0.008084 + 0.007541 + 0.007917 + 0.007917 + 0.007958 + 0.0065 + 0.0055 + 0.005708 + 0.005542 + 0.006042 + 0.005959 + 0.005125 + 0.005833 + 0.005875 + 0.006542 + 0.005834 + 0.006416 + 0.007375 + 0.006833 + 0.0065 + 0.00625 + 0.00625 + 0.005208 + 0.00625 + 0.005416 + 0.006333 + 0.005542 + 0.005792 + 0.005375 + 0.005875 + 0.005875 + 0.007875 + 0.00775 + 0.007333 + 0.005292 + 0.005375 + 0.005458 + 0.005791 + 0.004917 + 0.005292 + 0.005875 + 0.005667 + 0.005334 + 0.005416 + 0.005625 + 0.005083 + 0.00525 + 0.005583 + 0.005083 + 0.00525 + 0.005042 + 0.004959 + 0.005292 + 0.004958 + 0.005625 + 0.006208 + 0.005667 + 0.0055 + 0.005292 + 0.0055 + 0.005166 + 0.005458 + 0.00575 + 0.008542 + 0.008084 + 0.007542 + 0.007333 + 0.007916 + 0.008042 + 0.009 + 0.007292 + 0.006958 + 0.007 + 0.006959 + 0.007 + 0.007042 + 0.005042 + 0.005292 + 0.005375 + 0.005583 + 0.005542 + 0.005458 + 0.00525 + 0.005542 + 0.005334 + 0.005417 + 0.005416 + 0.005542 + 0.006 + 0.007833 + 0.008125 + 0.007584 + 0.007792 + 0.008 + 0.010375 + 0.008459 + 0.007792 + 0.007625 + 0.008084 + 0.007416 + 0.007625 + 0.007583 + 0.009875 + 0.008583 + 0.007916 + 0.007291 + 0.006542 + 0.006292 + 0.007084 + 0.007 + 0.006583 + 0.00625 + 0.006708 + 0.006625 + 0.006042 + 0.005417 + 0.00675 + 0.006917 + 0.006125 + 0.006708 + 0.006625 + 0.006583 + 0.006333 + 0.006917 + 0.007375 + 0.006583 + 0.007041 + 0.006833 + 0.006542 + 0.005541 + 0.005542 + 0.005709 + 0.005584 + 0.00575 + 0.005542 + 0.005958 + 0.005667 + 0.005875 + 0.005125 + 0.005458 + 0.005583 + 0.005417 + 0.005542 + 0.007042 + 0.0685 + 0.011667 + 0.00675 + 0.006167 + 0.006042 + 0.005833 + 0.005875 + 0.005959 + 0.005833 + 0.008625 + 0.009458 + 0.008 + 0.005708 + 0.007166 + 0.007625 + 0.016375 + 0.00975 + 0.006959 + 0.006417 + 0.009541 + 0.007458 + 0.009584 + 0.009166 + 0.007333 + 0.042 + 0.009125 + 0.00725 + 0.006709 + 0.007959 + 0.006917 + 0.006625 + 0.006 + 0.00825 + 0.007333 + 0.005959 + 0.007125 + 0.006334 + 0.007417 + 0.006917 + 0.007333 + 0.007167 + 0.005917 + 0.007291 + 0.005459 + 0.008667 + 0.007042 + 0.009583 + 0.008875 + 0.021041 + 0.012083 + 0.016583 + 0.008708 + 0.008792 + 0.008084 + 0.008917 + 0.008166 + 0.008458 + 0.008333 + 0.008042 + 0.007458 + 0.00725 + 0.007583 + 0.006667 + 0.005667 + 0.00625 + 0.006916 + 0.006875 + 0.008542 + 0.015834 + 0.009667 + 0.005584 + 0.006 + 0.0065 + 0.007167 + 0.006 + 0.005875 + 0.005125 + 0.006375 + 0.0065 + 0.006292 + 0.005916 + 0.005792 + 0.0055 + 0.006208 + 0.006208 + 0.00625 + 0.006208 + 0.006333 + 0.005917 + 0.005875 + 0.005583 + 0.00525 + 0.0055 + 0.005875 + 0.005792 + 0.005625 + 0.006167 + 0.006166 + 0.009 + 0.00775 + 0.008959 + 0.008334 + 0.009208 + 0.009583 + 0.008291 + 0.0065 + 0.0065 + 0.007541 + 0.007625 + 0.008417 + 0.009042 + 0.007791 + 0.007583 + 0.007625 + 0.007625 + 0.00725 + 0.007041 + 0.007542 + 0.008084 + 0.011959 + 0.01025 + 0.013333 + 0.011375 + 0.00975 + 0.009834 + 0.010083 + 0.0095 + 0.010834 + 0.010167 + 0.00975 + 0.010208 + 0.008459 + 0.01075 + 0.009667 + 0.009 + 0.008083 + 0.007416 + 0.006584 + 0.006625 + 0.007042 + 0.006333 + 0.006083 + 0.006334 + 0.006041 + 0.006333 + 0.005584 + 0.007042 + 0.015375 + 0.008875 + 0.008792 + 0.009291 + 0.008167 + 0.006417 + 0.006583 + 0.010583 + 0.008875 + 0.008667 + 0.009209 + 0.008584 + 0.008959 + 0.008959 + 0.008667 + 0.00775 + 0.00725 + 0.007584 + 0.007667 + 0.007041 + 0.006917 + 0.006792 + 0.008166 + 0.007542 + 0.007125 + 0.008416 + 0.00775 + 0.009167 + 0.007459 + 0.007917 + 0.007833 + 0.00825 + 0.007667 + 0.007708 + 0.007 + 0.007959 + 0.008 + 0.006625 + 0.007459 + 0.005083 + 0.005334 + 0.00525 + 0.005541 + 0.006 + 0.005958 + 0.005417 + 0.005833 + 0.0055 + 0.005416 + 0.005083 + 0.00525 + 0.005458 + 0.0055 + 0.005584 + 0.005333 + 0.005416 + 0.005292 + 0.005417 + 0.005208 + 0.006125 + 0.005625 + 0.00575 + 0.005375 + 0.005625 + 0.005792 + 0.005333 + 0.006084 + 0.005834 + 0.005917 + 0.005791 + 0.005667 + 0.005542 + 0.006833 + 0.00875 + 0.008709 + 0.008667 + 0.008208 + 0.00825 + 0.007875 + 0.008292 + 0.008125 + 0.006625 + 0.007292 + 0.007709 + 0.007459 + 0.006875 + 0.005709 + 0.005584 + 0.0055 + 0.00625 + 0.006 + 0.005958 + 0.006208 + 0.005542 + 0.005458 + 0.0055 + 0.005416 + 0.005542 + 0.006125 + 0.008625 + 0.008625 + 0.010125 + 0.008208 + 0.007625 + 0.007709 + 0.007959 + 0.008584 + 0.007209 + 0.008375 + 0.008917 + 0.008125 + 0.007917 + 0.012208 + 0.008375 + 0.008875 + 0.007292 + 0.006083 + 0.005042 + 0.005375 + 0.005208 + 0.005208 + 0.005667 + 0.006042 + 0.005458 + 0.005833 + 0.005417 + 0.005208 + 0.006 + 0.005334 + 0.005334 + 0.006084 + 0.006792 + 0.006333 + 0.016958 + 0.008167 + 0.006209 + 0.006625 + 0.006583 + 0.006792 + 0.006 + 0.006417 + 0.007167 + 0.007083 + 0.006708 + 0.006416 + 0.0065 + 0.006458 + 0.006459 + 0.006542 + 0.005291 + 0.005458 + 0.004958 + 0.005167 + 0.004834 + 0.004875 + 0.004875 + 0.005541 + 0.00575 + 0.005125 + 0.005125 + 0.005041 + 0.004792 + 0.004666 + 0.006708 + 0.006083 + 0.005834 + 0.00525 + 0.005417 + 0.005291 + 0.005417 + 0.008834 + 0.00675 + 0.007542 + 0.010958 + 0.008708 + 0.008 + 0.008084 + 0.008125 + 0.007334 + 0.008209 + 0.006834 + 0.006333 + 0.0065 + 0.006375 + 0.005791 + 0.005209 + 0.005 + 0.005708 + 0.005875 + 0.005542 + 0.005542 + 0.005792 + 0.005584 + 0.005583 + 0.005708 + 0.005667 + 0.005416 + 0.005375 + 0.00575 + 0.007833 + 0.007542 + 0.006875 + 0.007291 + 0.007208 + 0.006125 + 0.006292 + 0.0075 + 0.009416 + 0.00775 + 0.006958 + 0.007833 + 0.007 + 0.006458 + 0.007833 + 0.00775 + 0.007125 + 0.006333 + 0.006333 + 0.00675 + 0.006625 + 0.00625 + 0.005959 + 0.007042 + 0.007708 + 0.006375 + 0.005875 + 0.006584 + 0.007208 + 0.006333 + 0.006333 + 0.005583 + 0.005167 + 0.004667 + 0.006791 + 0.005084 + 0.005125 + 0.004959 + 0.005584 + 0.006 + 0.006333 + 0.0055 + 0.005292 + 0.005417 + 0.005042 + 0.005167 + 0.005333 + 0.005417 + 0.005041 + 0.005375 + 0.005208 + 0.005958 + 0.006167 + 0.0065 + 0.006833 + 0.006833 + 0.006125 + 0.005583 + 0.005875 + 0.005291 + 0.005083 + 0.005625 + 0.00475 + 0.005208 + 0.00525 + 0.005583 + 0.005208 + 0.00525 + 0.0055 + 0.005666 + 0.006542 + 0.006875 + 0.006584 + 0.007292 + 0.007416 + 0.010292 + 0.008416 + 0.0075 + 0.006959 + 0.006584 + 0.006625 + 0.006083 + 0.006084 + 0.005375 + 0.005792 + 0.00575 + 0.005875 + 0.009875 + 0.006792 + 0.007167 + 0.005792], + :filter-tx-request-memo + [0.009625 + 0.005416 + 0.00475 + 0.006458 + 0.005459 + 0.0055 + 0.006792 + 0.005292 + 0.006375 + 0.004583 + 0.0045 + 0.006375 + 0.004625 + 0.004708 + 0.005625 + 0.00475 + 0.006166 + 0.004625 + 0.004625 + 0.006708 + 0.00525 + 0.004625 + 0.006 + 0.004458 + 0.005958 + 0.004792 + 0.004542 + 0.006584 + 0.00475 + 0.004541 + 0.008 + 0.006167 + 0.008667 + 0.006083 + 0.006167 + 0.006042 + 0.0045 + 0.005792 + 0.006375 + 0.005 + 0.012417 + 0.006333 + 0.006167 + 0.007209 + 0.005667 + 0.006084 + 0.013333 + 0.0085 + 0.007291 + 0.008666 + 0.00925 + 0.006917 + 0.007792 + 0.011667 + 0.0065 + 0.005458 + 0.00825 + 0.00625 + 0.00725 + 0.005833 + 0.005792 + 0.008458 + 0.005667 + 0.005334 + 0.006625 + 0.006417 + 0.00825 + 0.023625 + 0.008292 + 0.007708 + 0.008333 + 0.0065 + 0.00675 + 0.006167 + 0.006292 + 0.006375 + 0.005875 + 0.006875 + 0.009375 + 0.007666 + 0.009666 + 0.006666 + 0.1405 + 0.008709 + 0.007708 + 0.012041 + 0.008833 + 0.006291 + 0.008292 + 0.005834 + 0.008375 + 0.005125 + 0.005125 + 0.007667 + 0.004709 + 0.006042 + 0.009708 + 0.006875 + 0.00875 + 0.006833 + 0.006417 + 0.0065 + 0.006 + 0.00675 + 0.006417 + 0.006458 + 0.005792 + 0.005875 + 0.006208 + 0.005583 + 0.005417 + 0.005459 + 0.005125 + 0.005875 + 0.00575 + 0.006792 + 0.005375 + 0.006167 + 0.005667 + 0.005833 + 0.005833 + 0.006375 + 0.006167 + 0.006125 + 0.005959 + 0.006042 + 0.005666 + 0.006 + 0.005708 + 0.006125 + 0.005833 + 0.005416 + 0.005417 + 0.005292 + 0.005209 + 0.009625 + 0.005541 + 0.006875 + 0.006541 + 0.005958 + 0.00675 + 0.00575 + 0.006083 + 0.009416 + 0.006291 + 0.006292 + 0.008 + 0.010666 + 0.008292 + 0.007375 + 0.007584 + 0.007375 + 0.007208 + 0.008416 + 0.007042 + 0.00625 + 0.006292 + 0.006292 + 0.006083 + 0.006125 + 0.006417 + 0.00625 + 0.00625 + 0.00625 + 0.006042 + 0.006417 + 0.006375 + 0.006 + 0.006292 + 0.006209 + 0.006375 + 0.006334 + 0.007042 + 0.0085 + 0.008708 + 0.006958 + 0.007041 + 0.008208 + 0.006875 + 0.012125 + 0.008541 + 0.00625 + 0.007084 + 0.0065 + 0.006625 + 0.006417 + 0.008 + 0.008458 + 0.007708 + 0.007208 + 0.006709 + 0.006667 + 0.006583 + 0.005708 + 0.005625 + 0.005375 + 0.006042 + 0.00675 + 0.005 + 0.005083 + 0.006041 + 0.00575 + 0.009291 + 0.0055 + 0.005459 + 0.005625 + 0.005 + 0.005167 + 0.005125 + 0.005209 + 0.005417 + 0.005166 + 0.00525 + 0.005292 + 0.004916 + 0.004958 + 0.005166 + 0.005083 + 0.005083 + 0.005917 + 0.005542 + 0.00525 + 0.005292 + 0.00525 + 0.005333 + 0.005042 + 0.005375 + 0.005792 + 0.005584 + 0.005041 + 0.005583 + 0.005583 + 0.005541 + 0.005167 + 0.005125 + 0.005791 + 0.005125 + 0.005708 + 0.00575 + 0.005667 + 0.005083 + 0.005708 + 0.006042 + 0.00575 + 0.005375 + 0.005792 + 0.010959 + 0.008584 + 0.010083 + 0.00825 + 0.0085 + 0.007916 + 0.007917 + 0.0085 + 0.007625 + 0.007125 + 0.007042 + 0.007292 + 0.007333 + 0.007084 + 0.0055 + 0.005541 + 0.005458 + 0.004625 + 0.00525 + 0.00575 + 0.006 + 0.005208 + 0.005333 + 0.005667 + 0.005542 + 0.005291 + 0.005833 + 0.005583 + 0.011 + 0.009125 + 0.008125 + 0.008208 + 0.008041 + 0.007833 + 0.009667 + 0.00725 + 0.008458 + 0.008291 + 0.008291 + 0.008041 + 0.006583 + 0.00825 + 0.008125 + 0.007959 + 0.006542 + 0.005958 + 0.005792 + 0.005875 + 0.005417 + 0.005167 + 0.005459 + 0.005667 + 0.005834 + 0.004917 + 0.0055 + 0.00575 + 0.005625 + 0.005708 + 0.00575 + 0.005333 + 0.005 + 0.005458 + 0.005792 + 0.004583 + 0.005875 + 0.005334 + 0.005834 + 0.00525 + 0.004958 + 0.005917 + 0.005333 + 0.005334 + 0.005458 + 0.005291 + 0.005292 + 0.005375 + 0.006 + 0.0055 + 0.005167 + 0.013541 + 0.006042 + 0.005208 + 0.007583 + 0.004959 + 0.006084 + 0.0135 + 0.005541 + 0.013958 + 0.0055 + 0.00575 + 0.008625 + 0.006167 + 0.010959 + 0.006208 + 0.005833 + 0.00825 + 0.007125 + 0.007209 + 0.0095 + 0.006333 + 0.009 + 0.008167 + 0.0125 + 0.008458 + 0.010667 + 0.010875 + 0.006958 + 0.010625 + 0.010916 + 0.008125 + 0.007875 + 0.011542 + 0.006709 + 0.01425 + 0.0065 + 0.006792 + 0.008625 + 0.007291 + 0.010375 + 0.007 + 0.006083 + 0.0085 + 0.006375 + 0.005833 + 0.011541 + 0.007334 + 0.009833 + 0.006834 + 0.010291 + 0.006041 + 0.00825 + 0.021084 + 0.006625 + 0.010708 + 0.008334 + 0.010875 + 0.007459 + 0.012958 + 0.009125 + 0.00625 + 0.012791 + 0.010292 + 0.007708 + 0.010833 + 0.006792 + 0.008709 + 0.00525 + 0.007166 + 0.00525 + 0.005209 + 0.006834 + 0.005167 + 0.005917 + 0.007542 + 0.005541 + 0.008167 + 0.005667 + 0.00575 + 0.007375 + 0.005084 + 0.00475 + 0.007583 + 0.004834 + 0.007084 + 0.006208 + 0.006166 + 0.0095 + 0.005917 + 0.008917 + 0.005625 + 0.005792 + 0.007208 + 0.005375 + 0.005791 + 0.007583 + 0.005458 + 0.009 + 0.006375 + 0.005417 + 0.007416 + 0.005167 + 0.005792 + 0.01125 + 0.0055 + 0.007459 + 0.005667 + 0.005125 + 0.009875 + 0.00525 + 0.005584 + 0.010292 + 0.004875 + 0.009625 + 0.006667 + 0.0075 + 0.011167 + 0.007083 + 0.008542 + 0.006125 + 0.012333 + 0.007917 + 0.006458 + 0.009833 + 0.009208 + 0.007125 + 0.010416 + 0.010542 + 0.007041 + 0.009292 + 0.006583 + 0.008458 + 0.007084 + 0.006584 + 0.009334 + 0.006708 + 0.006542 + 0.008084 + 0.005708 + 0.008833 + 0.0065 + 0.006625 + 0.008375 + 0.007959 + 0.011333 + 0.007 + 0.0065 + 0.009583 + 0.006875 + 0.010084 + 0.008333 + 0.010583 + 0.010541 + 0.008541 + 0.011916 + 0.020375 + 0.007875 + 0.011917 + 0.008292 + 0.011167 + 0.01175 + 0.008291 + 0.011291 + 0.012166 + 0.022375 + 0.006875 + 0.006459 + 0.008459 + 0.010167 + 0.007 + 0.006791 + 0.00925 + 0.005042 + 0.008584 + 0.008209 + 0.006459 + 0.010209 + 0.006917 + 0.006291 + 0.008875 + 0.006084 + 0.012458 + 0.006583 + 0.00525 + 0.007167 + 0.005375 + 0.005 + 0.005041 + 0.005083 + 0.011625 + 0.005167 + 0.005625 + 0.007875 + 0.006791 + 0.011208 + 0.006917 + 0.005459 + 0.008084 + 0.006083 + 0.006959 + 0.009333 + 0.006458 + 0.010375 + 0.006625 + 0.005667 + 0.007416 + 0.005375 + 0.005625 + 0.00825 + 0.004917 + 0.018334 + 0.005709 + 0.00625 + 0.013625 + 0.007125 + 0.006291 + 0.006083 + 0.006208 + 0.007625 + 0.00625 + 0.007042 + 0.006625 + 0.006666 + 0.007583 + 0.006917 + 0.006875 + 0.0065 + 0.008166 + 0.007208 + 0.00725 + 0.006833 + 0.00675 + 0.00625 + 0.006084 + 0.005917 + 0.006 + 0.006417 + 0.006416 + 0.00625 + 0.006375 + 0.006292 + 0.006125 + 0.006584 + 0.007084 + 0.005917 + 0.005667 + 0.006125 + 0.00575 + 0.006625 + 0.006625 + 0.007 + 0.007125 + 0.007167 + 0.009209 + 0.008459 + 0.008333 + 0.007166 + 0.006292 + 0.007041 + 0.007292 + 0.012084 + 0.009083 + 0.008541 + 0.006916 + 0.006916 + 0.005791 + 0.005583 + 0.005667 + 0.006334 + 0.005583 + 0.005542 + 0.005959 + 0.006084 + 0.00625 + 0.005333 + 0.0055 + 0.006125 + 0.006042 + 0.00575 + 0.005708 + 0.005875 + 0.006084 + 0.006292 + 0.006125 + 0.005416 + 0.005458 + 0.006042 + 0.005375 + 0.005458 + 0.005417 + 0.005334 + 0.005583 + 0.005417 + 0.006125 + 0.005834 + 0.006167 + 0.005334 + 0.005875 + 0.00575 + 0.005833 + 0.005458 + 0.005375 + 0.005833 + 0.005625 + 0.005833 + 0.005792 + 0.006167 + 0.005792 + 0.00575 + 0.00575 + 0.006 + 0.005291 + 0.005667 + 0.00625 + 0.006125 + 0.006125 + 0.005791 + 0.006125 + 0.007125 + 0.006083 + 0.005791 + 0.005875 + 0.005917 + 0.0065 + 0.007208 + 0.007125 + 0.007458 + 0.007458 + 0.006917 + 0.00675 + 0.005958 + 0.00575 + 0.006041 + 0.006416 + 0.006167 + 0.005667 + 0.005792 + 0.006208 + 0.005833 + 0.005834 + 0.00575 + 0.005875 + 0.005667 + 0.007 + 0.005666 + 0.005667 + 0.005625 + 0.00625 + 0.006041 + 0.005583 + 0.007125 + 0.007334 + 0.006667 + 0.006917 + 0.00825 + 0.007125 + 0.007166 + 0.005583 + 0.006541 + 0.00675 + 0.006834 + 0.008125 + 0.006708 + 0.007125 + 0.006792 + 0.006584 + 0.005708 + 0.005291 + 0.005417 + 0.005542 + 0.005959 + 0.0055 + 0.005792 + 0.005916 + 0.005542 + 0.006375 + 0.005291 + 0.005792 + 0.005834 + 0.005417 + 0.005833 + 0.005583 + 0.005333 + 0.005666 + 0.005708 + 0.0055 + 0.005 + 0.005333 + 0.005458 + 0.005083 + 0.005542 + 0.005542 + 0.005542 + 0.0055 + 0.005209 + 0.005667 + 0.005583 + 0.005625 + 0.005625 + 0.005583 + 0.005334 + 0.005084 + 0.005584 + 0.00625 + 0.005583 + 0.005459 + 0.006166 + 0.007458 + 0.006667 + 0.005584 + 0.005959 + 0.005792 + 0.005459 + 0.007167 + 0.027125 + 0.016333 + 0.008208 + 0.006292 + 0.00875 + 0.007541 + 0.006791 + 0.00725 + 0.014167 + 0.009541 + 0.006208 + 0.008417 + 0.007958 + 0.010625 + 0.008834 + 0.0085 + 0.008375 + 0.007042 + 0.007042 + 0.006125 + 0.005375 + 0.005541 + 0.015291 + 0.005916 + 0.006292 + 0.0085 + 0.00725 + 0.00675 + 0.006166 + 0.005667 + 0.006042 + 0.005709 + 0.006375 + 0.005833 + 0.006083 + 0.005792 + 0.006958 + 0.005791 + 0.006458 + 0.008625 + 0.008208 + 0.007792 + 0.008125 + 0.007041 + 0.006417 + 0.007083 + 0.0065 + 0.008375 + 0.008542 + 0.007917 + 0.007917 + 0.0075 + 0.00625 + 0.0055 + 0.004792 + 0.005083 + 0.00525 + 0.004667 + 0.004916 + 0.006375 + 0.0055 + 0.005458 + 0.005709 + 0.005417 + 0.005875 + 0.005209 + 0.005375 + 0.005333 + 0.005083 + 0.005 + 0.005041 + 0.005042 + 0.005084 + 0.005 + 0.004709 + 0.004459 + 0.005 + 0.004834 + 0.004958 + 0.004875 + 0.005 + 0.005084 + 0.005416 + 0.005333 + 0.005292 + 0.004959 + 0.005 + 0.005208 + 0.005167 + 0.007209 + 0.004792 + 0.005042 + 0.005166 + 0.00525 + 0.004917 + 0.010084 + 0.007583 + 0.00725 + 0.00525 + 0.005708 + 0.006292 + 0.005958 + 0.005709 + 0.0055 + 0.005667 + 0.008875 + 0.006959 + 0.00725 + 0.006875 + 0.005791 + 0.0075 + 0.007042 + 0.006625 + 0.005959 + 0.007709 + 0.007792 + 0.007416 + 0.006417 + 0.006041 + 0.005667 + 0.005209 + 0.005542 + 0.005458 + 0.0055 + 0.0055 + 0.005541 + 0.005416 + 0.005084 + 0.005167 + 0.005833 + 0.006959 + 0.006042 + 0.006125 + 0.006541 + 0.006042 + 0.005334 + 0.005625 + 0.005834 + 0.006291 + 0.006167 + 0.008708 + 0.007459 + 0.007167 + 0.007 + 0.005416 + 0.006042 + 0.005916 + 0.008083 + 0.00775 + 0.007125 + 0.007334 + 0.00625 + 0.005042 + 0.004625 + 0.004625 + 0.004458 + 0.004833 + 0.00475 + 0.004583 + 0.004709 + 0.005875 + 0.005166 + 0.004917 + 0.00475 + 0.004917 + 0.005166 + 0.004958 + 0.004709 + 0.004959 + 0.005 + 0.005083 + 0.004917 + 0.00475 + 0.005208 + 0.005083 + 0.004792 + 0.004666 + 0.004584 + 0.004875 + 0.005042 + 0.004542 + 0.004875 + 0.004459 + 0.004584 + 0.004958 + 0.004958 + 0.005083 + 0.004875 + 0.004833 + 0.00475 + 0.005 + 0.004791 + 0.004917 + 0.005166 + 0.005333 + 0.005167 + 0.0055 + 0.005833 + 0.006167 + 0.005166 + 0.007083 + 0.007167 + 0.006792 + 0.006583 + 0.006666 + 0.008167 + 0.006417 + 0.006125 + 0.006083 + 0.006417 + 0.007125 + 0.006625 + 0.006125 + 0.005917 + 0.007917 + 0.007666 + 0.007417 + 0.006458 + 0.005708 + 0.005542 + 0.005209 + 0.005208 + 0.005084 + 0.005375 + 0.005291 + 0.005459 + 0.005541 + 0.005375 + 0.006083 + 0.006333 + 0.007084 + 0.009292 + 0.007458 + 0.005458 + 0.005542 + 0.005667 + 0.004625 + 0.006166 + 0.006042 + 0.006083 + 0.006375 + 0.008 + 0.008167 + 0.007792 + 0.006666 + 0.007458 + 0.008125 + 0.00675 + 0.008042 + 0.007583 + 0.008167 + 0.007583 + 0.007333 + 0.006333 + 0.006459 + 0.004834 + 0.00475 + 0.005042 + 0.004875 + 0.00475 + 0.005625 + 0.005083 + 0.005125 + 0.005208 + 0.005458 + 0.00525 + 0.004917 + 0.005125 + 0.005125 + 0.004833 + 0.005167 + 0.005041 + 0.004917 + 0.005042 + 0.005083 + 0.004917 + 0.005125 + 0.00525 + 0.004834 + 0.00475 + 0.004875 + 0.004833 + 0.005292 + 0.00475 + 0.004792 + 0.005083 + 0.004709 + 0.005167 + 0.005 + 0.005 + 0.004666 + 0.004834 + 0.005041 + 0.004792 + 0.004916 + 0.005459 + 0.004917 + 0.0055 + 0.006166 + 0.006292 + 0.006542 + 0.006958 + 0.006791 + 0.006708 + 0.006417 + 0.007042 + 0.006833 + 0.006333 + 0.006708 + 0.0065 + 0.007667 + 0.007583 + 0.006375 + 0.005916 + 0.008125 + 0.008916 + 0.007542 + 0.008334 + 0.00675 + 0.006917 + 0.005792 + 0.005417 + 0.005291 + 0.0055 + 0.005333 + 0.005208 + 0.006792 + 0.00725 + 0.006917 + 0.00675 + 0.006667 + 0.006667 + 0.006709 + 0.0055 + 0.005167 + 0.005333 + 0.005292 + 0.005667 + 0.006125 + 0.005875 + 0.007708 + 0.008166 + 0.008083 + 0.007458 + 0.007041 + 0.007625 + 0.00675 + 0.007584 + 0.008 + 0.007792 + 0.007916 + 0.007583 + 0.007833 + 0.00575 + 0.004875 + 0.004916 + 0.004833 + 0.004709 + 0.004875 + 0.005 + 0.005292 + 0.00475 + 0.00525 + 0.005083 + 0.005167 + 0.00525 + 0.004875 + 0.004792 + 0.004708 + 0.005125 + 0.004875 + 0.005 + 0.004458 + 0.005125 + 0.004917 + 0.004875 + 0.005125 + 0.005125 + 0.005084 + 0.006334 + 0.006292 + 0.009416 + 0.00625 + 0.006292 + 0.006417 + 0.005416 + 0.005042 + 0.004875 + 0.005292 + 0.004958 + 0.004833 + 0.005 + 0.005375 + 0.004833 + 0.004917 + 0.004958 + 0.005167 + 0.005333 + 0.005083 + 0.005083 + 0.005042 + 0.005625 + 0.007125 + 0.006958 + 0.007291 + 0.006542 + 0.006542 + 0.006875 + 0.00675 + 0.005584 + 0.005833 + 0.006042 + 0.006084 + 0.007708 + 0.007792 + 0.00775 + 0.007709 + 0.007584 + 0.007458 + 0.005875 + 0.005291 + 0.005417 + 0.005208 + 0.007417 + 0.007167 + 0.006875 + 0.007 + 0.016125 + 0.006709 + 0.005125 + 0.005542 + 0.005583 + 0.005083 + 0.006167 + 0.005542 + 0.006 + 0.005042 + 0.006333 + 0.006042 + 0.008125 + 0.007583 + 0.007542 + 0.008084 + 0.008041 + 0.008584 + 0.006292 + 0.00875 + 0.008334 + 0.008583 + 0.009125 + 0.008375 + 0.007792 + 0.008333 + 0.007 + 0.007708 + 0.00675 + 0.007083 + 0.0075 + 0.006584 + 0.005542 + 0.00525 + 0.00825 + 0.0135 + 0.008125 + 0.008 + 0.007875 + 0.007166 + 0.00725 + 0.007125 + 0.006084 + 0.0055 + 0.005333 + 0.004958 + 0.004958 + 0.00475 + 0.005875 + 0.005583 + 0.005916 + 0.00575 + 0.00575 + 0.005042 + 0.005166 + 0.005375 + 0.005625 + 0.005334 + 0.005417 + 0.005125 + 0.005583 + 0.005416 + 0.005292 + 0.005 + 0.005125 + 0.005333 + 0.0195 + 0.005291 + 0.006125 + 0.005959 + 0.006042 + 0.0055 + 0.0055 + 0.00575 + 0.005709 + 0.006375 + 0.005792 + 0.005917 + 0.005541 + 0.005625 + 0.005416 + 0.005708 + 0.005458 + 0.006166 + 0.006792 + 0.006042 + 0.007542 + 0.007584 + 0.007167 + 0.007333 + 0.007083 + 0.0065 + 0.005625 + 0.005375 + 0.005709 + 0.005375 + 0.0055 + 0.00925 + 0.0065 + 0.006208 + 0.006667 + 0.006209 + 0.0055 + 0.005042 + 0.005584 + 0.0055 + 0.005167 + 0.005208 + 0.0055 + 0.005459 + 0.005959 + 0.005792 + 0.007 + 0.007542 + 0.007333 + 0.007209 + 0.00675 + 0.005708 + 0.005458 + 0.007667 + 0.007208 + 0.006958 + 0.006958 + 0.007 + 0.006458 + 0.007542 + 0.006041 + 0.006 + 0.01525 + 0.005333 + 0.004708 + 0.006042 + 0.005917 + 0.006291 + 0.006208 + 0.005458 + 0.004958 + 0.00525 + 0.005542 + 0.005375 + 0.005542 + 0.005333 + 0.005459 + 0.006459 + 0.006625 + 0.006458 + 0.006792 + 0.00625 + 0.006083 + 0.006459 + 0.0055 + 0.004958 + 0.005292 + 0.005291 + 0.005625 + 0.004833 + 0.005125 + 0.005042 + 0.005875 + 0.006167 + 0.006208 + 0.006625 + 0.006292 + 0.006458 + 0.006625 + 0.006792 + 0.005958 + 0.005791 + 0.005625 + 0.005 + 0.004833 + 0.004958 + 0.00525 + 0.005167 + 0.006041 + 0.005333 + 0.005292 + 0.005625 + 0.004834 + 0.004959 + 0.004917 + 0.005458 + 0.005208 + 0.0055 + 0.006625 + 0.005834 + 0.009917 + 0.006958 + 0.007 + 0.007791 + 0.006292 + 0.005458 + 0.005541 + 0.005583 + 0.00525 + 0.005041 + 0.012 + 0.006791 + 0.006417 + 0.006625 + 0.006708 + 0.005792 + 0.005417 + 0.005291 + 0.0055 + 0.005375 + 0.005666 + 0.00525 + 0.005208 + 0.005208 + 0.004833 + 0.0065 + 0.006209 + 0.0105 + 0.007292 + 0.007208 + 0.007416 + 0.00625 + 0.005417 + 0.006125 + 0.007166 + 0.007 + 0.007083 + 0.007542 + 0.006584 + 0.005834 + 0.00775 + 0.006042 + 0.005875 + 0.005917 + 0.006 + 0.0055 + 0.004625 + 0.005166 + 0.005458 + 0.004917 + 0.004375 + 0.005042 + 0.005041 + 0.00525 + 0.00475 + 0.004875 + 0.005167 + 0.005084 + 0.004834 + 0.004917 + 0.006375 + 0.005167 + 0.004875 + 0.005333 + 0.005 + 0.005375 + 0.005291 + 0.005167 + 0.005666 + 0.004834 + 0.005042 + 0.005 + 0.005166 + 0.004792 + 0.004625 + 0.005125 + 0.004709 + 0.004666 + 0.004708 + 0.004875 + 0.0055 + 0.00475 + 0.005 + 0.005583 + 0.004875 + 0.005125 + 0.004875 + 0.00525 + 0.005791 + 0.005625 + 0.005458 + 0.005291 + 0.005292 + 0.005125 + 0.005291 + 0.006 + 0.00575 + 0.005125 + 0.006083 + 0.006458 + 0.007625 + 0.007791 + 0.006958 + 0.008458 + 0.007458 + 0.007167 + 0.006209 + 0.005666 + 0.007 + 0.0065 + 0.006292 + 0.006375 + 0.006209 + 0.005542 + 0.005833 + 0.005333 + 0.005792 + 0.005375 + 0.005292 + 0.005209 + 0.004917 + 0.004917 + 0.005708 + 0.005708 + 0.005 + 0.01375 + 0.008667 + 0.007958 + 0.007667 + 0.007791 + 0.007125 + 0.008 + 0.006792 + 0.009584 + 0.00675 + 0.005917 + 0.005417 + 0.006458 + 0.005792 + 0.00725 + 0.0075 + 0.00625 + 0.005708 + 0.005792 + 0.005042 + 0.005083 + 0.004834 + 0.005083 + 0.005125 + 0.005083 + 0.005125 + 0.004709 + 0.005375 + 0.00475 + 0.004625 + 0.013875 + 0.004958 + 0.00475 + 0.005208 + 0.004958 + 0.005 + 0.004667 + 0.004875 + 0.004792 + 0.005041 + 0.004833 + 0.00525 + 0.005 + 0.005 + 0.004541 + 0.005083 + 0.005209 + 0.005125 + 0.004959 + 0.004917 + 0.004791 + 0.004459 + 0.0045 + 0.004709 + 0.004667 + 0.004583 + 0.004917 + 0.005209 + 0.004959 + 0.004709 + 0.004834 + 0.004459 + 0.005084 + 0.004833 + 0.00575 + 0.005375 + 0.0055 + 0.004917 + 0.004959 + 0.00525 + 0.005125 + 0.00475 + 0.0055 + 0.006041 + 0.010709 + 0.007042 + 0.007125 + 0.007084 + 0.006375 + 0.005667 + 0.00725 + 0.006875 + 0.005792 + 0.006042 + 0.005791 + 0.005458 + 0.004833 + 0.005 + 0.00525 + 0.005709 + 0.005042 + 0.005417 + 0.006208 + 0.006291 + 0.006 + 0.006166 + 0.005667 + 0.005375 + 0.005 + 0.010875 + 0.008166 + 0.007167 + 0.006625 + 0.006917 + 0.006708 + 0.0055 + 0.007584 + 0.006834 + 0.00625 + 0.006708 + 0.006584 + 0.00575 + 0.005792 + 0.007208 + 0.007542 + 0.006834 + 0.006167 + 0.005458 + 0.004958 + 0.005 + 0.005083 + 0.00475 + 0.004834 + 0.005292 + 0.004375 + 0.00525 + 0.005084 + 0.00525 + 0.004875 + 0.004666 + 0.004792 + 0.005 + 0.004625 + 0.004709 + 0.005042 + 0.005125 + 0.004791 + 0.004792 + 0.004708 + 0.004708 + 0.004833 + 0.004875 + 0.00475 + 0.004958 + 0.004542 + 0.004542 + 0.004917 + 0.004875 + 0.004875 + 0.004666 + 0.004292 + 0.0045 + 0.004417 + 0.0045 + 0.004375 + 0.004834 + 0.004584 + 0.004667 + 0.005083 + 0.004958 + 0.005084 + 0.00475 + 0.005 + 0.005083 + 0.004958 + 0.005292 + 0.005667 + 0.004834 + 0.005375 + 0.004917 + 0.005292 + 0.004959 + 0.005292 + 0.00525 + 0.006041 + 0.010125 + 0.00725 + 0.006667 + 0.007166 + 0.006417 + 0.005875 + 0.005584 + 0.005292 + 0.005208 + 0.005917 + 0.006083 + 0.006125 + 0.006 + 0.006166 + 0.0055 + 0.005333 + 0.00525 + 0.005208 + 0.005583 + 0.004834 + 0.005166 + 0.005042 + 0.005584 + 0.005334 + 0.005459 + 0.006042 + 0.007375 + 0.007084 + 0.007334 + 0.007125 + 0.006584 + 0.005834 + 0.007208 + 0.006333 + 0.006792 + 0.006834 + 0.006541 + 0.005834 + 0.005709 + 0.007084 + 0.007042 + 0.006416 + 0.0055 + 0.005666 + 0.00525 + 0.004959 + 0.00475 + 0.004792 + 0.006792 + 0.005833 + 0.0045 + 0.006208 + 0.004875 + 0.005084 + 0.004917 + 0.004625 + 0.004542 + 0.005 + 0.004791 + 0.0045 + 0.00475 + 0.004833 + 0.004875 + 0.004458 + 0.004917 + 0.004916 + 0.004917 + 0.005042 + 0.004958 + 0.005042 + 0.004916 + 0.005209 + 0.004917 + 0.004708 + 0.005042 + 0.004666 + 0.005042 + 0.00475 + 0.005 + 0.009125 + 0.005333 + 0.005458 + 0.005833 + 0.006542 + 0.006292 + 0.006583 + 0.005333 + 0.00525 + 0.005709 + 0.00675 + 0.006917 + 0.007375 + 0.006792 + 0.007583 + 0.006791 + 0.007083 + 0.00625 + 0.006208 + 0.005917 + 0.007834 + 0.008125 + 0.007292 + 0.007125 + 0.006584 + 0.006584 + 0.006875 + 0.006042 + 0.005792 + 0.006209 + 0.008125 + 0.019708 + 0.011583 + 0.005958 + 0.006541 + 0.007583 + 0.007708 + 0.006917 + 0.006083 + 0.006416 + 0.005792 + 0.006125 + 0.005583 + 0.005583 + 0.005292 + 0.005709 + 0.00525 + 0.006792 + 0.010583 + 0.008375 + 0.007834 + 0.007792 + 0.008083 + 0.0075 + 0.006042 + 0.0075 + 0.007792 + 0.007334 + 0.006959 + 0.006875 + 0.006375 + 0.007334 + 0.006625 + 0.00625 + 0.00625 + 0.005875 + 0.005916 + 0.005792 + 0.0065 + 0.006292 + 0.0075 + 0.006542 + 0.007416 + 0.006625 + 0.006833 + 0.005708 + 0.005041 + 0.00525 + 0.006208 + 0.005875 + 0.006042 + 0.006958 + 0.006625 + 0.00625 + 0.005917 + 0.005833 + 0.005541 + 0.006458 + 0.006208 + 0.007292 + 0.011375 + 0.005292 + 0.005417 + 0.005208 + 0.008334 + 0.0065 + 0.008916 + 0.006375 + 0.005916 + 0.006375 + 0.005917 + 0.005791 + 0.006125 + 0.005833 + 0.00625 + 0.006667 + 0.006333 + 0.006125 + 0.006083 + 0.00575 + 0.006125 + 0.00575 + 0.005625 + 0.006875 + 0.008375 + 0.015167 + 0.008208 + 0.007417 + 0.009583 + 0.008209 + 0.009625 + 0.01025 + 0.009083 + 0.007541 + 0.007875 + 0.007041 + 0.006708 + 0.008792 + 0.008709 + 0.007334 + 0.006542 + 0.006875 + 0.005917 + 0.006792 + 0.005791 + 0.0065 + 0.005375 + 0.006125 + 0.005458 + 0.005375 + 0.00525 + 0.011708 + 0.005792 + 0.005584 + 0.005084 + 0.005875 + 0.00625 + 0.009041 + 0.008208 + 0.00775 + 0.00775 + 0.007709 + 0.008167 + 0.009041 + 0.006417 + 0.006917 + 0.00675 + 0.006916 + 0.006041 + 0.006 + 0.008083 + 0.007041 + 0.007833 + 0.005584 + 0.005459 + 0.005084 + 0.005291 + 0.004875 + 0.005083 + 0.004792 + 0.005834 + 0.004666 + 0.004833 + 0.005292 + 0.004916 + 0.005417 + 0.005167 + 0.004666 + 0.005125 + 0.005083 + 0.005 + 0.004792 + 0.005333 + 0.0045 + 0.005 + 0.004709 + 0.004959 + 0.004917 + 0.005 + 0.005166 + 0.004666 + 0.004667 + 0.004667 + 0.005166 + 0.005084 + 0.005209 + 0.004833 + 0.005125 + 0.004958 + 0.004958 + 0.005 + 0.004916 + 0.004875 + 0.005167 + 0.004584 + 0.004834 + 0.004583 + 0.004958 + 0.004584 + 0.004875 + 0.005083 + 0.005167 + 0.005042 + 0.005167 + 0.00525 + 0.005083 + 0.005416 + 0.005084 + 0.005 + 0.00525 + 0.007583 + 0.007708 + 0.007208 + 0.006958 + 0.006375 + 0.005916 + 0.0075 + 0.007333 + 0.006209 + 0.005625 + 0.006208 + 0.005292 + 0.005333 + 0.005 + 0.004875 + 0.004584 + 0.005375 + 0.005709 + 0.005084 + 0.005417 + 0.005083 + 0.005375 + 0.004917 + 0.007208 + 0.006125 + 0.006209 + 0.006375 + 0.007 + 0.005958 + 0.006375 + 0.005834 + 0.00725 + 0.007042 + 0.00725 + 0.006875 + 0.005709 + 0.006 + 0.005875 + 0.007167 + 0.007459 + 0.006959 + 0.006959 + 0.006875 + 0.007167 + 0.005167 + 0.004917 + 0.005125 + 0.004917 + 0.004833 + 0.010125 + 0.004875 + 0.004208 + 0.004583 + 0.004625 + 0.00425 + 0.004792 + 0.004084 + 0.003958 + 0.004333 + 0.004084 + 0.00425 + 0.004083 + 0.003792 + 0.004125 + 0.004791 + 0.004625 + 0.004709 + 0.004 + 0.003792 + 0.004208 + 0.003917 + 0.004042 + 0.004083 + 0.003958 + 0.004334 + 0.003958 + 0.003917 + 0.004375 + 0.004 + 0.003958 + 0.004084 + 0.003958 + 0.003875 + 0.004125 + 0.003917 + 0.004541 + 0.004333 + 0.004167 + 0.004208 + 0.004417 + 0.004 + 0.0045 + 0.005125 + 0.005917 + 0.0065 + 0.005917 + 0.00575 + 0.005292 + 0.004584 + 0.004667 + 0.004792 + 0.005917 + 0.005708 + 0.006208 + 0.007542 + 0.006709 + 0.005416 + 0.005333 + 0.00425 + 0.004583 + 0.004583 + 0.00425 + 0.004375 + 0.004333 + 0.004417 + 0.004542 + 0.004583 + 0.004958 + 0.005792 + 0.005666 + 0.005417 + 0.006291 + 0.005792 + 0.0055 + 0.004583 + 0.0045 + 0.004375 + 0.006542 + 0.005334 + 0.005167 + 0.006583 + 0.006666 + 0.006125 + 0.006458 + 0.005917 + 0.004459 + 0.005584 + 0.006334 + 0.006542 + 0.006375 + 0.005834 + 0.005959 + 0.005084 + 0.005 + 0.004584 + 0.004667 + 0.005125 + 0.004583 + 0.004958 + 0.004917 + 0.016042 + 0.006208 + 0.005042 + 0.004583 + 0.005375 + 0.006417 + 0.005875 + 0.005459 + 0.005292 + 0.005916 + 0.006041 + 0.006375 + 0.006458 + 0.00625 + 0.006125 + 0.006292 + 0.006334 + 0.005209 + 0.004542 + 0.00525 + 0.005416 + 0.005 + 0.004875 + 0.004875 + 0.005125 + 0.004791 + 0.00475 + 0.0045 + 0.005209 + 0.004625 + 0.004834 + 0.004791 + 0.004625 + 0.004833 + 0.00475 + 0.00525 + 0.004958 + 0.004875 + 0.004708 + 0.004875 + 0.004375 + 0.005042 + 0.005459 + 0.007459 + 0.006042 + 0.006208 + 0.006583 + 0.00675 + 0.006834 + 0.006625 + 0.006917 + 0.00875 + 0.007625 + 0.006583 + 0.008375 + 0.008167 + 0.008166 + 0.007625 + 0.007209 + 0.006875 + 0.006292 + 0.005042 + 0.005375 + 0.00575 + 0.005166 + 0.005125 + 0.005375 + 0.005959 + 0.006334 + 0.006667 + 0.006583 + 0.00675 + 0.006708 + 0.007083 + 0.006208 + 0.005375 + 0.005458 + 0.005125 + 0.006167 + 0.006334 + 0.006084 + 0.008167 + 0.007958 + 0.007333 + 0.007583 + 0.00675 + 0.00775 + 0.007 + 0.008083 + 0.008209 + 0.007834 + 0.007917 + 0.007584 + 0.007792 + 0.005708 + 0.004833 + 0.00525 + 0.005291 + 0.005125 + 0.005292 + 0.005208 + 0.005958 + 0.005042 + 0.004625 + 0.004958 + 0.004959 + 0.005542 + 0.005208 + 0.004916 + 0.005042 + 0.005042 + 0.005083 + 0.004667 + 0.005208 + 0.005958 + 0.007 + 0.006209 + 0.006 + 0.005458 + 0.005209 + 0.005209 + 0.005709 + 0.004959 + 0.005042 + 0.004541 + 0.004917 + 0.004666 + 0.004958 + 0.004708 + 0.004917 + 0.004667 + 0.004917 + 0.004791 + 0.005209 + 0.004834 + 0.005209 + 0.004709 + 0.00525 + 0.004958 + 0.006083 + 0.006042 + 0.006209 + 0.006917 + 0.007708 + 0.00675 + 0.00625 + 0.005333 + 0.004916 + 0.005542 + 0.004792 + 0.0055 + 0.00575 + 0.006584 + 0.006083 + 0.007958 + 0.008208 + 0.008125 + 0.008208 + 0.007833 + 0.006833 + 0.00625 + 0.005 + 0.005459 + 0.004959 + 0.006666 + 0.006875 + 0.006958 + 0.006791 + 0.006667 + 0.006792 + 0.006541 + 0.006083 + 0.005208 + 0.005042 + 0.005166 + 0.005625 + 0.005917 + 0.005 + 0.005958 + 0.006292 + 0.008625 + 0.008208 + 0.007333 + 0.008 + 0.008041 + 0.006709 + 0.00625 + 0.007584 + 0.007958 + 0.00775 + 0.007875 + 0.007584 + 0.007584 + 0.008209 + 0.006375 + 0.005958 + 0.005917 + 0.0065 + 0.006292 + 0.006375 + 0.005875 + 0.005041 + 0.005625 + 0.004917 + 0.004959 + 0.005292 + 0.004833 + 0.005208 + 0.00525 + 0.005041 + 0.004834 + 0.005291 + 0.005125 + 0.004791 + 0.005167 + 0.005125 + 0.004958 + 0.004625 + 0.005709 + 0.0065 + 0.006291 + 0.006042 + 0.005459 + 0.005708 + 0.005458 + 0.005708 + 0.005375 + 0.005459 + 0.005417 + 0.005875 + 0.00675 + 0.006958 + 0.007208 + 0.00725 + 0.007042 + 0.007041 + 0.005917 + 0.00575 + 0.005625 + 0.00575 + 0.005375 + 0.005833 + 0.005792 + 0.006041 + 0.005625 + 0.0065 + 0.005667 + 0.006125 + 0.005583 + 0.005667 + 0.006042 + 0.006416 + 0.006625 + 0.008084 + 0.006917 + 0.006166 + 0.006375 + 0.0065 + 0.006291 + 0.005958 + 0.004958 + 0.005167 + 0.007 + 0.006875 + 0.006833 + 0.006292 + 0.00675 + 0.0085 + 0.007167 + 0.005459 + 0.005542 + 0.00525 + 0.005167 + 0.005 + 0.004875 + 0.004792 + 0.004791 + 0.005583 + 0.005875 + 0.007791 + 0.007916 + 0.008292 + 0.007709 + 0.007834 + 0.007833 + 0.009208 + 0.007125 + 0.007833 + 0.008084 + 0.007792 + 0.007708 + 0.007 + 0.011166 + 0.008375 + 0.00625 + 0.006208 + 0.006 + 0.006167 + 0.006083 + 0.005666 + 0.004875 + 0.004708 + 0.005375 + 0.0055 + 0.004791 + 0.00475 + 0.005291 + 0.004958 + 0.005334 + 0.004542 + 0.005041 + 0.004667 + 0.004709 + 0.005042 + 0.005375 + 0.004666 + 0.005125 + 0.004959 + 0.004667 + 0.004916 + 0.004875 + 0.004583 + 0.005208 + 0.004958 + 0.004917 + 0.004666 + 0.004875 + 0.004833 + 0.004958 + 0.004875 + 0.004834 + 0.004625 + 0.004792 + 0.004916 + 0.004917 + 0.004917 + 0.005 + 0.005 + 0.005125 + 0.005167 + 0.004875 + 0.004875 + 0.005625 + 0.0055 + 0.005583 + 0.00475 + 0.005 + 0.004917 + 0.004959 + 0.005167 + 0.005042 + 0.005542 + 0.006291 + 0.01075 + 0.008416 + 0.007542 + 0.007709 + 0.007584 + 0.007709 + 0.005875 + 0.005208 + 0.00575 + 0.007 + 0.007042 + 0.006625 + 0.00675 + 0.006833 + 0.007292 + 0.006834 + 0.005959 + 0.0055 + 0.0055 + 0.012041 + 0.005709 + 0.00475 + 0.005125 + 0.005375 + 0.005625 + 0.006667 + 0.009084 + 0.008667 + 0.008125 + 0.0075 + 0.007875 + 0.008125 + 0.009625 + 0.007417 + 0.008667 + 0.01625 + 0.007125 + 0.007417 + 0.009625 + 0.030584 + 0.010208 + 0.005583 + 0.005625 + 0.006541 + 0.005792 + 0.005625 + 0.005917 + 0.006042 + 0.007125 + 0.006541 + 0.006208 + 0.004958 + 0.006167 + 0.005875 + 0.005875 + 0.006 + 0.005542 + 0.005667 + 0.005375 + 0.006375 + 0.005708 + 0.006167 + 0.005791 + 0.006292 + 0.014 + 0.00575 + 0.006541 + 0.006625 + 0.007166 + 0.007 + 0.005958 + 0.005875 + 0.005416 + 0.005458 + 0.006 + 0.005583 + 0.005625 + 0.006584 + 0.006042 + 0.005792 + 0.005417 + 0.005666 + 0.00625 + 0.0145 + 0.005666 + 0.005375 + 0.005917 + 0.005291 + 0.005458 + 0.005667 + 0.006041 + 0.00575 + 0.005458 + 0.006042 + 0.005833 + 0.005417 + 0.006 + 0.006 + 0.005958 + 0.006916 + 0.011458 + 0.008333 + 0.007833 + 0.008042 + 0.007834 + 0.007959 + 0.006125 + 0.005875 + 0.005833 + 0.005375 + 0.007583 + 0.00725 + 0.006709 + 0.006708 + 0.00675 + 0.006834 + 0.007084 + 0.005917 + 0.006041 + 0.005417 + 0.005208 + 0.005541 + 0.005458 + 0.005292 + 0.005583 + 0.005125 + 0.014542 + 0.008916 + 0.008292 + 0.007958 + 0.007625 + 0.006834 + 0.007167 + 0.006041 + 0.007083 + 0.008167 + 0.007209 + 0.007292 + 0.006792 + 0.007209 + 0.007166 + 0.007542 + 0.005917 + 0.005833 + 0.005625 + 0.005584 + 0.005584 + 0.00575 + 0.005833 + 0.00625 + 0.006 + 0.00625 + 0.005667 + 0.005833 + 0.005375 + 0.005167 + 0.005333 + 0.005958 + 0.005958 + 0.006041 + 0.005708 + 0.006417 + 0.007083 + 0.007 + 0.006542 + 0.006 + 0.00575 + 0.005334 + 0.005584 + 0.006 + 0.014292 + 0.005375 + 0.004792 + 0.005416 + 0.00525 + 0.005416 + 0.004958 + 0.005125 + 0.004833 + 0.005041 + 0.005 + 0.005208 + 0.0055 + 0.005125 + 0.004834 + 0.004917 + 0.004875 + 0.005125 + 0.005167 + 0.005667 + 0.005958 + 0.011125 + 0.005666 + 0.005125 + 0.005292 + 0.005292 + 0.005208 + 0.005458 + 0.005375 + 0.006583 + 0.005958 + 0.008292 + 0.007417 + 0.007125 + 0.007375 + 0.006792 + 0.008041 + 0.00575 + 0.005166 + 0.007583 + 0.006292 + 0.006334 + 0.006292 + 0.006375 + 0.005375 + 0.005584 + 0.005416 + 0.005292 + 0.005375 + 0.005375 + 0.005208 + 0.00575 + 0.005625 + 0.005375 + 0.006541 + 0.006125 + 0.006875 + 0.007167 + 0.0065 + 0.006375 + 0.006375 + 0.006042 + 0.006167 + 0.005541 + 0.00475 + 0.007542 + 0.007709 + 0.006959 + 0.007334 + 0.007042 + 0.006 + 0.0075 + 0.006125 + 0.006083 + 0.005958 + 0.006084 + 0.006208 + 0.005417 + 0.004667 + 0.005041 + 0.005333 + 0.005208 + 0.004958 + 0.005125 + 0.00525 + 0.005125 + 0.005375 + 0.005541 + 0.005 + 0.005458 + 0.005083 + 0.004792 + 0.005417 + 0.005541 + 0.005208 + 0.005292 + 0.005333 + 0.005333 + 0.00475 + 0.004916 + 0.005417 + 0.004292 + 0.00525 + 0.004834 + 0.005041 + 0.005 + 0.004792 + 0.00625 + 0.006167 + 0.005875 + 0.006167 + 0.005666 + 0.005042 + 0.00475 + 0.005333 + 0.005083 + 0.005334 + 0.005 + 0.005292 + 0.005 + 0.0055 + 0.005792 + 0.005875 + 0.005209 + 0.005416 + 0.005375 + 0.005417 + 0.005458 + 0.005541 + 0.00425 + 0.006583 + 0.006958 + 0.011792 + 0.007458 + 0.008 + 0.007375 + 0.006959 + 0.005709 + 0.005583 + 0.006 + 0.005958 + 0.005375 + 0.006208 + 0.006583 + 0.0065 + 0.006208 + 0.006708 + 0.006 + 0.011125 + 0.005208 + 0.00575 + 0.005292 + 0.005125 + 0.005125 + 0.006083 + 0.00675 + 0.0075 + 0.00775 + 0.009291 + 0.007417 + 0.006792 + 0.006875 + 0.006959 + 0.006 + 0.005417 + 0.007333 + 0.007541 + 0.007042 + 0.007 + 0.00625 + 0.006583 + 0.007666 + 0.006 + 0.005958 + 0.005458 + 0.004667 + 0.005208 + 0.005125 + 0.00475 + 0.015125 + 0.006833 + 0.007375 + 0.006375 + 0.006375 + 0.006875 + 0.006417 + 0.006292 + 0.005958 + 0.006 + 0.006416 + 0.006542 + 0.006208 + 0.005958 + 0.006167 + 0.005833 + 0.00575 + 0.006083 + 0.006292 + 0.00625 + 0.005916 + 0.006209 + 0.007459 + 0.006959 + 0.00675 + 0.005958 + 0.005292 + 0.00525 + 0.005541 + 0.005458 + 0.005541 + 0.005417 + 0.005667 + 0.005791 + 0.006 + 0.005625 + 0.005625 + 0.005542 + 0.005291 + 0.005209 + 0.004917 + 0.005667 + 0.00575 + 0.005625 + 0.005291 + 0.005292 + 0.0055 + 0.005209 + 0.00525 + 0.005458 + 0.0055 + 0.0065 + 0.006208 + 0.010708 + 0.007958 + 0.007458 + 0.007833 + 0.008542 + 0.006917 + 0.006 + 0.005875 + 0.0055 + 0.005042 + 0.005042 + 0.006875 + 0.007209 + 0.006834 + 0.007417 + 0.006833 + 0.007125 + 0.006959 + 0.005792 + 0.0055 + 0.005292 + 0.00525 + 0.005417 + 0.005459 + 0.005584 + 0.006375 + 0.007917 + 0.007875 + 0.007625 + 0.007917 + 0.007958 + 0.007708 + 0.005666 + 0.007125 + 0.008083 + 0.008125 + 0.007666 + 0.007583 + 0.007917 + 0.008209 + 0.007292 + 0.006333 + 0.006833 + 0.0065 + 0.0065 + 0.006083 + 0.005625 + 0.005291 + 0.005667 + 0.00525 + 0.005791 + 0.005417 + 0.005 + 0.005167 + 0.004834 + 0.004875 + 0.004792 + 0.005 + 0.004958 + 0.004875 + 0.005042 + 0.004834 + 0.004958 + 0.005 + 0.0055 + 0.004625 + 0.005041 + 0.005167 + 0.005083 + 0.004791 + 0.005125 + 0.004958 + 0.00475 + 0.004708 + 0.0045 + 0.00525 + 0.004833 + 0.004916 + 0.005125 + 0.004958 + 0.004708 + 0.004792 + 0.005334 + 0.005209 + 0.005125 + 0.005042 + 0.004791 + 0.00475 + 0.0055 + 0.005459 + 0.005666 + 0.005084 + 0.005125 + 0.004917 + 0.005083 + 0.005541 + 0.005167 + 0.005041 + 0.005958 + 0.010792 + 0.008291 + 0.008083 + 0.007583 + 0.007916 + 0.008083 + 0.008333 + 0.007125 + 0.007084 + 0.00675 + 0.007125 + 0.006625 + 0.00675 + 0.006459 + 0.006542 + 0.006458 + 0.006708 + 0.00675 + 0.006416 + 0.006958 + 0.005917 + 0.005292 + 0.005125 + 0.005333 + 0.005042 + 0.005167 + 0.008333 + 0.007791 + 0.007542 + 0.008084 + 0.007334 + 0.007334 + 0.008625 + 0.007041 + 0.007458 + 0.007833 + 0.007541 + 0.007875 + 0.006917 + 0.007792 + 0.008042 + 0.00775 + 0.00675 + 0.006208 + 0.006333 + 0.006292 + 0.00525 + 0.005417 + 0.005083 + 0.005708 + 0.004833 + 0.004958 + 0.005167 + 0.005541 + 0.005834 + 0.006792 + 0.006666 + 0.006583 + 0.005958 + 0.006292 + 0.005 + 0.004875 + 0.005625 + 0.005209 + 0.005125 + 0.004875 + 0.004958 + 0.006166 + 0.00625 + 0.006333 + 0.0065 + 0.00625 + 0.006416 + 0.006583 + 0.006417 + 0.006042 + 0.006416 + 0.006458 + 0.006208 + 0.006333 + 0.006042 + 0.006209 + 0.006 + 0.006458 + 0.006917 + 0.006375 + 0.006292 + 0.006125 + 0.00625 + 0.005916 + 0.005583 + 0.005875 + 0.004875 + 0.005291 + 0.005167 + 0.0065 + 0.006625 + 0.00725 + 0.006625 + 0.008375 + 0.007708 + 0.006667 + 0.006375 + 0.006208 + 0.007125 + 0.007791 + 0.00725 + 0.005916 + 0.005833 + 0.005917 + 0.005917 + 0.005958 + 0.005833 + 0.005958 + 0.005708 + 0.006292 + 0.00675 + 0.00625 + 0.006625 + 0.007875 + 0.006917 + 0.006458 + 0.008333 + 0.007333 + 0.007166 + 0.006708 + 0.007208 + 0.007458 + 0.007625 + 0.007916 + 0.007833 + 0.056333 + 0.013625 + 0.007084 + 0.006375 + 0.007208 + 0.006 + 0.006042 + 0.008958 + 0.006833 + 0.006625 + 0.00575 + 0.006083 + 0.005167 + 0.005125 + 0.006167 + 0.004791 + 0.005292 + 0.005333 + 0.005333 + 0.005458 + 0.005042 + 0.004917 + 0.004541 + 0.006708 + 0.005125 + 0.005125 + 0.005125 + 0.004959 + 0.005083 + 0.005333 + 0.004875 + 0.005042 + 0.005209 + 0.004834 + 0.005709 + 0.005 + 0.005041 + 0.005292 + 0.005416 + 0.004834 + 0.00525 + 0.005125 + 0.004916 + 0.00475 + 0.004708 + 0.004542 + 0.004625 + 0.004333 + 0.004667 + 0.005167 + 0.005541 + 0.005 + 0.004875 + 0.00475 + 0.004917 + 0.005083 + 0.00475 + 0.005208 + 0.004875 + 0.006375 + 0.005041 + 0.005083 + 0.005584 + 0.005209 + 0.005792 + 0.005458 + 0.005542 + 0.005291 + 0.005708 + 0.009833 + 0.00675 + 0.006791 + 0.006292 + 0.006167 + 0.006709 + 0.00875 + 0.006584 + 0.0065 + 0.006292 + 0.005416 + 0.00525 + 0.005833 + 0.005792 + 0.005209 + 0.006334 + 0.008291 + 0.005958 + 0.006459 + 0.005416 + 0.005958 + 0.005625 + 0.005875 + 0.005292 + 0.005125 + 0.006208 + 0.00875 + 0.006459 + 0.006583 + 0.006 + 0.006166 + 0.006667 + 0.005875 + 0.009125 + 0.0055 + 0.006209 + 0.0055 + 0.005417 + 0.005625 + 0.006083 + 0.008375 + 0.006208 + 0.00525 + 0.00475 + 0.004958 + 0.00525 + 0.005209 + 0.004833 + 0.004958 + 0.004541 + 0.004875 + 0.005 + 0.004542 + 0.004833 + 0.005 + 0.004875 + 0.004833 + 0.00475 + 0.005083 + 0.005 + 0.005042 + 0.005042 + 0.004875 + 0.004875 + 0.004667 + 0.004667 + 0.004833 + 0.004875 + 0.00475 + 0.004875 + 0.004875 + 0.004709 + 0.004916 + 0.005459 + 0.005 + 0.004584 + 0.006792 + 0.005916 + 0.005583 + 0.013667 + 0.00625 + 0.007375 + 0.007584 + 0.006875 + 0.007625 + 0.008333 + 0.007375 + 0.016166 + 0.006792 + 0.006416 + 0.013667 + 0.008917 + 0.007125 + 0.006083 + 0.006584 + 0.006292 + 0.012125 + 0.010333 + 0.007041 + 0.006583 + 0.008417 + 0.007959 + 0.010125 + 0.014333 + 0.007291 + 0.008167 + 0.008083 + 0.00625 + 0.010167 + 0.007125 + 0.006167 + 0.006792 + 0.006833 + 0.010458 + 0.006041 + 0.00625 + 0.00675 + 0.005333 + 0.007208 + 0.014042 + 0.006166 + 0.005708 + 0.005667 + 0.005917 + 0.00525 + 0.005666 + 0.005042 + 0.006791 + 0.007625 + 0.009125 + 0.006 + 0.006542 + 0.005833 + 0.006 + 0.005541 + 0.006667 + 0.005916 + 0.009667 + 0.006333 + 0.006584 + 0.006167 + 0.006166 + 0.005792 + 0.005292 + 0.00525 + 0.005084 + 0.005416 + 0.004959 + 0.004959 + 0.004916 + 0.005959 + 0.005042 + 0.005333 + 0.006291 + 0.006333 + 0.005833 + 0.004542 + 0.005 + 0.005375 + 0.005541 + 0.005334 + 0.005584 + 0.005834 + 0.005709 + 0.005292 + 0.005416 + 0.004958 + 0.004875 + 0.005 + 0.005083 + 0.006667 + 0.005708 + 0.00575 + 0.013083 + 0.005875 + 0.005875 + 0.005917 + 0.006875 + 0.006458 + 0.007917 + 0.006333 + 0.005583 + 0.005958 + 0.0055 + 0.00725 + 0.005875 + 0.005583 + 0.004875 + 0.004959 + 0.004917 + 0.005083 + 0.011958 + 0.007167 + 0.005916 + 0.005459 + 0.008334 + 0.005209 + 0.005417 + 0.004917 + 0.005625 + 0.006209 + 0.006625 + 0.00625 + 0.006166 + 0.011625 + 0.006834 + 0.006334 + 0.006041 + 0.005833 + 0.005333 + 0.005625 + 0.005417 + 0.005 + 0.0105 + 0.005375 + 0.005667 + 0.005917 + 0.006208 + 0.0055 + 0.00525 + 0.008875 + 0.005333 + 0.005625 + 0.005459 + 0.005834 + 0.005333 + 0.004875 + 0.006417 + 0.006042 + 0.011875 + 0.006209 + 0.009333 + 0.006125 + 0.006417 + 0.005834 + 0.006125 + 0.005834 + 0.006 + 0.006292 + 0.008541 + 0.006042 + 0.005791 + 0.006125 + 0.005 + 0.004834 + 0.004834 + 0.004792 + 0.005042 + 0.004834 + 0.005 + 0.004542 + 0.005458 + 0.005 + 0.011333 + 0.008958 + 0.005083 + 0.0045 + 0.004583 + 0.004708 + 0.004625 + 0.004833 + 0.004958 + 0.005 + 0.004958 + 0.0045 + 0.005 + 0.00475 + 0.005084 + 0.004334 + 0.005041 + 0.005167 + 0.006834 + 0.004917 + 0.005083 + 0.005 + 0.004959 + 0.004834 + 0.005084 + 0.004625 + 0.012084 + 0.005125 + 0.00525 + 0.004875 + 0.004875 + 0.005459 + 0.005375 + 0.005333 + 0.004625 + 0.005583 + 0.005042 + 0.005334 + 0.005917 + 0.005958 + 0.005083 + 0.005542 + 0.005084 + 0.005375 + 0.005542 + 0.005125 + 0.005334 + 0.009708 + 0.005916 + 0.006209 + 0.006167 + 0.005916 + 0.006625 + 0.005916 + 0.009209 + 0.005916 + 0.005 + 0.005167 + 0.004958 + 0.005583 + 0.0055 + 0.004875 + 0.005292 + 0.005666 + 0.0055 + 0.005125 + 0.005417 + 0.005375 + 0.005458 + 0.005166 + 0.005083 + 0.00525 + 0.005291 + 0.005083 + 0.013333 + 0.006375 + 0.006375 + 0.006583 + 0.006041 + 0.005875 + 0.005792 + 0.008375 + 0.005042 + 0.006458 + 0.005833 + 0.005541 + 0.005542 + 0.005917 + 0.007583 + 0.005666 + 0.005125 + 0.004834 + 0.005084 + 0.004875 + 0.004834 + 0.004709 + 0.004917 + 0.01675 + 0.00475 + 0.00525 + 0.004625 + 0.005208 + 0.005333 + 0.004458 + 0.004667 + 0.004416 + 0.004792 + 0.005375 + 0.004542 + 0.0045 + 0.004959 + 0.005042 + 0.005708 + 0.0045 + 0.004791 + 0.004542 + 0.004916 + 0.004959 + 0.005084 + 0.004917 + 0.004666 + 0.004792 + 0.004417 + 0.004625 + 0.004625 + 0.005125 + 0.004875 + 0.005 + 0.006333 + 0.005 + 0.004541 + 0.004833 + 0.004625 + 0.004875 + 0.004708 + 0.004708 + 0.00475 + 0.004833 + 0.004791 + 0.005125 + 0.005291 + 0.004625 + 0.005 + 0.004875 + 0.005167 + 0.005125 + 0.005041 + 0.00525 + 0.006667 + 0.005875 + 0.008417 + 0.00625 + 0.006334 + 0.005375 + 0.005708 + 0.004542 + 0.005167 + 0.00525 + 0.004958 + 0.004583 + 0.004791 + 0.004875 + 0.004834 + 0.004666 + 0.005083 + 0.0075 + 0.005166 + 0.005334 + 0.0055 + 0.005 + 0.004875 + 0.005125 + 0.004875 + 0.005458 + 0.005833 + 0.006166 + 0.005667 + 0.008042 + 0.005917 + 0.006041 + 0.005917 + 0.006084 + 0.004834 + 0.006209 + 0.005416 + 0.008542 + 0.006042 + 0.006083 + 0.005583 + 0.005584 + 0.005417 + 0.004875 + 0.004708 + 0.004833 + 0.00525 + 0.004916 + 0.004792 + 0.005708 + 0.005417 + 0.004875 + 0.004541 + 0.00475 + 0.005 + 0.005042 + 0.004417 + 0.004417 + 0.004583 + 0.004083 + 0.004458 + 0.005375 + 0.004708 + 0.004667 + 0.00475 + 0.004459 + 0.004459 + 0.004542 + 0.004833 + 0.004833 + 0.004791 + 0.004625 + 0.004583 + 0.004875 + 0.004542 + 0.005 + 0.004917 + 0.004709 + 0.004459 + 0.004625 + 0.004708 + 0.00475 + 0.0045 + 0.004791 + 0.005042 + 0.004666 + 0.004208 + 0.004375 + 0.004542 + 0.00425 + 0.005375 + 0.005208 + 0.004875 + 0.004625 + 0.005333 + 0.00475 + 0.004834 + 0.00475 + 0.010875 + 0.005542 + 0.006458 + 0.005917 + 0.006291 + 0.006167 + 0.005875 + 0.008416 + 0.006083 + 0.004917 + 0.005 + 0.005542 + 0.005208 + 0.004917 + 0.00475 + 0.009959 + 0.00525 + 0.005083 + 0.005291 + 0.004209 + 0.004792 + 0.005458 + 0.005041 + 0.005125 + 0.005083 + 0.005333 + 0.005917 + 0.0055 + 0.005792 + 0.008917 + 0.006209 + 0.0125 + 0.009209 + 0.005917 + 0.005583 + 0.0055 + 0.004791 + 0.00925 + 0.00625 + 0.012666 + 0.011792 + 0.006292 + 0.006333 + 0.005708 + 0.00875 + 0.005625 + 0.012208 + 0.008958 + 0.005333 + 0.005209 + 0.005625 + 0.004959 + 0.005083 + 0.0045 + 0.004958 + 0.004917 + 0.006125 + 0.006542 + 0.006875 + 0.012875 + 0.005625 + 0.004542 + 0.005166 + 0.004917 + 0.004917 + 0.005792 + 0.005459 + 0.005542 + 0.005625 + 0.011917 + 0.00575 + 0.006209 + 0.005125 + 0.005917 + 0.004959 + 0.00525 + 0.005333 + 0.005 + 0.004625 + 0.005583 + 0.005125 + 0.005166 + 0.005167 + 0.017 + 0.005083 + 0.005459 + 0.005375 + 0.005083 + 0.004958 + 0.005125 + 0.004875 + 0.00475 + 0.005584 + 0.006041 + 0.005459 + 0.00575 + 0.005542 + 0.005167 + 0.005583 + 0.005625 + 0.005208 + 0.005084 + 0.006125 + 0.006792 + 0.015667 + 0.006041 + 0.006625 + 0.006666 + 0.006333 + 0.006292 + 0.005375 + 0.005084 + 0.005625 + 0.005583 + 0.005583 + 0.005375 + 0.004958 + 0.005083 + 0.005125 + 0.0055 + 0.004792 + 0.005625 + 0.005417 + 0.005542 + 0.005208 + 0.005625 + 0.00525 + 0.00525 + 0.005084 + 0.007084 + 0.011708 + 0.006208 + 0.00625 + 0.006041 + 0.005584 + 0.006417 + 0.005292 + 0.008709 + 0.009541 + 0.005958 + 0.005917 + 0.006291 + 0.006083 + 0.006083 + 0.013208 + 0.004917 + 0.004958 + 0.005042 + 0.005041 + 0.004625 + 0.005209 + 0.004916 + 0.005667 + 0.004333 + 0.0045 + 0.005125 + 0.004916 + 0.004709 + 0.005084 + 0.005041 + 0.004917 + 0.004375 + 0.00475 + 0.00475 + 0.004542 + 0.004375 + 0.005083 + 0.004666 + 0.004833 + 0.004458 + 0.004958 + 0.004917 + 0.005166 + 0.005041 + 0.004709 + 0.004959 + 0.005125 + 0.00475 + 0.004791 + 0.004958 + 0.004917 + 0.004708 + 0.004375 + 0.004667 + 0.004458 + 0.005375 + 0.005042 + 0.005084 + 0.004333 + 0.004458 + 0.004708 + 0.00425 + 0.004459 + 0.008209 + 0.010667 + 0.004875 + 0.00475 + 0.004792 + 0.005375 + 0.005292 + 0.005042 + 0.005125 + 0.009084 + 0.006 + 0.006041 + 0.012709 + 0.006334 + 0.006416 + 0.006458 + 0.006292 + 0.0055 + 0.007625 + 0.0055 + 0.005375 + 0.00525 + 0.005125 + 0.005084 + 0.005167 + 0.017292 + 0.0055 + 0.005334 + 0.00475 + 0.004834 + 0.004875 + 0.004625 + 0.005167 + 0.005 + 0.005333 + 0.005084 + 0.006334 + 0.006 + 0.009875 + 0.006 + 0.006375 + 0.0055 + 0.005708 + 0.005916 + 0.005417 + 0.009791 + 0.009542 + 0.005791 + 0.006417 + 0.005958 + 0.005834 + 0.005834 + 0.004666 + 0.004959 + 0.004666 + 0.004958 + 0.004625 + 0.005042 + 0.004209 + 0.005084 + 0.005833 + 0.005292 + 0.004666 + 0.004209 + 0.005334 + 0.005083 + 0.004791 + 0.005334 + 0.005 + 0.004125 + 0.004833 + 0.004792 + 0.004958 + 0.004875 + 0.004875 + 0.004625 + 0.004916 + 0.004542 + 0.005209 + 0.004791 + 0.005292 + 0.004541 + 0.005083 + 0.004667 + 0.00475 + 0.00475 + 0.0045 + 0.00475 + 0.005042 + 0.004875 + 0.004584 + 0.004583 + 0.004916 + 0.004792 + 0.00525 + 0.004625 + 0.004917 + 0.004584 + 0.004334 + 0.004292 + 0.005125 + 0.00825 + 0.005166 + 0.004875 + 0.004917 + 0.01125 + 0.005 + 0.005542 + 0.005209 + 0.005625 + 0.006292 + 0.006583 + 0.006 + 0.006083 + 0.010084 + 0.006375 + 0.006625 + 0.005708 + 0.005125 + 0.00875 + 0.005416 + 0.005125 + 0.005083 + 0.005333 + 0.005375 + 0.005291 + 0.005375 + 0.005041 + 0.005083 + 0.004875 + 0.004625 + 0.005 + 0.005125 + 0.0115 + 0.005834 + 0.00575 + 0.006584 + 0.006084 + 0.006209 + 0.006583 + 0.00675 + 0.009334 + 0.006125 + 0.007 + 0.005542 + 0.011209 + 0.006417 + 0.006 + 0.006458 + 0.012291 + 0.006167 + 0.007084 + 0.004916 + 0.014083 + 0.006084 + 0.005334 + 0.006 + 0.006833 + 0.006625 + 0.006792 + 0.006708 + 0.004959 + 0.005 + 0.005417 + 0.005833 + 0.004875 + 0.004792 + 0.005583 + 0.005667 + 0.005917 + 0.006084 + 0.0065 + 0.005834 + 0.005959 + 0.005042 + 0.004708 + 0.004792 + 0.004583 + 0.005958 + 0.005084 + 0.005459 + 0.004792 + 0.004917 + 0.004791 + 0.004708 + 0.004791 + 0.005084 + 0.005458 + 0.005875 + 0.005875 + 0.00575 + 0.005667 + 0.005709 + 0.006125 + 0.006 + 0.005583 + 0.005625 + 0.005375 + 0.013625 + 0.006084 + 0.00575 + 0.006 + 0.005333 + 0.004958 + 0.004875 + 0.004709 + 0.004709 + 0.004584 + 0.007875 + 0.008375 + 0.019709 + 0.009 + 0.007417 + 0.007292 + 0.007583 + 0.012 + 0.00875 + 0.007667 + 0.0065 + 0.00525 + 0.0045 + 0.006542 + 0.005459 + 0.004958 + 0.005291 + 0.0045 + 0.005458 + 0.004583 + 0.005167 + 0.00525 + 0.006167 + 0.005291 + 0.0055 + 0.004958 + 0.005833 + 0.005541 + 0.005208 + 0.007 + 0.015166 + 0.012958 + 0.005125 + 0.004875 + 0.006708 + 0.0065 + 0.006167 + 0.005333 + 0.005541 + 0.005167 + 0.006292 + 0.005875 + 0.007791 + 0.007375 + 0.007292 + 0.006333 + 0.0055 + 0.005125 + 0.004708 + 0.006 + 0.005167 + 0.005166 + 0.005792 + 0.005291 + 0.005209 + 0.004584 + 0.005084 + 0.005542 + 0.005166 + 0.005084 + 0.005 + 0.005 + 0.005208 + 0.005667 + 0.00575 + 0.006417 + 0.005625 + 0.005875 + 0.006209 + 0.006542 + 0.006416 + 0.006417 + 0.007 + 0.006542 + 0.006333 + 0.004959 + 0.00525 + 0.005459 + 0.005625 + 0.005083 + 0.004417 + 0.004875 + 0.005 + 0.004541 + 0.004917 + 0.004958 + 0.006 + 0.006042 + 0.005291 + 0.005042 + 0.005208 + 0.004958 + 0.005625 + 0.006042 + 0.005583 + 0.005708 + 0.005417 + 0.005042 + 0.005208 + 0.005458 + 0.005083 + 0.004917 + 0.01125 + 0.011333 + 0.009167 + 0.008208 + 0.007625 + 0.007458 + 0.008292 + 0.008291 + 0.007083 + 0.007 + 0.007041 + 0.00675 + 0.006375 + 0.005708 + 0.0055 + 0.005125 + 0.005417 + 0.006 + 0.005625 + 0.005625 + 0.005125 + 0.005833 + 0.005125 + 0.007041 + 0.007167 + 0.007167 + 0.006458 + 0.007708 + 0.007708 + 0.007542 + 0.006042 + 0.005833 + 0.007833 + 0.008041 + 0.006458 + 0.00725 + 0.007917 + 0.008208 + 0.007041 + 0.007708 + 0.008 + 0.007708 + 0.008 + 0.006583 + 0.00625 + 0.00625 + 0.005209 + 0.004625 + 0.005167 + 0.004875 + 0.004625 + 0.006167 + 0.004583 + 0.005125 + 0.005917 + 0.005375 + 0.005583 + 0.006125 + 0.005125 + 0.005125 + 0.005041 + 0.005292 + 0.004584 + 0.005042 + 0.005166 + 0.005125 + 0.00525 + 0.005208 + 0.005042 + 0.004875 + 0.004875 + 0.004709 + 0.004917 + 0.005625 + 0.005166 + 0.004958 + 0.005209 + 0.004625 + 0.005 + 0.004917 + 0.005583 + 0.005667 + 0.004709 + 0.004958 + 0.00525 + 0.0055 + 0.004958 + 0.005417 + 0.004958 + 0.0055 + 0.004917 + 0.005958 + 0.005917 + 0.0055 + 0.005 + 0.005083 + 0.00525 + 0.005208 + 0.006542 + 0.006584 + 0.007375 + 0.007833 + 0.007541 + 0.00825 + 0.00675 + 0.008042 + 0.007708 + 0.008166 + 0.007083 + 0.006667 + 0.007292 + 0.00675 + 0.005584 + 0.00575 + 0.005291 + 0.005167 + 0.005792 + 0.005709 + 0.005375 + 0.005542 + 0.0055 + 0.0075 + 0.006583 + 0.00675 + 0.006583 + 0.006833 + 0.007291 + 0.007792 + 0.005917 + 0.005708 + 0.006166 + 0.010917 + 0.007708 + 0.007708 + 0.008 + 0.00725 + 0.007792 + 0.007291 + 0.0075 + 0.008041 + 0.007542 + 0.007792 + 0.007833 + 0.006541 + 0.005875 + 0.005083 + 0.00475 + 0.005292 + 0.004916 + 0.004958 + 0.004625 + 0.005625 + 0.005458 + 0.004917 + 0.005292 + 0.005375 + 0.005125 + 0.005166 + 0.004875 + 0.005042 + 0.004875 + 0.005125 + 0.005042 + 0.004833 + 0.005 + 0.004708 + 0.004958 + 0.005042 + 0.005292 + 0.005542 + 0.005042 + 0.004667 + 0.004709 + 0.004709 + 0.005084 + 0.00525 + 0.00475 + 0.005084 + 0.005167 + 0.004958 + 0.004625 + 0.00575 + 0.004709 + 0.005084 + 0.005166 + 0.005833 + 0.006042 + 0.005084 + 0.01325 + 0.015708 + 0.026125 + 0.007584 + 0.007291 + 0.006458 + 0.005542 + 0.005417 + 0.007333 + 0.006625 + 0.006666 + 0.006167 + 0.006375 + 0.014167 + 0.0065 + 0.006292 + 0.005792 + 0.008833 + 0.007833 + 0.007917 + 0.006584 + 0.006542 + 0.007083 + 0.006958 + 0.0065 + 0.015084 + 0.005417 + 0.006333 + 0.006333 + 0.006083 + 0.005125 + 0.005417 + 0.005167 + 0.007209 + 0.006667 + 0.007208 + 0.007291 + 0.00725 + 0.007042 + 0.007625 + 0.006666 + 0.005333 + 0.0055 + 0.007459 + 0.006917 + 0.007 + 0.007417 + 0.005875 + 0.005875 + 0.0055 + 0.0075 + 0.007291 + 0.007042 + 0.007541 + 0.006625 + 0.005291 + 0.005334 + 0.005584 + 0.005709 + 0.005625 + 0.005083 + 0.004792 + 0.005625 + 0.005292 + 0.005 + 0.004709 + 0.005417 + 0.005583 + 0.005042 + 0.005542 + 0.005583 + 0.0055 + 0.004792 + 0.005209 + 0.004834 + 0.004791 + 0.005083 + 0.00525 + 0.005 + 0.004834 + 0.004709 + 0.004791 + 0.004875 + 0.00475 + 0.004708 + 0.004792 + 0.004584 + 0.0045 + 0.004833 + 0.004833 + 0.004834 + 0.00425 + 0.005458 + 0.004792 + 0.004833 + 0.004833 + 0.005 + 0.011625 + 0.005125 + 0.004625 + 0.005709 + 0.005542 + 0.006041 + 0.005292 + 0.005875 + 0.005042 + 0.005083 + 0.005167 + 0.009625 + 0.006417 + 0.006459 + 0.006667 + 0.006125 + 0.007375 + 0.006416 + 0.006 + 0.006625 + 0.007625 + 0.006292 + 0.006459 + 0.004875 + 0.00525 + 0.00525 + 0.005416 + 0.005417 + 0.005166 + 0.005125 + 0.005791 + 0.00525 + 0.005125 + 0.005209 + 0.004916 + 0.005375 + 0.006667 + 0.006167 + 0.00625 + 0.006084 + 0.006084 + 0.006334 + 0.005959 + 0.007334 + 0.006709 + 0.006167 + 0.0055 + 0.007084 + 0.007 + 0.007 + 0.006125 + 0.006541 + 0.005959 + 0.006333 + 0.007125 + 0.007 + 0.007583 + 0.007209 + 0.006167 + 0.005125 + 0.005042 + 0.005416 + 0.004667 + 0.004667 + 0.004458 + 0.004417 + 0.004666 + 0.004875 + 0.005167 + 0.004459 + 0.005084 + 0.004875 + 0.004917 + 0.004875 + 0.005 + 0.004666 + 0.004917 + 0.005167 + 0.005 + 0.004666 + 0.004875 + 0.005041 + 0.004958 + 0.005208 + 0.005042 + 0.004875 + 0.005042 + 0.004666 + 0.012583 + 0.004958 + 0.00475 + 0.004667 + 0.005083 + 0.005542 + 0.005459 + 0.005459 + 0.006167 + 0.006625 + 0.006458 + 0.00625 + 0.006667 + 0.006541 + 0.006417 + 0.006292 + 0.004709 + 0.005084 + 0.004875 + 0.005292 + 0.00525 + 0.005459 + 0.005209 + 0.006584 + 0.013625 + 0.006584 + 0.005667 + 0.005375 + 0.005708 + 0.005667 + 0.005834 + 0.005459 + 0.007333 + 0.007333 + 0.006958 + 0.006958 + 0.005958 + 0.005375 + 0.005959 + 0.006209 + 0.006709 + 0.007042 + 0.007 + 0.006791 + 0.008167 + 0.006958 + 0.006125 + 0.005333 + 0.005166 + 0.005125 + 0.005333 + 0.005125 + 0.005041 + 0.005125 + 0.004958 + 0.005958 + 0.005834 + 0.006125 + 0.011125 + 0.00725 + 0.007459 + 0.007625 + 0.007042 + 0.005958 + 0.007458 + 0.006541 + 0.007459 + 0.008042 + 0.007625 + 0.007916 + 0.007583 + 0.006917 + 0.005834 + 0.005334 + 0.005083 + 0.005333 + 0.004791 + 0.005167 + 0.005 + 0.005041 + 0.004708 + 0.005167 + 0.005625 + 0.005375 + 0.005333 + 0.005292 + 0.004417 + 0.004958 + 0.005208 + 0.004791 + 0.005209 + 0.004834 + 0.005166 + 0.00475 + 0.004875 + 0.005125 + 0.005333 + 0.004917 + 0.005542 + 0.006583 + 0.005666 + 0.005958 + 0.005833 + 0.006042 + 0.005875 + 0.005792 + 0.005209 + 0.005041 + 0.005583 + 0.004875 + 0.004875 + 0.005125 + 0.004916 + 0.004917 + 0.004916 + 0.005167 + 0.004709 + 0.004708 + 0.004791 + 0.005667 + 0.005917 + 0.005167 + 0.005084 + 0.00775 + 0.006958 + 0.0065 + 0.006708 + 0.006708 + 0.007125 + 0.0075 + 0.00625 + 0.005542 + 0.010125 + 0.007666 + 0.007541 + 0.007625 + 0.006625 + 0.006666 + 0.006542 + 0.005042 + 0.00475 + 0.005333 + 0.005458 + 0.00525 + 0.00525 + 0.005583 + 0.007 + 0.006875 + 0.006542 + 0.006875 + 0.00675 + 0.00675 + 0.006583 + 0.005875 + 0.00525 + 0.006167 + 0.006167 + 0.006042 + 0.007583 + 0.007667 + 0.00725 + 0.00775 + 0.007458 + 0.007 + 0.007 + 0.007542 + 0.007583 + 0.007458 + 0.007792 + 0.007625 + 0.007417 + 0.006667 + 0.006333 + 0.0065 + 0.006333 + 0.006792 + 0.00625 + 0.006291 + 0.005417 + 0.005834 + 0.005458 + 0.004666 + 0.004875 + 0.005625 + 0.005083 + 0.005208 + 0.004583 + 0.004959 + 0.004791 + 0.004667 + 0.005083 + 0.005041 + 0.00525 + 0.005208 + 0.005084 + 0.004792 + 0.005125 + 0.005 + 0.004959 + 0.004875 + 0.005334 + 0.00525 + 0.004625 + 0.004458 + 0.004959 + 0.004667 + 0.006833 + 0.00475 + 0.004542 + 0.005167 + 0.007083 + 0.005416 + 0.005167 + 0.005167 + 0.005041 + 0.004833 + 0.0055 + 0.004958 + 0.004625 + 0.005541 + 0.005875 + 0.007166 + 0.007583 + 0.017875 + 0.006541 + 0.006625 + 0.006208 + 0.00625 + 0.005416 + 0.007084 + 0.006542 + 0.006208 + 0.00825 + 0.007416 + 0.008292 + 0.008042 + 0.007208 + 0.007 + 0.00675 + 0.005958 + 0.0055 + 0.005333 + 0.005625 + 0.00525 + 0.006375 + 0.006458 + 0.007583 + 0.00675 + 0.006708 + 0.005292 + 0.006334 + 0.006541 + 0.006042 + 0.005667 + 0.005958 + 0.007583 + 0.008458 + 0.008625 + 0.009292 + 0.00875 + 0.008 + 0.007834 + 0.008459 + 0.006625 + 0.007292 + 0.01125 + 0.007625 + 0.007833 + 0.007333 + 0.006708 + 0.006542 + 0.007459 + 0.006542 + 0.007375 + 0.00625 + 0.006375 + 0.005084 + 0.005459 + 0.00575 + 0.006417 + 0.0045 + 0.006 + 0.006166 + 0.012417 + 0.00575 + 0.00525 + 0.013958 + 0.006125 + 0.005917 + 0.005208 + 0.004958 + 0.005125 + 0.005375 + 0.0055 + 0.005084 + 0.005375 + 0.004959 + 0.005417 + 0.005667 + 0.005417 + 0.005083 + 0.005042 + 0.005375 + 0.00525 + 0.004875 + 0.005208 + 0.00475 + 0.0045 + 0.004708 + 0.004917 + 0.005209 + 0.004916 + 0.004875 + 0.004583 + 0.005417 + 0.004916 + 0.004792 + 0.004917 + 0.005208 + 0.005208 + 0.0055 + 0.005417 + 0.004958 + 0.005333 + 0.005958 + 0.005208 + 0.00525 + 0.005958 + 0.005041 + 0.006292 + 0.006375 + 0.007708 + 0.007541 + 0.006833 + 0.007667 + 0.00675 + 0.005334 + 0.00575 + 0.005167 + 0.006958 + 0.006583 + 0.006208 + 0.006583 + 0.005875 + 0.005125 + 0.005125 + 0.005667 + 0.004958 + 0.005041 + 0.005583 + 0.013458 + 0.005584 + 0.006125 + 0.007 + 0.006417 + 0.005167 + 0.006417 + 0.007833 + 0.008542 + 0.007875 + 0.0085 + 0.008375 + 0.007792 + 0.007083 + 0.008916 + 0.007917 + 0.008042 + 0.007375 + 0.007542 + 0.006875 + 0.007667 + 0.008625 + 0.006792 + 0.007167 + 0.006958 + 0.00625 + 0.006084 + 0.006375 + 0.00675 + 0.006916 + 0.006458 + 0.006167 + 0.006584 + 0.007125 + 0.005375 + 0.004708 + 0.005458 + 0.004583 + 0.005042 + 0.005084 + 0.004834 + 0.005083 + 0.004875 + 0.00525 + 0.004792 + 0.005125 + 0.004666 + 0.005084 + 0.005083 + 0.005333 + 0.005208 + 0.004834 + 0.004708 + 0.004958 + 0.005208 + 0.004584 + 0.004583 + 0.004875 + 0.004541 + 0.004834 + 0.005 + 0.005208 + 0.005 + 0.005208 + 0.00525 + 0.004958 + 0.0125 + 0.004792 + 0.004875 + 0.005667 + 0.006208 + 0.005542 + 0.004959 + 0.0055 + 0.005333 + 0.005292 + 0.005125 + 0.004916 + 0.005375 + 0.006125 + 0.011 + 0.007458 + 0.007042 + 0.006709 + 0.006792 + 0.006166 + 0.007333 + 0.006709 + 0.006417 + 0.006708 + 0.005666 + 0.005459 + 0.0055 + 0.00525 + 0.00525 + 0.005375 + 0.00575 + 0.005375 + 0.005291 + 0.005084 + 0.0055 + 0.005 + 0.00525 + 0.005125 + 0.005458 + 0.005166 + 0.007333 + 0.007667 + 0.007042 + 0.006833 + 0.0065 + 0.005833 + 0.007833 + 0.005792 + 0.006291 + 0.00675 + 0.00675 + 0.005875 + 0.006291 + 0.006125 + 0.007708 + 0.007291 + 0.006167 + 0.006041 + 0.005792 + 0.005 + 0.005208 + 0.005167 + 0.005 + 0.004833 + 0.004708 + 0.004791 + 0.005084 + 0.005125 + 0.004708 + 0.004875 + 0.005459 + 0.005417 + 0.004708 + 0.004875 + 0.00525 + 0.004625 + 0.004958 + 0.004833 + 0.005333 + 0.004792 + 0.005167 + 0.005291 + 0.005417 + 0.005084 + 0.006542 + 0.005208 + 0.005416 + 0.004875 + 0.005125 + 0.005292 + 0.005208 + 0.005083 + 0.004709 + 0.005083 + 0.004917 + 0.005584 + 0.004791 + 0.004875 + 0.005083 + 0.00525 + 0.00475 + 0.005292 + 0.005583 + 0.005791 + 0.00575 + 0.005833 + 0.005708 + 0.005333 + 0.005375 + 0.0055 + 0.005333 + 0.005625 + 0.006083 + 0.005333 + 0.006333 + 0.007458 + 0.007125 + 0.015 + 0.006834 + 0.006 + 0.005584 + 0.005292 + 0.00525 + 0.005792 + 0.007375 + 0.006209 + 0.006875 + 0.006584 + 0.007125 + 0.00675 + 0.007083 + 0.006792 + 0.006334 + 0.004958 + 0.0055 + 0.005208 + 0.005 + 0.005083 + 0.005291 + 0.005375 + 0.006208 + 0.00775 + 0.007208 + 0.007375 + 0.007292 + 0.006458 + 0.005917 + 0.008125 + 0.006208 + 0.007083 + 0.006667 + 0.00625 + 0.005708 + 0.005333 + 0.007958 + 0.007042 + 0.005875 + 0.005834 + 0.005834 + 0.005417 + 0.004458 + 0.004667 + 0.004875 + 0.005 + 0.005542 + 0.00475 + 0.004958 + 0.005125 + 0.004917 + 0.004792 + 0.004792 + 0.004458 + 0.004583 + 0.004875 + 0.005042 + 0.004833 + 0.005084 + 0.005042 + 0.004541 + 0.00475 + 0.005 + 0.004375 + 0.005208 + 0.004917 + 0.005333 + 0.004875 + 0.005042 + 0.004875 + 0.005208 + 0.004583 + 0.005 + 0.004875 + 0.004792 + 0.004375 + 0.004584 + 0.0045 + 0.00475 + 0.004583 + 0.004542 + 0.004833 + 0.004875 + 0.004708 + 0.004708 + 0.004875 + 0.005292 + 0.00525 + 0.00525 + 0.005208 + 0.005209 + 0.00475 + 0.004792 + 0.005166 + 0.005208 + 0.0055 + 0.006209 + 0.010708 + 0.007291 + 0.00725 + 0.006541 + 0.006792 + 0.005583 + 0.005 + 0.005208 + 0.005 + 0.005958 + 0.006208 + 0.006041 + 0.006208 + 0.006042 + 0.005667 + 0.005125 + 0.004875 + 0.005292 + 0.005292 + 0.006792 + 0.006 + 0.005209 + 0.0055 + 0.005416 + 0.005292 + 0.004875 + 0.006416 + 0.007542 + 0.006875 + 0.007125 + 0.006667 + 0.006542 + 0.005708 + 0.004875 + 0.007334 + 0.007459 + 0.006959 + 0.0065 + 0.006584 + 0.005542 + 0.007208 + 0.007333 + 0.006333 + 0.005458 + 0.005917 + 0.005209 + 0.005125 + 0.004334 + 0.005292 + 0.005708 + 0.005 + 0.004208 + 0.005 + 0.005167 + 0.005 + 0.004583 + 0.004541 + 0.0045 + 0.004666 + 0.00475 + 0.004792 + 0.0045 + 0.004708 + 0.005125 + 0.004958 + 0.005042 + 0.00475 + 0.005458 + 0.004667 + 0.004875 + 0.004708 + 0.005208 + 0.004625 + 0.005125 + 0.004708 + 0.004875 + 0.004625 + 0.00475 + 0.005 + 0.005 + 0.004542 + 0.004333 + 0.004583 + 0.004625 + 0.004875 + 0.004583 + 0.004875 + 0.004959 + 0.004917 + 0.00525 + 0.0055 + 0.005167 + 0.005125 + 0.005208 + 0.005375 + 0.005 + 0.004792 + 0.005333 + 0.004875 + 0.006083 + 0.010875 + 0.00725 + 0.007334 + 0.006834 + 0.007167 + 0.005916 + 0.007292 + 0.006041 + 0.006375 + 0.006083 + 0.006 + 0.00525 + 0.005167 + 0.005125 + 0.004791 + 0.005083 + 0.0055 + 0.005083 + 0.005042 + 0.005125 + 0.005292 + 0.005166 + 0.004958 + 0.005292 + 0.004917 + 0.005333 + 0.007125 + 0.007333 + 0.007291 + 0.007417 + 0.006291 + 0.005792 + 0.00725 + 0.006125 + 0.006792 + 0.006959 + 0.006792 + 0.005833 + 0.006 + 0.007125 + 0.006917 + 0.006542 + 0.006333 + 0.005541 + 0.004959 + 0.004584 + 0.004916 + 0.00475 + 0.004708 + 0.004833 + 0.004791 + 0.005125 + 0.004375 + 0.005042 + 0.005084 + 0.004667 + 0.005041 + 0.004708 + 0.004458 + 0.005083 + 0.00575 + 0.005375 + 0.004375 + 0.004583 + 0.005 + 0.005083 + 0.004583 + 0.005375 + 0.004875 + 0.004833 + 0.004875 + 0.004875 + 0.005042 + 0.004958 + 0.004541 + 0.004917 + 0.004917 + 0.004708 + 0.004541 + 0.004667 + 0.004833 + 0.004791 + 0.004333 + 0.009292 + 0.006 + 0.0055 + 0.005875 + 0.006417 + 0.006292 + 0.006208 + 0.006 + 0.006125 + 0.005917 + 0.005375 + 0.005625 + 0.005833 + 0.006167 + 0.0055 + 0.006459 + 0.008708 + 0.007208 + 0.00675 + 0.007125 + 0.006875 + 0.007084 + 0.007334 + 0.007792 + 0.006333 + 0.006125 + 0.005917 + 0.006167 + 0.007208 + 0.00675 + 0.006208 + 0.006208 + 0.005417 + 0.006084 + 0.005834 + 0.005958 + 0.006708 + 0.005791 + 0.005792 + 0.006875 + 0.006125 + 0.006791 + 0.0065 + 0.007666 + 0.007 + 0.006583 + 0.006833 + 0.00575 + 0.007666 + 0.008083 + 0.007917 + 0.006417 + 0.007541 + 0.007666 + 0.007334 + 0.007459 + 0.007959 + 0.007625 + 0.007 + 0.005791 + 0.005334 + 0.006041 + 0.006209 + 0.005666 + 0.005625 + 0.005917 + 0.006583 + 0.00625 + 0.005459 + 0.005209 + 0.00575 + 0.007542 + 0.006917 + 0.006875 + 0.006 + 0.005625 + 0.00575 + 0.007 + 0.005959 + 0.006042 + 0.006416 + 0.006 + 0.006416 + 0.006125 + 0.006459 + 0.006209 + 0.005833 + 0.00525 + 0.005 + 0.005791 + 0.00575 + 0.006666 + 0.006875 + 0.006042 + 0.006875 + 0.005208 + 0.005458 + 0.005083 + 0.005917 + 0.00625 + 0.006167 + 0.006125 + 0.006042 + 0.006041 + 0.005833 + 0.006042 + 0.007709 + 0.007 + 0.007083 + 0.007917 + 0.00975 + 0.007958 + 0.007 + 0.008958 + 0.007667 + 0.008667 + 0.009 + 0.0085 + 0.007708 + 0.006417 + 0.0075 + 0.007084 + 0.009625 + 0.008875 + 0.007208 + 0.007042 + 0.006417 + 0.00625 + 0.0065 + 0.006417 + 0.005084 + 0.004875 + 0.005167 + 0.005416 + 0.005042 + 0.004959 + 0.005333 + 0.00525 + 0.005083 + 0.005042 + 0.006958 + 0.006416 + 0.006792 + 0.008209 + 0.007917 + 0.007917 + 0.007666 + 0.007625 + 0.009042 + 0.00725 + 0.00725 + 0.006 + 0.006958 + 0.005917 + 0.006416 + 0.010208 + 0.007833 + 0.007625 + 0.007125 + 0.00525 + 0.004959 + 0.004834 + 0.005333 + 0.005083 + 0.004625 + 0.00525 + 0.00525 + 0.005625 + 0.005542 + 0.004333 + 0.005167 + 0.005167 + 0.005125 + 0.005125 + 0.004792 + 0.004875 + 0.004625 + 0.005125 + 0.004667 + 0.005167 + 0.004708 + 0.004916 + 0.00475 + 0.004375 + 0.005 + 0.004834 + 0.005167 + 0.005125 + 0.004917 + 0.00525 + 0.004875 + 0.005292 + 0.004791 + 0.004958 + 0.005042 + 0.004792 + 0.004958 + 0.004917 + 0.005125 + 0.004917 + 0.005125 + 0.005416 + 0.005333 + 0.004584 + 0.00525 + 0.004709 + 0.0045 + 0.005917 + 0.0055 + 0.004959 + 0.004875 + 0.005167 + 0.005375 + 0.004917 + 0.005292 + 0.006916 + 0.006917 + 0.008 + 0.007083 + 0.006333 + 0.005875 + 0.005917 + 0.007958 + 0.007416 + 0.006292 + 0.006292 + 0.005958 + 0.004958 + 0.005333 + 0.005458 + 0.00525 + 0.005667 + 0.005125 + 0.005084 + 0.004084 + 0.004916 + 0.00475 + 0.00575 + 0.00475 + 0.005209 + 0.0055 + 0.005 + 0.005334 + 0.004834 + 0.008167 + 0.007416 + 0.007083 + 0.007083 + 0.006375 + 0.00575 + 0.007083 + 0.006375 + 0.006958 + 0.006875 + 0.006958 + 0.006 + 0.006042 + 0.007625 + 0.007583 + 0.007292 + 0.005541 + 0.005458 + 0.004917 + 0.005042 + 0.005 + 0.005208 + 0.005083 + 0.005083 + 0.006208 + 0.005292 + 0.005125 + 0.005333 + 0.004833 + 0.004875 + 0.004959 + 0.004959 + 0.005291 + 0.004959 + 0.004667 + 0.004916 + 0.004875 + 0.005167 + 0.00475 + 0.004917 + 0.004791 + 0.004667 + 0.005084 + 0.004709 + 0.005 + 0.004958 + 0.0045 + 0.004708 + 0.0045 + 0.004333 + 0.005125 + 0.005 + 0.004875 + 0.004583 + 0.004583 + 0.004792 + 0.004875 + 0.005167 + 0.005208 + 0.004625 + 0.004958 + 0.004458 + 0.004916 + 0.004417 + 0.005042 + 0.005083 + 0.005208 + 0.005208 + 0.005042 + 0.004916 + 0.004917 + 0.005292 + 0.005083 + 0.00825 + 0.007417 + 0.007334 + 0.00725 + 0.006375 + 0.006417 + 0.007417 + 0.007416 + 0.006458 + 0.006292 + 0.005709 + 0.005166 + 0.005209 + 0.005042 + 0.005333 + 0.005417 + 0.0055 + 0.005167 + 0.004916 + 0.004917 + 0.00525 + 0.005125 + 0.005292 + 0.008959 + 0.00625 + 0.006333 + 0.006291 + 0.007292 + 0.006458 + 0.005958 + 0.006584 + 0.005958 + 0.007083 + 0.007291 + 0.005833 + 0.006958 + 0.006625 + 0.005958 + 0.00625 + 0.007416 + 0.007125 + 0.007375 + 0.007542 + 0.005167 + 0.004334 + 0.004709 + 0.004875 + 0.005083 + 0.004833 + 0.004667 + 0.005625 + 0.005 + 0.005 + 0.004375 + 0.005042 + 0.005042 + 0.004917 + 0.005083 + 0.004875 + 0.00525 + 0.005042 + 0.005292 + 0.004625 + 0.004916 + 0.005 + 0.004875 + 0.004584 + 0.004667 + 0.005084 + 0.005292 + 0.004791 + 0.005 + 0.0045 + 0.005375 + 0.005 + 0.004916 + 0.00475 + 0.005041 + 0.0045 + 0.004958 + 0.005167 + 0.004875 + 0.004917 + 0.005 + 0.00475 + 0.005542 + 0.004875 + 0.00475 + 0.004708 + 0.004792 + 0.004833 + 0.005583 + 0.005125 + 0.004792 + 0.005042 + 0.005 + 0.004959 + 0.004625 + 0.005167 + 0.005209 + 0.006959 + 0.007 + 0.00725 + 0.007166 + 0.006375 + 0.005666 + 0.007459 + 0.0075 + 0.0065 + 0.006291 + 0.006375 + 0.005291 + 0.005625 + 0.005166 + 0.005209 + 0.005375 + 0.005625 + 0.004958 + 0.005375 + 0.005042 + 0.005 + 0.004791 + 0.00475 + 0.006375 + 0.005834 + 0.005834 + 0.006084 + 0.006958 + 0.006125 + 0.006416 + 0.005917 + 0.006166 + 0.008042 + 0.007417 + 0.006792 + 0.006667 + 0.00675 + 0.006292 + 0.005625 + 0.008125 + 0.007208 + 0.007042 + 0.007208 + 0.005083 + 0.004583 + 0.004708 + 0.004833 + 0.004584 + 0.005042 + 0.004541 + 0.005208 + 0.00525 + 0.005208 + 0.004916 + 0.004709 + 0.004667 + 0.005416 + 0.005208 + 0.004958 + 0.005042 + 0.00475 + 0.004875 + 0.005125 + 0.005083 + 0.004292 + 0.004834 + 0.004666 + 0.004542 + 0.004542 + 0.0045 + 0.004834 + 0.005 + 0.004834 + 0.005083 + 0.004666 + 0.00475 + 0.0045 + 0.005166 + 0.004333 + 0.004708 + 0.004417 + 0.004833 + 0.004708 + 0.004834 + 0.004834 + 0.005334 + 0.004459 + 0.005083 + 0.004666 + 0.00525 + 0.004625 + 0.004917 + 0.005166 + 0.005583 + 0.004833 + 0.005208 + 0.005 + 0.004583 + 0.004792 + 0.009375 + 0.006334 + 0.007 + 0.007167 + 0.006709 + 0.006375 + 0.005834 + 0.007208 + 0.007333 + 0.00625 + 0.006167 + 0.006333 + 0.005375 + 0.005583 + 0.004916 + 0.005084 + 0.005375 + 0.005583 + 0.004833 + 0.0055 + 0.00525 + 0.00525 + 0.004959 + 0.004792 + 0.005083 + 0.00475 + 0.005125 + 0.00525 + 0.007625 + 0.006791 + 0.006792 + 0.007 + 0.006708 + 0.005834 + 0.006166 + 0.007584 + 0.006416 + 0.006833 + 0.007 + 0.006833 + 0.006833 + 0.006708 + 0.008083 + 0.008583 + 0.005667 + 0.004459 + 0.004584 + 0.00475 + 0.004333 + 0.004458 + 0.004292 + 0.005792 + 0.005542 + 0.004416 + 0.00525 + 0.0045 + 0.005041 + 0.005125 + 0.004708 + 0.004708 + 0.004542 + 0.004709 + 0.004792 + 0.004917 + 0.004792 + 0.004959 + 0.004667 + 0.004458 + 0.004625 + 0.004167 + 0.004708 + 0.004833 + 0.004708 + 0.004583 + 0.004917 + 0.004792 + 0.004625 + 0.004667 + 0.005208 + 0.004875 + 0.004709 + 0.004791 + 0.004875 + 0.004959 + 0.005291 + 0.005 + 0.004917 + 0.005167 + 0.004459 + 0.004917 + 0.005125 + 0.004708 + 0.004875 + 0.005416 + 0.005292 + 0.014667 + 0.006042 + 0.00475 + 0.004959 + 0.004791 + 0.005417 + 0.006291 + 0.007 + 0.008291 + 0.007917 + 0.008166 + 0.0075 + 0.007666 + 0.007625 + 0.006708 + 0.005792 + 0.005667 + 0.004917 + 0.005167 + 0.005125 + 0.008333 + 0.006166 + 0.006083 + 0.006083 + 0.005583 + 0.004792 + 0.005167 + 0.00525 + 0.005 + 0.00525 + 0.005042 + 0.004875 + 0.005 + 0.004708 + 0.006417 + 0.006375 + 0.008417 + 0.00875 + 0.00725 + 0.007542 + 0.007459 + 0.006542 + 0.007541 + 0.0085 + 0.00825 + 0.012792 + 0.009291 + 0.007875 + 0.007 + 0.007584 + 0.005334 + 0.005541 + 0.006083 + 0.005625 + 0.005416 + 0.005791 + 0.005208 + 0.006334 + 0.005292 + 0.005167 + 0.004875 + 0.004792 + 0.004875 + 0.004833 + 0.004167 + 0.004458 + 0.004209 + 0.004375 + 0.004583 + 0.004709 + 0.003958 + 0.004292 + 0.004041 + 0.004166 + 0.003958 + 0.004375 + 0.00425 + 0.004167 + 0.003875 + 0.004125 + 0.004125 + 0.004875 + 0.004709 + 0.005583 + 0.004708 + 0.005041 + 0.004042 + 0.004375 + 0.004459 + 0.004291 + 0.003917 + 0.004041 + 0.004208 + 0.004042 + 0.004291 + 0.004458 + 0.004458 + 0.004167 + 0.005 + 0.005042 + 0.004542 + 0.004125 + 0.004375 + 0.004166 + 0.004167 + 0.006416 + 0.00575 + 0.006458 + 0.006208 + 0.006583 + 0.005625 + 0.00575 + 0.008416 + 0.0065 + 0.006417 + 0.005541 + 0.005333 + 0.0045 + 0.004333 + 0.004417 + 0.004416 + 0.004209 + 0.004292 + 0.00425 + 0.004375 + 0.005 + 0.004833 + 0.005875 + 0.0055 + 0.005166 + 0.005417 + 0.005708 + 0.005708 + 0.004917 + 0.005459 + 0.005125 + 0.005084 + 0.005333 + 0.006917 + 0.006917 + 0.007 + 0.005459 + 0.006083 + 0.005916 + 0.00625 + 0.008667 + 0.008334 + 0.007875 + 0.007833 + 0.007458 + 0.006584 + 0.005709 + 0.005416 + 0.004708 + 0.005 + 0.005292 + 0.004625 + 0.005042 + 0.00575 + 0.004875 + 0.005166 + 0.005 + 0.00575 + 0.005208 + 0.005167 + 0.005166 + 0.005209 + 0.005 + 0.004708 + 0.004917 + 0.005084 + 0.005042 + 0.005291 + 0.005667 + 0.006167 + 0.006834 + 0.006458 + 0.00625 + 0.006375 + 0.006167 + 0.006166 + 0.006333 + 0.005125 + 0.005125 + 0.004958 + 0.004875 + 0.004375 + 0.004708 + 0.004833 + 0.004875 + 0.004791 + 0.004958 + 0.005583 + 0.005208 + 0.00475 + 0.005333 + 0.004917 + 0.004834 + 0.005333 + 0.005125 + 0.00525 + 0.005166 + 0.006833 + 0.006625 + 0.006458 + 0.00675 + 0.006542 + 0.007417 + 0.00725 + 0.006125 + 0.006084 + 0.008166 + 0.007917 + 0.00775 + 0.007667 + 0.007125 + 0.006458 + 0.006833 + 0.005667 + 0.005167 + 0.0055 + 0.005167 + 0.005583 + 0.005458 + 0.005667 + 0.007417 + 0.007041 + 0.007125 + 0.006583 + 0.0065 + 0.006625 + 0.006417 + 0.005875 + 0.005042 + 0.006208 + 0.006375 + 0.007667 + 0.008792 + 0.00775 + 0.007375 + 0.007375 + 0.007667 + 0.005709 + 0.006417 + 0.007708 + 0.008167 + 0.007666 + 0.00725 + 0.007625 + 0.007916 + 0.006042 + 0.005125 + 0.004833 + 0.005 + 0.005 + 0.005042 + 0.004959 + 0.005 + 0.005583 + 0.005417 + 0.004708 + 0.004875 + 0.005416 + 0.005583 + 0.005 + 0.00475 + 0.004666 + 0.004584 + 0.00475 + 0.004958 + 0.00525 + 0.004917 + 0.00525 + 0.004625 + 0.004917 + 0.005167 + 0.0055 + 0.00475 + 0.005292 + 0.005125 + 0.005083 + 0.004708 + 0.005208 + 0.004667 + 0.004709 + 0.004875 + 0.004875 + 0.004375 + 0.004875 + 0.004667 + 0.00475 + 0.004917 + 0.005041 + 0.005167 + 0.00525 + 0.004916 + 0.004709 + 0.004542 + 0.005333 + 0.005541 + 0.007417 + 0.006959 + 0.0065 + 0.006416 + 0.006167 + 0.006792 + 0.006666 + 0.005959 + 0.007167 + 0.008125 + 0.00725 + 0.008916 + 0.007 + 0.007958 + 0.008 + 0.006459 + 0.00625 + 0.006375 + 0.005791 + 0.005334 + 0.005625 + 0.005875 + 0.005084 + 0.00525 + 0.005625 + 0.007208 + 0.00725 + 0.006459 + 0.007167 + 0.006666 + 0.006792 + 0.007042 + 0.005791 + 0.005042 + 0.00575 + 0.006375 + 0.005959 + 0.008 + 0.008083 + 0.007208 + 0.007583 + 0.007625 + 0.006625 + 0.006833 + 0.007791 + 0.008042 + 0.007667 + 0.007791 + 0.008417 + 0.007792 + 0.005833 + 0.005083 + 0.00575 + 0.005333 + 0.005292 + 0.005083 + 0.005083 + 0.005875 + 0.006 + 0.005209 + 0.004417 + 0.005416 + 0.006208 + 0.006042 + 0.006583 + 0.006291 + 0.005875 + 0.005083 + 0.005167 + 0.004917 + 0.004958 + 0.005708 + 0.005875 + 0.005708 + 0.005125 + 0.005125 + 0.005291 + 0.005375 + 0.005125 + 0.005375 + 0.006416 + 0.007333 + 0.007375 + 0.006458 + 0.007 + 0.006333 + 0.007125 + 0.00675 + 0.007 + 0.006709 + 0.006541 + 0.005875 + 0.005875 + 0.006709 + 0.005375 + 0.0055 + 0.006917 + 0.005875 + 0.005792 + 0.006334 + 0.007958 + 0.006541 + 0.00775 + 0.007333 + 0.005875 + 0.006458 + 0.005791 + 0.006292 + 0.00725 + 0.0065 + 0.007292 + 0.0085 + 0.007584 + 0.007209 + 0.007125 + 0.006875 + 0.006333 + 0.0055 + 0.005667 + 0.005708 + 0.00575 + 0.005084 + 0.006 + 0.005834 + 0.007209 + 0.006917 + 0.006458 + 0.006166 + 0.006875 + 0.005375 + 0.005625 + 0.005458 + 0.005875 + 0.006125 + 0.005167 + 0.006791 + 0.006667 + 0.00675 + 0.00825 + 0.008292 + 0.007041 + 0.0075 + 0.006083 + 0.006 + 0.0055 + 0.008 + 0.007167 + 0.007 + 0.0075 + 0.006875 + 0.005917 + 0.005417 + 0.005 + 0.005334 + 0.004792 + 0.004833 + 0.005458 + 0.005459 + 0.005459 + 0.00525 + 0.00525 + 0.005 + 0.005417 + 0.005291 + 0.005167 + 0.005041 + 0.005083 + 0.005 + 0.00475 + 0.004709 + 0.004875 + 0.004667 + 0.005167 + 0.004791 + 0.004792 + 0.004834 + 0.005209 + 0.0055 + 0.006 + 0.005833 + 0.005667 + 0.005042 + 0.004875 + 0.004709 + 0.004916 + 0.004292 + 0.004875 + 0.004875 + 0.004709 + 0.005125 + 0.0055 + 0.004834 + 0.004625 + 0.00525 + 0.005 + 0.00525 + 0.00525 + 0.005667 + 0.006459 + 0.007042 + 0.008541 + 0.007542 + 0.006542 + 0.006625 + 0.006 + 0.005792 + 0.00575 + 0.005375 + 0.006084 + 0.006209 + 0.005958 + 0.007083 + 0.007125 + 0.006708 + 0.006833 + 0.006541 + 0.005708 + 0.004917 + 0.005458 + 0.004667 + 0.005375 + 0.006583 + 0.006625 + 0.006209 + 0.006292 + 0.005958 + 0.005125 + 0.00525 + 0.005458 + 0.005458 + 0.005375 + 0.005417 + 0.005209 + 0.005167 + 0.005208 + 0.005958 + 0.00625 + 0.007666 + 0.010875 + 0.008291 + 0.006583 + 0.006084 + 0.005125 + 0.004625 + 0.006292 + 0.008041 + 0.006958 + 0.00725 + 0.007208 + 0.006375 + 0.006041 + 0.005334 + 0.004792 + 0.004875 + 0.005041 + 0.005042 + 0.005125 + 0.005166 + 0.00475 + 0.005625 + 0.005375 + 0.0045 + 0.0055 + 0.004667 + 0.005166 + 0.005167 + 0.005125 + 0.004667 + 0.004875 + 0.004416 + 0.004709 + 0.005375 + 0.004875 + 0.004667 + 0.004875 + 0.004959 + 0.004917 + 0.004541 + 0.005042 + 0.004625 + 0.00475 + 0.005125 + 0.004875 + 0.004792 + 0.004792 + 0.004916 + 0.004667 + 0.004917 + 0.0045 + 0.005125 + 0.004792 + 0.005125 + 0.005458 + 0.005041 + 0.005042 + 0.004917 + 0.004834 + 0.004709 + 0.004958 + 0.00525 + 0.006666 + 0.006166 + 0.006292 + 0.006208 + 0.00625 + 0.005333 + 0.004792 + 0.005458 + 0.005375 + 0.00625 + 0.005875 + 0.007083 + 0.006917 + 0.00725 + 0.006708 + 0.006125 + 0.00525 + 0.005167 + 0.005417 + 0.005291 + 0.005583 + 0.005542 + 0.006875 + 0.006916 + 0.006417 + 0.006375 + 0.00625 + 0.005667 + 0.00525 + 0.005333 + 0.005625 + 0.005 + 0.005375 + 0.005542 + 0.005291 + 0.005833 + 0.006167 + 0.008042 + 0.0075 + 0.006625 + 0.006125 + 0.005625 + 0.006042 + 0.005541 + 0.008167 + 0.007459 + 0.006791 + 0.006875 + 0.006833 + 0.00625 + 0.007709 + 0.006209 + 0.005792 + 0.006916 + 0.005833 + 0.005417 + 0.004959 + 0.004916 + 0.00475 + 0.005292 + 0.00575 + 0.005209 + 0.005916 + 0.005167 + 0.005458 + 0.005917 + 0.005209 + 0.005167 + 0.005125 + 0.005084 + 0.005584 + 0.005167 + 0.005125 + 0.005208 + 0.004708 + 0.004833 + 0.004917 + 0.005292 + 0.005167 + 0.005 + 0.004917 + 0.005125 + 0.004625 + 0.004917 + 0.004959 + 0.004833 + 0.005625 + 0.005292 + 0.006041 + 0.0065 + 0.006167 + 0.006625 + 0.005958 + 0.0065 + 0.006041 + 0.005875 + 0.008083 + 0.005083 + 0.004708 + 0.005333 + 0.005584 + 0.005417 + 0.005167 + 0.005291 + 0.004959 + 0.005042 + 0.004875 + 0.005334 + 0.005208 + 0.006541 + 0.006167 + 0.007792 + 0.007208 + 0.006792 + 0.006542 + 0.006458 + 0.005333 + 0.005042 + 0.005208 + 0.005041 + 0.004792 + 0.004875 + 0.006917 + 0.006416 + 0.006208 + 0.00625 + 0.005708 + 0.005291 + 0.004959 + 0.004875 + 0.005083 + 0.005083 + 0.004833 + 0.00525 + 0.005417 + 0.005625 + 0.006042 + 0.007083 + 0.007208 + 0.007125 + 0.006959 + 0.0065 + 0.005959 + 0.005292 + 0.006916 + 0.007 + 0.006791 + 0.006584 + 0.006375 + 0.005625 + 0.006792 + 0.006625 + 0.005667 + 0.005666 + 0.005958 + 0.005292 + 0.004625 + 0.004834 + 0.004584 + 0.005125 + 0.004417 + 0.005083 + 0.005208 + 0.005125 + 0.005208 + 0.004792 + 0.005083 + 0.004708 + 0.005042 + 0.004666 + 0.005 + 0.004667 + 0.004959 + 0.00475 + 0.004834 + 0.004916 + 0.004917 + 0.005 + 0.004667 + 0.004917 + 0.004791 + 0.004667 + 0.005041 + 0.004959 + 0.005125 + 0.004791 + 0.004458 + 0.004917 + 0.004334 + 0.004459 + 0.004458 + 0.004458 + 0.004709 + 0.004708 + 0.005042 + 0.004583 + 0.004458 + 0.004542 + 0.005292 + 0.004875 + 0.005 + 0.005208 + 0.005 + 0.006 + 0.005917 + 0.006 + 0.006167 + 0.005833 + 0.004917 + 0.005834 + 0.007542 + 0.007292 + 0.006625 + 0.007 + 0.00675 + 0.005709 + 0.004791 + 0.005125 + 0.005208 + 0.006875 + 0.005584 + 0.005834 + 0.005542 + 0.00575 + 0.00525 + 0.005458 + 0.005083 + 0.005291 + 0.005292 + 0.005417 + 0.004958 + 0.005208 + 0.004959 + 0.005125 + 0.005125 + 0.005 + 0.006084 + 0.007875 + 0.007084 + 0.006792 + 0.007208 + 0.006458 + 0.006916 + 0.005292 + 0.007375 + 0.00675 + 0.006875 + 0.0065 + 0.006042 + 0.00575 + 0.006875 + 0.007375 + 0.00625 + 0.005833 + 0.005792 + 0.004541 + 0.004625 + 0.005083 + 0.004875 + 0.004791 + 0.004709 + 0.005042 + 0.004708 + 0.005125 + 0.004625 + 0.0045 + 0.004542 + 0.004416 + 0.004583 + 0.004833 + 0.005084 + 0.004375 + 0.004917 + 0.004708 + 0.005166 + 0.004458 + 0.005083 + 0.004667 + 0.00475 + 0.004625 + 0.005208 + 0.004917 + 0.004541 + 0.004667 + 0.004583 + 0.004792 + 0.005042 + 0.004583 + 0.0045 + 0.004709 + 0.004542 + 0.00475 + 0.004583 + 0.004291 + 0.004583 + 0.004875 + 0.004583 + 0.005042 + 0.004459 + 0.004792 + 0.005167 + 0.005416 + 0.005042 + 0.005167 + 0.005083 + 0.005125 + 0.005 + 0.00525 + 0.004875 + 0.006041 + 0.007167 + 0.007125 + 0.007083 + 0.006916 + 0.006042 + 0.005958 + 0.007083 + 0.006458 + 0.005916 + 0.005958 + 0.005958 + 0.005042 + 0.005209 + 0.004959 + 0.004625 + 0.00525 + 0.005375 + 0.005333 + 0.00525 + 0.00475 + 0.005167 + 0.005166 + 0.005125 + 0.005167 + 0.005333 + 0.005 + 0.007208 + 0.007083 + 0.007292 + 0.007125 + 0.006458 + 0.006 + 0.007375 + 0.005625 + 0.005959 + 0.006667 + 0.007208 + 0.005709 + 0.005875 + 0.005583 + 0.00725 + 0.006875 + 0.005917 + 0.005958 + 0.005833 + 0.006417 + 0.005834 + 0.006375 + 0.006167 + 0.005375 + 0.005125 + 0.005208 + 0.005458 + 0.005083 + 0.005958 + 0.006458 + 0.006583 + 0.006833 + 0.005458 + 0.005542 + 0.005791 + 0.006084 + 0.005834 + 0.005584 + 0.005417 + 0.006042 + 0.005875 + 0.00575 + 0.006292 + 0.005916 + 0.005917 + 0.005875 + 0.006333 + 0.006875 + 0.006833 + 0.007083 + 0.006291 + 0.006667 + 0.005791 + 0.005667 + 0.005458 + 0.00525 + 0.005625 + 0.005291 + 0.005959 + 0.005084 + 0.004667 + 0.00575 + 0.005667 + 0.006167 + 0.007083 + 0.0075 + 0.007417 + 0.006375 + 0.006916 + 0.007208 + 0.005958 + 0.004917 + 0.00525 + 0.005291 + 0.006292 + 0.007833 + 0.007583 + 0.007583 + 0.007917 + 0.008167 + 0.007709 + 0.005875 + 0.006166 + 0.007083 + 0.008375 + 0.006875 + 0.005791 + 0.006042 + 0.005875 + 0.005917 + 0.006333 + 0.005666 + 0.006292 + 0.006375 + 0.005834 + 0.005875 + 0.006375 + 0.006333 + 0.005833 + 0.006208 + 0.007 + 0.008166 + 0.007416 + 0.006833 + 0.007375 + 0.007666 + 0.008042 + 0.008166 + 0.006583 + 0.008083 + 0.007667 + 0.007375 + 0.007583 + 0.006833 + 0.010334 + 0.007375 + 0.006375 + 0.006 + 0.006 + 0.006667 + 0.006292 + 0.005791 + 0.006167 + 0.005917 + 0.006666 + 0.006 + 0.005708 + 0.006291 + 0.006542 + 0.005583 + 0.004875 + 0.004584 + 0.005042 + 0.007 + 0.008 + 0.008583 + 0.009125 + 0.0085 + 0.00775 + 0.006709 + 0.006833 + 0.007083 + 0.005792 + 0.008417 + 0.007 + 0.006541 + 0.006375 + 0.006708 + 0.006583 + 0.004958 + 0.00575 + 0.005708 + 0.006833 + 0.005583 + 0.006542 + 0.0075 + 0.005958 + 0.005708 + 0.006 + 0.005792 + 0.005 + 0.004542 + 0.004791 + 0.005125 + 0.005917 + 0.005375 + 0.005375 + 0.005542 + 0.005291 + 0.005333 + 0.005625 + 0.006667 + 0.006042 + 0.006208 + 0.006458 + 0.008042 + 0.007334 + 0.007042 + 0.006958 + 0.007416 + 0.006375 + 0.005166 + 0.005083 + 0.005417 + 0.007 + 0.006416 + 0.006209 + 0.006042 + 0.005875 + 0.004959 + 0.005291 + 0.005167 + 0.004917 + 0.005292 + 0.005416 + 0.005542 + 0.005209 + 0.005209 + 0.005417 + 0.005209 + 0.00475 + 0.006375 + 0.007791 + 0.007042 + 0.007125 + 0.00725 + 0.006375 + 0.005959 + 0.004958 + 0.007291 + 0.00725 + 0.007 + 0.007167 + 0.0065 + 0.006083 + 0.006917 + 0.007 + 0.005833 + 0.005542 + 0.006084 + 0.005167 + 0.005125 + 0.004459 + 0.004834 + 0.005541 + 0.004667 + 0.004541 + 0.005625 + 0.005375 + 0.005125 + 0.005125 + 0.00475 + 0.004584 + 0.004416 + 0.004708 + 0.00475 + 0.004708 + 0.00475 + 0.004917 + 0.004583 + 0.005167 + 0.004792 + 0.005041 + 0.005125 + 0.005417 + 0.004708 + 0.00525 + 0.004833 + 0.005125 + 0.004833 + 0.004667 + 0.004708 + 0.004958 + 0.004625 + 0.005 + 0.004584 + 0.005041 + 0.00475 + 0.004958 + 0.005 + 0.00475 + 0.004875 + 0.004625 + 0.004958 + 0.004708 + 0.004917 + 0.005125 + 0.005125 + 0.005042 + 0.005458 + 0.004958 + 0.005 + 0.004708 + 0.005458 + 0.005625 + 0.007458 + 0.00725 + 0.007375 + 0.006833 + 0.007292 + 0.006125 + 0.007584 + 0.006458 + 0.006291 + 0.006375 + 0.0065 + 0.005042 + 0.005167 + 0.005 + 0.005292 + 0.005 + 0.00525 + 0.005542 + 0.005833 + 0.006 + 0.005375 + 0.005083 + 0.00525 + 0.005458 + 0.005166 + 0.005292 + 0.008 + 0.007125 + 0.006959 + 0.006959 + 0.005917 + 0.005458 + 0.007833 + 0.006625 + 0.007375 + 0.007166 + 0.006375 + 0.006 + 0.006125 + 0.008334 + 0.0075 + 0.00675 + 0.006583 + 0.005541 + 0.004833 + 0.005083 + 0.004875 + 0.004917 + 0.005 + 0.004416 + 0.004833 + 0.004708 + 0.004708 + 0.005 + 0.005083 + 0.005 + 0.004666 + 0.0045 + 0.004666 + 0.004417 + 0.005 + 0.004542 + 0.004666 + 0.0045 + 0.004666 + 0.00475 + 0.004917 + 0.007333 + 0.004916 + 0.004833 + 0.005375 + 0.004792 + 0.004958 + 0.004917 + 0.004916 + 0.004834 + 0.005041 + 0.005042 + 0.004875 + 0.004584 + 0.004916 + 0.004875 + 0.004209 + 0.004875 + 0.004958 + 0.004709 + 0.004459 + 0.004584 + 0.005084 + 0.004541 + 0.005375 + 0.005333 + 0.004792 + 0.004667 + 0.004875 + 0.005292 + 0.004958 + 0.004708 + 0.00525 + 0.008125 + 0.007167 + 0.006875 + 0.006625 + 0.006625 + 0.005958 + 0.0075 + 0.00725 + 0.006458 + 0.006333 + 0.005666 + 0.005208 + 0.005167 + 0.004917 + 0.005375 + 0.005334 + 0.004833 + 0.004917 + 0.005125 + 0.005 + 0.005 + 0.005 + 0.004875 + 0.006292 + 0.006291 + 0.006375 + 0.005916 + 0.006917 + 0.006792 + 0.005583 + 0.00575 + 0.005875 + 0.007375 + 0.007 + 0.006833 + 0.005833 + 0.006542 + 0.0055 + 0.006042 + 0.007333 + 0.007541 + 0.006833 + 0.00675 + 0.005416 + 0.004625 + 0.005042 + 0.004667 + 0.00525 + 0.004875 + 0.004792 + 0.004625 + 0.004541 + 0.004833 + 0.004542 + 0.005292 + 0.005416 + 0.005125 + 0.004875 + 0.0045 + 0.004916 + 0.004667 + 0.0045 + 0.004625 + 0.004833 + 0.004542 + 0.004667 + 0.004416 + 0.004667 + 0.004583 + 0.004917 + 0.005083 + 0.004833 + 0.004917 + 0.004959 + 0.005 + 0.004834 + 0.0045 + 0.004459 + 0.004708 + 0.004958 + 0.004958 + 0.004875 + 0.00475 + 0.004709 + 0.004666 + 0.004792 + 0.00525 + 0.004625 + 0.004167 + 0.004583 + 0.00475 + 0.003958 + 0.004834 + 0.004959 + 0.004625 + 0.005084 + 0.00475 + 0.004584 + 0.0065 + 0.006125 + 0.005958 + 0.007166 + 0.006708 + 0.005542 + 0.005833 + 0.00575 + 0.007167 + 0.007417 + 0.006041 + 0.006583 + 0.005958 + 0.00525 + 0.005375 + 0.005208 + 0.00525 + 0.005084 + 0.005042 + 0.004958 + 0.004875 + 0.005166 + 0.005084 + 0.004791 + 0.004667 + 0.004625 + 0.006125 + 0.006084 + 0.006375 + 0.007125 + 0.006667 + 0.005834 + 0.005416 + 0.005542 + 0.007125 + 0.007125 + 0.006833 + 0.005958 + 0.007166 + 0.005583 + 0.006042 + 0.007208 + 0.007375 + 0.007167 + 0.006958 + 0.005167 + 0.004792 + 0.004333 + 0.004666 + 0.004834 + 0.004666 + 0.0045 + 0.0055 + 0.005041 + 0.005042 + 0.004334 + 0.004875 + 0.005375 + 0.005042 + 0.004583 + 0.004625 + 0.00475 + 0.004666 + 0.005 + 0.004875 + 0.00475 + 0.004666 + 0.004583 + 0.00475 + 0.004208 + 0.004125 + 0.004709 + 0.005083 + 0.004791 + 0.004417 + 0.004959 + 0.004709 + 0.004959 + 0.004625 + 0.00475 + 0.00475 + 0.004333 + 0.004833 + 0.004583 + 0.004792 + 0.004459 + 0.005042 + 0.004791 + 0.004958 + 0.005042 + 0.004708 + 0.004916 + 0.004333 + 0.004916 + 0.005208 + 0.004833 + 0.004833 + 0.004708 + 0.004625 + 0.004458 + 0.004875 + 0.006666 + 0.00675 + 0.007334 + 0.006792 + 0.006458 + 0.006041 + 0.005667 + 0.007041 + 0.007333 + 0.006083 + 0.006 + 0.006041 + 0.005042 + 0.005166 + 0.005125 + 0.005 + 0.005208 + 0.005167 + 0.005208 + 0.00475 + 0.005208 + 0.004625 + 0.005167 + 0.005125 + 0.006 + 0.007208 + 0.005 + 0.005084 + 0.005042 + 0.006333 + 0.005541 + 0.005625 + 0.005791 + 0.005709 + 0.007584 + 0.007125 + 0.006417 + 0.00725 + 0.006459 + 0.006167 + 0.00575 + 0.007959 + 0.007291 + 0.007375 + 0.006625 + 0.005292 + 0.004541 + 0.004708 + 0.004833 + 0.004583 + 0.005167 + 0.005208 + 0.004833 + 0.004458 + 0.005083 + 0.0045 + 0.004917 + 0.004958 + 0.004708 + 0.005208 + 0.004833 + 0.004833 + 0.004583 + 0.004791 + 0.005 + 0.004625 + 0.0045 + 0.004208 + 0.004625 + 0.004292 + 0.004666 + 0.004709 + 0.004625 + 0.01375 + 0.005291 + 0.004958 + 0.00475 + 0.005125 + 0.004958 + 0.005375 + 0.005541 + 0.005375 + 0.004625 + 0.004916 + 0.004375 + 0.005791 + 0.005917 + 0.007084 + 0.005959 + 0.006084 + 0.006125 + 0.006 + 0.005792 + 0.006208 + 0.006125 + 0.006375 + 0.005792 + 0.005667 + 0.005583 + 0.004583 + 0.006333 + 0.005708 + 0.007292 + 0.007042 + 0.006666 + 0.005375 + 0.005458 + 0.007667 + 0.00725 + 0.006708 + 0.006459 + 0.006042 + 0.006083 + 0.005833 + 0.005125 + 0.005084 + 0.005042 + 0.005166 + 0.005375 + 0.005 + 0.006208 + 0.006666 + 0.008875 + 0.006 + 0.005 + 0.005375 + 0.005375 + 0.005833 + 0.005 + 0.006416 + 0.006083 + 0.006083 + 0.005625 + 0.008083 + 0.008 + 0.007625 + 0.006542 + 0.007584 + 0.007209 + 0.007167 + 0.00725 + 0.008334 + 0.007375 + 0.007459 + 0.007417 + 0.006292 + 0.005667 + 0.005 + 0.004625 + 0.004292 + 0.004625 + 0.005 + 0.005083 + 0.004583 + 0.005 + 0.00475 + 0.005166 + 0.004792 + 0.005333 + 0.004708 + 0.004875 + 0.004833 + 0.004875 + 0.004875 + 0.004875 + 0.004625 + 0.00475 + 0.0045 + 0.004666 + 0.004458 + 0.005083 + 0.004542 + 0.00425 + 0.004458 + 0.004708 + 0.005042 + 0.004833 + 0.004917 + 0.004792 + 0.004625 + 0.004167 + 0.004542 + 0.004834 + 0.004375 + 0.004625 + 0.005083 + 0.005083 + 0.004917 + 0.004541 + 0.00475 + 0.004625 + 0.00475 + 0.004583 + 0.0055 + 0.005167 + 0.005375 + 0.004875 + 0.007292 + 0.006583 + 0.006042 + 0.006042 + 0.0065 + 0.007125 + 0.007208 + 0.006875 + 0.005792 + 0.007833 + 0.0075 + 0.007375 + 0.008 + 0.006916 + 0.006959 + 0.007125 + 0.005334 + 0.005042 + 0.005375 + 0.0055 + 0.005292 + 0.007167 + 0.006417 + 0.006916 + 0.0065 + 0.006916 + 0.006583 + 0.006708 + 0.005125 + 0.005 + 0.0045 + 0.005 + 0.0055 + 0.005833 + 0.007833 + 0.007458 + 0.007625 + 0.007084 + 0.007042 + 0.007166 + 0.006417 + 0.00825 + 0.007542 + 0.007084 + 0.00775 + 0.007375 + 0.007709 + 0.009375 + 0.006334 + 0.004917 + 0.005 + 0.004584 + 0.004541 + 0.004625 + 0.004708 + 0.005125 + 0.005541 + 0.004834 + 0.004625 + 0.004792 + 0.005291 + 0.005042 + 0.004792 + 0.004791 + 0.00475 + 0.004708 + 0.004958 + 0.00475 + 0.005 + 0.005 + 0.004667 + 0.004875 + 0.0045 + 0.004542 + 0.004625 + 0.004917 + 0.004833 + 0.004625 + 0.005 + 0.004791 + 0.004459 + 0.004416 + 0.004542 + 0.004709 + 0.004583 + 0.004958 + 0.004917 + 0.004625 + 0.004375 + 0.004708 + 0.00525 + 0.004833 + 0.004833 + 0.005333 + 0.006292 + 0.006208 + 0.006334 + 0.006958 + 0.006833 + 0.006458 + 0.005708 + 0.005125 + 0.005167 + 0.004792 + 0.004709 + 0.005625 + 0.006 + 0.005459 + 0.007875 + 0.008208 + 0.007458 + 0.007208 + 0.007666 + 0.006708 + 0.006208 + 0.005291 + 0.00525 + 0.005333 + 0.00475 + 0.005166 + 0.007 + 0.006833 + 0.006916 + 0.006917 + 0.006166 + 0.0065 + 0.00625 + 0.005584 + 0.004834 + 0.004959 + 0.004916 + 0.005167 + 0.005541 + 0.006 + 0.007667 + 0.007084 + 0.007584 + 0.007292 + 0.007583 + 0.007583 + 0.006541 + 0.007542 + 0.007292 + 0.007875 + 0.00725 + 0.007042 + 0.007209 + 0.008459 + 0.006625 + 0.006 + 0.006209 + 0.006375 + 0.005542 + 0.007958 + 0.005375 + 0.005792 + 0.0055 + 0.004416 + 0.004417 + 0.005416 + 0.005083 + 0.004875 + 0.004625 + 0.004667 + 0.005084 + 0.004792 + 0.004792 + 0.005 + 0.004792 + 0.004583 + 0.004792 + 0.004666 + 0.004792 + 0.004833 + 0.004583 + 0.00475 + 0.004834 + 0.004542 + 0.005 + 0.004541 + 0.004459 + 0.004666 + 0.004667 + 0.005 + 0.004791 + 0.004917 + 0.004459 + 0.0045 + 0.004667 + 0.00475 + 0.005209 + 0.005083 + 0.004917 + 0.004459 + 0.004833 + 0.004916 + 0.004916 + 0.004917 + 0.005 + 0.005042 + 0.005084 + 0.004959 + 0.004917 + 0.005 + 0.00525 + 0.005208 + 0.006208 + 0.005917 + 0.007709 + 0.007584 + 0.007334 + 0.007584 + 0.007792 + 0.006791 + 0.006167 + 0.004917 + 0.005083 + 0.005333 + 0.0055 + 0.006917 + 0.006708 + 0.00625 + 0.0065 + 0.006208 + 0.00675 + 0.006375 + 0.006166 + 0.004917 + 0.005167 + 0.005125 + 0.005125 + 0.004875 + 0.004666 + 0.005917 + 0.005708 + 0.007667 + 0.007667 + 0.007125 + 0.006917 + 0.007459 + 0.006292 + 0.007125 + 0.008583 + 0.019166 + 0.018167 + 0.006917 + 0.006666 + 0.006083 + 0.007625 + 0.006375 + 0.005291 + 0.005083 + 0.005208 + 0.004875 + 0.004458 + 0.004959 + 0.005292 + 0.004458 + 0.004458 + 0.005 + 0.00525 + 0.004833 + 0.005083 + 0.004541 + 0.004875 + 0.004792 + 0.004625 + 0.004792 + 0.004875 + 0.004875 + 0.005375 + 0.005333 + 0.006167 + 0.006125 + 0.005709 + 0.005666 + 0.005167 + 0.004708 + 0.004667 + 0.004541 + 0.005167 + 0.004625 + 0.004833 + 0.004625 + 0.004708 + 0.004667 + 0.006208 + 0.004667 + 0.004875 + 0.005208 + 0.005 + 0.004833 + 0.005 + 0.004834 + 0.005042 + 0.004917 + 0.005291 + 0.005417 + 0.006125 + 0.005167 + 0.005541 + 0.005708 + 0.005667 + 0.005375 + 0.005709 + 0.004958 + 0.005875 + 0.006667 + 0.009541 + 0.00775 + 0.007 + 0.007334 + 0.006334 + 0.006917 + 0.005833 + 0.005416 + 0.008 + 0.007416 + 0.007583 + 0.0065 + 0.00625 + 0.006125 + 0.00625 + 0.006542 + 0.005709 + 0.005792 + 0.005791 + 0.005834 + 0.005667 + 0.005875 + 0.005084 + 0.005416 + 0.005208 + 0.0075 + 0.008625 + 0.008 + 0.00775 + 0.007916 + 0.007916 + 0.029791 + 0.008833 + 0.012125 + 0.0095 + 0.008792 + 0.007166 + 0.007125 + 0.007166 + 0.009042 + 0.007375 + 0.00775 + 0.005958 + 0.006083 + 0.00625 + 0.006334 + 0.006542 + 0.006333 + 0.006833 + 0.007958 + 0.006958 + 0.006416 + 0.006167 + 0.009625 + 0.006167 + 0.006584 + 0.006167 + 0.005959 + 0.006333 + 0.006416 + 0.00775 + 0.007958 + 0.006458 + 0.006125 + 0.006291 + 0.006792 + 0.006667 + 0.006708 + 0.006458 + 0.006208 + 0.006417 + 0.006417 + 0.005666 + 0.006542 + 0.007375 + 0.006375 + 0.005875 + 0.0065 + 0.00675 + 0.00625 + 0.006333 + 0.006167 + 0.006 + 0.006291 + 0.006833 + 0.006166 + 0.005833 + 0.006375 + 0.00575 + 0.007834 + 0.006291 + 0.006 + 0.006542 + 0.006 + 0.006042 + 0.006709 + 0.007791 + 0.007625 + 0.011916 + 0.01025 + 0.008458 + 0.006833 + 0.007083 + 0.00675 + 0.007 + 0.008666 + 0.01025 + 0.007083 + 0.008666 + 0.007417 + 0.007792 + 0.006792 + 0.005667 + 0.005541 + 0.006292 + 0.007083 + 0.007334 + 0.006084 + 0.006375 + 0.005792 + 0.006083 + 0.006125 + 0.005958 + 0.006083 + 0.007958 + 0.008208 + 0.008167 + 0.008042 + 0.007666 + 0.00675 + 0.006417 + 0.008916 + 0.008042 + 0.006375 + 0.006834 + 0.006666 + 0.006084 + 0.006417 + 0.010541 + 0.00775 + 0.007541 + 0.006792 + 0.005375 + 0.005 + 0.005083 + 0.005125 + 0.005375 + 0.005583 + 0.005167 + 0.005375 + 0.005584 + 0.005166 + 0.005375 + 0.004958 + 0.005417 + 0.004916 + 0.005167 + 0.005583 + 0.005542 + 0.00575 + 0.005625 + 0.00525 + 0.005459 + 0.005167 + 0.005292 + 0.005792 + 0.005666 + 0.005584 + 0.005667 + 0.00525 + 0.005208 + 0.005083 + 0.004959 + 0.005625 + 0.005833 + 0.005334 + 0.004625 + 0.004958 + 0.005375 + 0.005125 + 0.005333 + 0.005833 + 0.005375 + 0.005458 + 0.005125 + 0.005375 + 0.005459 + 0.005 + 0.005334 + 0.006125 + 0.005667 + 0.006292 + 0.005583 + 0.005542 + 0.005416 + 0.005542 + 0.006084 + 0.005875 + 0.006209 + 0.008375 + 0.007667 + 0.007791 + 0.007125 + 0.006583 + 0.0065 + 0.00825 + 0.006875 + 0.006667 + 0.006625 + 0.006084 + 0.005291 + 0.005584 + 0.005792 + 0.005625 + 0.00625 + 0.005667 + 0.005708 + 0.005708 + 0.005375 + 0.006125 + 0.00575 + 0.005709 + 0.006041 + 0.005667 + 0.005917 + 0.006959 + 0.007667 + 0.007625 + 0.007458 + 0.007666 + 0.007417 + 0.007458 + 0.007666 + 0.006667 + 0.0075 + 0.006917 + 0.006708 + 0.006958 + 0.00775 + 0.0075 + 0.007542 + 0.006333 + 0.005542 + 0.005666 + 0.0055 + 0.005625 + 0.006 + 0.005625 + 0.006167 + 0.00625 + 0.004583 + 0.004583 + 0.0055 + 0.005 + 0.005417 + 0.005542 + 0.005625 + 0.004958 + 0.005042 + 0.005291 + 0.005167 + 0.004833 + 0.005416 + 0.0055 + 0.0055 + 0.005208 + 0.005 + 0.005208 + 0.005208 + 0.004916 + 0.005625 + 0.005708 + 0.00575 + 0.00525 + 0.005334 + 0.004875 + 0.00525 + 0.00525 + 0.005666 + 0.006375 + 0.006125 + 0.005959 + 0.005042 + 0.005333 + 0.00525 + 0.005333 + 0.005417 + 0.005584 + 0.005209 + 0.005334 + 0.005666 + 0.005667 + 0.005292 + 0.005625 + 0.006041 + 0.005458 + 0.005417 + 0.005166 + 0.005458 + 0.00825 + 0.007625 + 0.007542 + 0.007791 + 0.006625 + 0.006125 + 0.010916 + 0.006792 + 0.006208 + 0.006333 + 0.006375 + 0.006166 + 0.005708 + 0.005583 + 0.005417 + 0.005709 + 0.005167 + 0.005125 + 0.006291 + 0.005959 + 0.005666 + 0.004916 + 0.005292 + 0.005333 + 0.005875 + 0.005958 + 0.01625 + 0.008041 + 0.007667 + 0.007584 + 0.0075 + 0.007167 + 0.00725 + 0.008208 + 0.007333 + 0.008458 + 0.00775 + 0.008125 + 0.007917 + 0.00675 + 0.007542 + 0.007958 + 0.007625 + 0.005958 + 0.005416 + 0.00525 + 0.005208 + 0.004542 + 0.005041 + 0.004917 + 0.005292 + 0.005375 + 0.005291 + 0.005 + 0.005791 + 0.005292 + 0.004958 + 0.005042 + 0.005625 + 0.006417 + 0.00625 + 0.007041 + 0.0065 + 0.006541 + 0.006667 + 0.006333 + 0.006625 + 0.005834 + 0.005375 + 0.005375 + 0.005291 + 0.005125 + 0.005083 + 0.004917 + 0.004833 + 0.005042 + 0.004584 + 0.005 + 0.004833 + 0.00475 + 0.005375 + 0.004666 + 0.005208 + 0.005042 + 0.005375 + 0.004833 + 0.005166 + 0.004958 + 0.005042 + 0.005208 + 0.0055 + 0.005291 + 0.005792 + 0.005417 + 0.005333 + 0.0055 + 0.0055 + 0.0055 + 0.004834 + 0.005291 + 0.011292 + 0.008125 + 0.007791 + 0.007875 + 0.00775 + 0.007542 + 0.00825 + 0.008 + 0.006958 + 0.006916 + 0.00675 + 0.0065 + 0.006584 + 0.005417 + 0.005375 + 0.005625 + 0.005416 + 0.0055 + 0.005334 + 0.005625 + 0.005292 + 0.005458 + 0.005208 + 0.005792 + 0.005416 + 0.005583 + 0.005417 + 0.007958 + 0.008208 + 0.007958 + 0.007875 + 0.007834 + 0.008209 + 0.008833 + 0.006583 + 0.005166 + 0.007625 + 0.0065 + 0.006 + 0.006166 + 0.006417 + 0.00775 + 0.008417 + 0.006333 + 0.006041 + 0.006417 + 0.006625 + 0.006708 + 0.005375 + 0.005708 + 0.005541 + 0.005584 + 0.004917 + 0.004625 + 0.005167 + 0.004667 + 0.004834 + 0.005083 + 0.005125 + 0.004541 + 0.004958 + 0.005042 + 0.004708 + 0.0055 + 0.00525 + 0.005125 + 0.005 + 0.004792 + 0.005166 + 0.005125 + 0.004833 + 0.005166 + 0.004583 + 0.004834 + 0.004625 + 0.004875 + 0.004875 + 0.004709 + 0.004458 + 0.004833 + 0.0045 + 0.004375 + 0.004291 + 0.004625 + 0.004917 + 0.005208 + 0.00475 + 0.004917 + 0.005084 + 0.004833 + 0.005167 + 0.0055 + 0.005209 + 0.005125 + 0.004792 + 0.005542 + 0.004875 + 0.005167 + 0.005125 + 0.005292 + 0.006 + 0.0105 + 0.007666 + 0.007792 + 0.007334 + 0.007709 + 0.00775 + 0.008583 + 0.00675 + 0.006333 + 0.006291 + 0.00625 + 0.006208 + 0.006541 + 0.005584 + 0.005041 + 0.005292 + 0.005417 + 0.005083 + 0.005208 + 0.005875 + 0.006666 + 0.006334 + 0.006209 + 0.006375 + 0.006375 + 0.007166 + 0.008417 + 0.007875 + 0.007292 + 0.008417 + 0.007333 + 0.008125 + 0.008959 + 0.008125 + 0.006333 + 0.008125 + 0.007375 + 0.007583 + 0.0065 + 0.009416 + 0.007417 + 0.007666 + 0.00625 + 0.006291 + 0.006166 + 0.00625 + 0.005083 + 0.004792 + 0.00475 + 0.004708 + 0.005125 + 0.005167 + 0.004625 + 0.004833 + 0.005417 + 0.004417 + 0.00475 + 0.004583 + 0.005084 + 0.004958 + 0.004833 + 0.004916 + 0.004709 + 0.005125 + 0.005167 + 0.004958 + 0.00475 + 0.004834 + 0.004959 + 0.005209 + 0.005083 + 0.004917 + 0.005 + 0.00475 + 0.004666 + 0.004875 + 0.004958 + 0.004958 + 0.004542 + 0.004333 + 0.004875 + 0.004417 + 0.00475 + 0.004833 + 0.005 + 0.00475 + 0.005084 + 0.005 + 0.004709 + 0.004959 + 0.005083 + 0.00525 + 0.005625 + 0.005416 + 0.004959 + 0.005 + 0.004875 + 0.004958 + 0.005333 + 0.005209 + 0.011084 + 0.007542 + 0.007541 + 0.0075 + 0.007583 + 0.006958 + 0.0085 + 0.006958 + 0.006375 + 0.006542 + 0.006292 + 0.006459 + 0.00625 + 0.006292 + 0.005 + 0.004834 + 0.00525 + 0.00525 + 0.005 + 0.005292 + 0.005167 + 0.004958 + 0.005625 + 0.005041 + 0.005083 + 0.005458 + 0.0075 + 0.007917 + 0.007917 + 0.007791 + 0.007833 + 0.007834 + 0.008042 + 0.007792 + 0.006542 + 0.013791 + 0.007875 + 0.007417 + 0.005667 + 0.007417 + 0.007458 + 0.007667 + 0.008625 + 0.00675 + 0.00625 + 0.006083 + 0.005 + 0.005 + 0.004709 + 0.005083 + 0.005375 + 0.004875 + 0.004916 + 0.004917 + 0.005166 + 0.004709 + 0.004625 + 0.004375 + 0.00475 + 0.004792 + 0.004792 + 0.005083 + 0.006291 + 0.00725 + 0.006875 + 0.006667 + 0.0065 + 0.006666 + 0.00625 + 0.006084 + 0.006584 + 0.006625 + 0.005834 + 0.004666 + 0.00475 + 0.004833 + 0.004625 + 0.004917 + 0.004875 + 0.004583 + 0.004625 + 0.0045 + 0.004375 + 0.004458 + 0.005 + 0.004708 + 0.004292 + 0.004583 + 0.005041 + 0.004667 + 0.005583 + 0.005416 + 0.004875 + 0.004583 + 0.005166 + 0.004875 + 0.005625 + 0.006167 + 0.007625 + 0.0075 + 0.0085 + 0.007333 + 0.007666 + 0.007292 + 0.007209 + 0.008459 + 0.008209 + 0.007083 + 0.006291 + 0.00775 + 0.00675 + 0.006666 + 0.005625 + 0.005 + 0.004709 + 0.004916 + 0.00525 + 0.005292 + 0.005209 + 0.0055 + 0.005459 + 0.005333 + 0.005083 + 0.00675 + 0.006791 + 0.006625 + 0.005791 + 0.006917 + 0.007334 + 0.007291 + 0.007208 + 0.006042 + 0.007916 + 0.00775 + 0.006375 + 0.00875 + 0.007 + 0.008125 + 0.006542 + 0.008292 + 0.007583 + 0.008 + 0.007458 + 0.0065 + 0.005458 + 0.005208 + 0.005 + 0.005292 + 0.005792 + 0.00525 + 0.006041 + 0.00575 + 0.006708 + 0.00575 + 0.005417 + 0.005167 + 0.005417 + 0.006 + 0.005084 + 0.006125 + 0.005833 + 0.005166 + 0.005125 + 0.004958 + 0.005917 + 0.005708 + 0.005166 + 0.005458 + 0.005458 + 0.005792 + 0.00525 + 0.00525 + 0.005917 + 0.006042 + 0.006875 + 0.007292 + 0.006791 + 0.005542 + 0.005292 + 0.005708 + 0.0055 + 0.005667 + 0.005708 + 0.005417 + 0.005375 + 0.005417 + 0.00475 + 0.005333 + 0.005042 + 0.004917 + 0.005708 + 0.005625 + 0.005333 + 0.005292 + 0.0055 + 0.006041 + 0.004792 + 0.005083 + 0.009916 + 0.006667 + 0.007208 + 0.007125 + 0.006583 + 0.005959 + 0.005459 + 0.007375 + 0.007334 + 0.007459 + 0.00625 + 0.005542 + 0.005834 + 0.005292 + 0.004791 + 0.004667 + 0.005084 + 0.014458 + 0.005291 + 0.005667 + 0.005125 + 0.004833 + 0.006667 + 0.006667 + 0.005916 + 0.006083 + 0.006042 + 0.005709 + 0.004959 + 0.006292 + 0.005708 + 0.006417 + 0.006084 + 0.007084 + 0.007125 + 0.007708 + 0.006208 + 0.005375 + 0.005875 + 0.00625 + 0.005792 + 0.007333 + 0.007 + 0.007166 + 0.006708 + 0.005667 + 0.004958 + 0.004916 + 0.004917 + 0.005 + 0.005209 + 0.004875 + 0.004792 + 0.005458 + 0.005125 + 0.005333 + 0.005167 + 0.005209 + 0.005125 + 0.005292 + 0.004583 + 0.0045 + 0.004833 + 0.004875 + 0.004584 + 0.004667 + 0.00475 + 0.004667 + 0.004708 + 0.00475 + 0.005167 + 0.005 + 0.005042 + 0.004959 + 0.004542 + 0.005 + 0.004791 + 0.005292 + 0.004708 + 0.004708 + 0.004708 + 0.004583 + 0.004708 + 0.004916 + 0.004875 + 0.004875 + 0.004833 + 0.004666 + 0.004792 + 0.004458 + 0.004458 + 0.004833 + 0.004875 + 0.005416 + 0.005417 + 0.005166 + 0.004792 + 0.004833 + 0.004875 + 0.005042 + 0.00625 + 0.006166 + 0.005875 + 0.007042 + 0.007 + 0.005375 + 0.006208 + 0.006208 + 0.007083 + 0.007167 + 0.006 + 0.005792 + 0.006125 + 0.004708 + 0.004958 + 0.005167 + 0.005292 + 0.005 + 0.005 + 0.005125 + 0.004708 + 0.004708 + 0.005042 + 0.005083 + 0.00475 + 0.004916 + 0.006208 + 0.006375 + 0.006208 + 0.007291 + 0.006791 + 0.005917 + 0.005666 + 0.005709 + 0.007291 + 0.007167 + 0.007125 + 0.006208 + 0.00675 + 0.006041 + 0.005792 + 0.007083 + 0.006458 + 0.00675 + 0.006875 + 0.005792 + 0.004958 + 0.004583 + 0.004958 + 0.004833 + 0.004583 + 0.004417 + 0.004334 + 0.005625 + 0.004583 + 0.004709 + 0.00525 + 0.005209 + 0.004917 + 0.00475 + 0.004416 + 0.004667 + 0.004667 + 0.004791 + 0.004583 + 0.0045 + 0.004583 + 0.004583 + 0.004375 + 0.004875 + 0.004666 + 0.005 + 0.004792 + 0.005083 + 0.005 + 0.005291 + 0.0045 + 0.004875 + 0.004958 + 0.005334 + 0.004834 + 0.004667 + 0.005 + 0.004584 + 0.004584 + 0.004834 + 0.004958 + 0.004958 + 0.0055 + 0.004959 + 0.005291 + 0.0055 + 0.0055 + 0.007166 + 0.006125 + 0.004917 + 0.005125 + 0.005375 + 0.004625 + 0.004833 + 0.005292 + 0.005375 + 0.005208 + 0.010792 + 0.007166 + 0.007292 + 0.007333 + 0.006708 + 0.006583 + 0.007375 + 0.006625 + 0.006375 + 0.006708 + 0.005917 + 0.005333 + 0.005583 + 0.005417 + 0.005291 + 0.005417 + 0.005583 + 0.005375 + 0.004958 + 0.004875 + 0.005375 + 0.004916 + 0.004792 + 0.005083 + 0.005709 + 0.005791 + 0.004542 + 0.007084 + 0.007292 + 0.006666 + 0.007375 + 0.013416 + 0.006333 + 0.005542 + 0.004917 + 0.008417 + 0.007375 + 0.007375 + 0.007375 + 0.007583 + 0.00775 + 0.008375 + 0.007583 + 0.005667 + 0.004875 + 0.004666 + 0.004584 + 0.004916 + 0.004917 + 0.004625 + 0.005458 + 0.004708 + 0.004834 + 0.005791 + 0.004875 + 0.005 + 0.004667 + 0.004834 + 0.004375 + 0.00475 + 0.004667 + 0.004584 + 0.004375 + 0.004209 + 0.013 + 0.004958 + 0.004709 + 0.0045 + 0.005083 + 0.004917 + 0.004792 + 0.004583 + 0.005375 + 0.004666 + 0.004917 + 0.004459 + 0.004666 + 0.004333 + 0.004417 + 0.004875 + 0.005333 + 0.004875 + 0.004792 + 0.005291 + 0.004667 + 0.004625 + 0.004708 + 0.004666 + 0.004542 + 0.004458 + 0.005209 + 0.005084 + 0.005042 + 0.004541 + 0.004792 + 0.004875 + 0.004875 + 0.005 + 0.004959 + 0.005083 + 0.00575 + 0.007666 + 0.006958 + 0.007125 + 0.006958 + 0.006917 + 0.005542 + 0.007458 + 0.006583 + 0.00625 + 0.006375 + 0.00625 + 0.004791 + 0.005208 + 0.005208 + 0.005458 + 0.004625 + 0.004916 + 0.005 + 0.005458 + 0.004792 + 0.005167 + 0.004875 + 0.00475 + 0.004375 + 0.005292 + 0.005209 + 0.010084 + 0.0085 + 0.007459 + 0.007084 + 0.005834 + 0.006125 + 0.00775 + 0.006458 + 0.007042 + 0.00775 + 0.007958 + 0.0075 + 0.006334 + 0.008125 + 0.006917 + 0.006875 + 0.006667 + 0.006542 + 0.006834 + 0.005917 + 0.005791 + 0.005792 + 0.00575 + 0.006375 + 0.006416 + 0.005542 + 0.005959 + 0.006709 + 0.006708 + 0.005542 + 0.006208 + 0.005291 + 0.006083 + 0.00675 + 0.007334 + 0.007 + 0.006334 + 0.00525 + 0.004875 + 0.004833 + 0.005292 + 0.005708 + 0.005625 + 0.005625 + 0.004834 + 0.005542 + 0.005416 + 0.005333 + 0.005417 + 0.006459 + 0.006333 + 0.006333 + 0.006291 + 0.007125 + 0.007333 + 0.006958 + 0.006417 + 0.006959 + 0.006792 + 0.00625 + 0.004875 + 0.00475 + 0.004959 + 0.004584 + 0.005583 + 0.00625 + 0.006292 + 0.007166 + 0.006625 + 0.006666 + 0.00625 + 0.006625 + 0.006292 + 0.010166 + 0.008792 + 0.007292 + 0.006958 + 0.007 + 0.006834 + 0.0085 + 0.007834 + 0.007417 + 0.007208 + 0.007583 + 0.006333 + 0.005708 + 0.005542 + 0.005292 + 0.005542 + 0.006 + 0.006 + 0.005542 + 0.005666 + 0.0055 + 0.005542 + 0.006417 + 0.007791 + 0.007042 + 0.0135 + 0.006875 + 0.008416 + 0.007792 + 0.0125 + 0.013625 + 0.007792 + 0.009084 + 0.008084 + 0.007542 + 0.006708 + 0.007875 + 0.007917 + 0.008541 + 0.022666 + 0.012375 + 0.009958 + 0.010333 + 0.007625 + 0.007125 + 0.004875 + 0.006084 + 0.006125 + 0.006625 + 0.006417 + 0.00525 + 0.006583 + 0.006666 + 0.006292 + 0.006 + 0.006125 + 0.005958 + 0.006708 + 0.00625 + 0.005833 + 0.006333 + 0.005417 + 0.004584 + 0.004875 + 0.005167 + 0.004791 + 0.004875 + 0.004791 + 0.004708 + 0.004959 + 0.005083 + 0.005083 + 0.005 + 0.005042 + 0.00475 + 0.005542 + 0.005417 + 0.005458 + 0.005125 + 0.00475 + 0.0055 + 0.004666 + 0.005 + 0.004833 + 0.005 + 0.005041 + 0.005 + 0.005333 + 0.004708 + 0.005125 + 0.005083 + 0.005083 + 0.0055 + 0.005917 + 0.005583 + 0.005375 + 0.004625 + 0.005375 + 0.009625 + 0.005917 + 0.006459 + 0.007042 + 0.006625 + 0.006166 + 0.005708 + 0.005792 + 0.007208 + 0.007208 + 0.006708 + 0.006417 + 0.006125 + 0.004917 + 0.013583 + 0.005166 + 0.005625 + 0.005375 + 0.005875 + 0.006375 + 0.006541 + 0.005542 + 0.006416 + 0.007375 + 0.006875 + 0.006459 + 0.007167 + 0.007083 + 0.00675 + 0.006667 + 0.006166 + 0.005625 + 0.005625 + 0.006542 + 0.007458 + 0.007542 + 0.007459 + 0.006459 + 0.007 + 0.00725 + 0.006708 + 0.008042 + 0.008125 + 0.007709 + 0.007833 + 0.006416 + 0.00625 + 0.006083 + 0.004834 + 0.005084 + 0.005042 + 0.004875 + 0.006083 + 0.006 + 0.005625 + 0.005334 + 0.0055 + 0.0055 + 0.005208 + 0.005 + 0.005125 + 0.00525 + 0.004958 + 0.005208 + 0.005125 + 0.005125 + 0.005458 + 0.005125 + 0.004958 + 0.005125 + 0.005041 + 0.004708 + 0.004917 + 0.005333 + 0.004625 + 0.004791 + 0.005083 + 0.005042 + 0.005125 + 0.005 + 0.004833 + 0.00475 + 0.005292 + 0.005 + 0.005083 + 0.005375 + 0.005 + 0.005042 + 0.005083 + 0.004833 + 0.005333 + 0.005209 + 0.004959 + 0.005625 + 0.005625 + 0.005417 + 0.005041 + 0.005083 + 0.00525 + 0.00625 + 0.006416 + 0.007667 + 0.006875 + 0.006625 + 0.006417 + 0.006208 + 0.005875 + 0.006041 + 0.008 + 0.007958 + 0.007791 + 0.007333 + 0.006917 + 0.006792 + 0.006541 + 0.005792 + 0.00575 + 0.005459 + 0.005375 + 0.005167 + 0.005833 + 0.005583 + 0.005416 + 0.005542 + 0.00575 + 0.005334 + 0.012584 + 0.006875 + 0.007 + 0.006417 + 0.007584 + 0.008042 + 0.007416 + 0.006292 + 0.006208 + 0.007625 + 0.008167 + 0.006709 + 0.008209 + 0.007541 + 0.007875 + 0.007125 + 0.007334 + 0.007458 + 0.008042 + 0.007875 + 0.006959 + 0.006209 + 0.005625 + 0.005167 + 0.004958 + 0.004625 + 0.004667 + 0.005167 + 0.005167 + 0.005459 + 0.005292 + 0.005375 + 0.005084 + 0.004625 + 0.005042 + 0.005083 + 0.005 + 0.005083 + 0.005 + 0.005166 + 0.004917 + 0.00525 + 0.004875 + 0.005083 + 0.005708 + 0.004584 + 0.005041 + 0.005458 + 0.004792 + 0.004791 + 0.004625 + 0.004917 + 0.005458 + 0.004792 + 0.004833 + 0.004833 + 0.004792 + 0.005 + 0.005 + 0.004917 + 0.004791 + 0.005208 + 0.00525 + 0.004875 + 0.005125 + 0.004583 + 0.005583 + 0.004667 + 0.005459 + 0.005375 + 0.005541 + 0.005167 + 0.0055 + 0.00525 + 0.005625 + 0.007584 + 0.006584 + 0.007584 + 0.007625 + 0.0075 + 0.008 + 0.007083 + 0.009292 + 0.008084 + 0.007667 + 0.006958 + 0.007041 + 0.006625 + 0.006083 + 0.005375 + 0.005125 + 0.004916 + 0.005083 + 0.0055 + 0.005208 + 0.00525 + 0.00525 + 0.006917 + 0.0065 + 0.006459 + 0.006375 + 0.007208 + 0.007542 + 0.006583 + 0.006834 + 0.005959 + 0.005792 + 0.007167 + 0.008875 + 0.014667 + 0.008084 + 0.006959 + 0.007709 + 0.00775 + 0.008958 + 0.008792 + 0.008667 + 0.007625 + 0.0075 + 0.007125 + 0.006166 + 0.005208 + 0.004667 + 0.004875 + 0.004792 + 0.004584 + 0.004708 + 0.012208 + 0.00525 + 0.005333 + 0.00525 + 0.005834 + 0.005792 + 0.00625 + 0.006709 + 0.00625 + 0.006042 + 0.006625 + 0.006458 + 0.006375 + 0.00575 + 0.005125 + 0.00475 + 0.005042 + 0.004833 + 0.012917 + 0.004834 + 0.004834 + 0.004916 + 0.005458 + 0.005792 + 0.005375 + 0.005084 + 0.004667 + 0.005125 + 0.004958 + 0.004875 + 0.004834 + 0.004792 + 0.004958 + 0.004791 + 0.004959 + 0.005041 + 0.004792 + 0.004708 + 0.004917 + 0.004958 + 0.0045 + 0.005584 + 0.005334 + 0.005084 + 0.00525 + 0.006333 + 0.006083 + 0.005833 + 0.005667 + 0.006333 + 0.006 + 0.005792 + 0.0055 + 0.00625 + 0.007625 + 0.006584 + 0.006959 + 0.006667 + 0.005833 + 0.004833 + 0.005375 + 0.004834 + 0.005209 + 0.005083 + 0.005208 + 0.005041 + 0.007334 + 0.006542 + 0.00625 + 0.006 + 0.006042 + 0.004958 + 0.005333 + 0.005375 + 0.005042 + 0.005291 + 0.00525 + 0.005875 + 0.005959 + 0.005875 + 0.008 + 0.0065 + 0.006541 + 0.006875 + 0.006666 + 0.005125 + 0.00575 + 0.008041 + 0.007375 + 0.007125 + 0.007042 + 0.0065 + 0.006083 + 0.00525 + 0.004833 + 0.006042 + 0.005583 + 0.005791 + 0.004875 + 0.004666 + 0.005167 + 0.004833 + 0.005084 + 0.004625 + 0.005959 + 0.006416 + 0.006375 + 0.006917 + 0.007084 + 0.006083 + 0.005125 + 0.005125 + 0.00525 + 0.005291 + 0.005042 + 0.005 + 0.005334 + 0.0055 + 0.005584 + 0.005875 + 0.005583 + 0.005083 + 0.004625 + 0.004458 + 0.00475 + 0.004334 + 0.004292 + 0.004916 + 0.004709 + 0.004833 + 0.004458 + 0.004917 + 0.005 + 0.004875 + 0.005 + 0.004708 + 0.004917 + 0.00475 + 0.004708 + 0.004958 + 0.004708 + 0.004917 + 0.005334 + 0.007166 + 0.006042 + 0.006125 + 0.006042 + 0.006125 + 0.005167 + 0.005208 + 0.005416 + 0.006 + 0.005416 + 0.005583 + 0.010042 + 0.006833 + 0.006333 + 0.008583 + 0.006166 + 0.00525 + 0.005166 + 0.00475 + 0.005 + 0.005209 + 0.005584 + 0.005083 + 0.012875 + 0.005125 + 0.006875 + 0.006292 + 0.006 + 0.005333 + 0.005 + 0.00625 + 0.007166 + 0.006792 + 0.00675 + 0.007792 + 0.007792 + 0.007167 + 0.00725 + 0.007167 + 0.006708 + 0.006916 + 0.006583 + 0.0055 + 0.005917 + 0.007041 + 0.007417 + 0.007 + 0.007084 + 0.006667 + 0.006 + 0.005208 + 0.004708 + 0.004833 + 0.004958 + 0.004916 + 0.004792 + 0.005417 + 0.005625 + 0.006417 + 0.005541 + 0.007458 + 0.005 + 0.005375 + 0.005375 + 0.00475 + 0.004584 + 0.004959 + 0.004958 + 0.004583 + 0.005208 + 0.005041 + 0.005208 + 0.004834 + 0.00475 + 0.004875 + 0.004875 + 0.005208 + 0.005167 + 0.005125 + 0.004708 + 0.004875 + 0.004833 + 0.004666 + 0.00475 + 0.004708 + 0.00475 + 0.004916 + 0.005125 + 0.005292 + 0.004416 + 0.004875 + 0.00525 + 0.005166 + 0.004958 + 0.004875 + 0.005042 + 0.005208 + 0.005 + 0.005583 + 0.005 + 0.007416 + 0.006458 + 0.006542 + 0.006708 + 0.006833 + 0.006583 + 0.00675 + 0.005584 + 0.006292 + 0.005958 + 0.006 + 0.007833 + 0.0075 + 0.007666 + 0.007375 + 0.006958 + 0.006916 + 0.00675 + 0.005333 + 0.005458 + 0.006167 + 0.007166 + 0.006417 + 0.006792 + 0.007 + 0.006542 + 0.0075 + 0.007083 + 0.007417 + 0.007583 + 0.0065 + 0.006709 + 0.00675 + 0.006167 + 0.005125 + 0.0065 + 0.006417 + 0.006125 + 0.0075 + 0.00775 + 0.007458 + 0.007541 + 0.007375 + 0.0075 + 0.007 + 0.007667 + 0.007709 + 0.0075 + 0.007708 + 0.007625 + 0.007333 + 0.006166 + 0.004959 + 0.0045 + 0.004708 + 0.004875 + 0.005208 + 0.004625 + 0.0055 + 0.004959 + 0.004792 + 0.005 + 0.005041 + 0.00475 + 0.00475 + 0.004708 + 0.004708 + 0.004833 + 0.005 + 0.005125 + 0.004875 + 0.00525 + 0.005292 + 0.005 + 0.004958 + 0.004667 + 0.005042 + 0.004958 + 0.004625 + 0.004916 + 0.005 + 0.005083 + 0.004708 + 0.004583 + 0.004833 + 0.004417 + 0.0045 + 0.004625 + 0.005083 + 0.004708 + 0.004666 + 0.005833 + 0.00625 + 0.0065 + 0.005959 + 0.00625 + 0.006375 + 0.005791 + 0.006292 + 0.005667 + 0.00725 + 0.007208 + 0.006417 + 0.006542 + 0.006541 + 0.006458 + 0.006375 + 0.005666 + 0.006042 + 0.006125 + 0.005583 + 0.007333 + 0.0075 + 0.00775 + 0.007459 + 0.007625 + 0.007125 + 0.006209 + 0.00525 + 0.005167 + 0.004958 + 0.006291 + 0.006458 + 0.006625 + 0.006708 + 0.007166 + 0.006917 + 0.009333 + 0.006375 + 0.005625 + 0.005208 + 0.005875 + 0.005709 + 0.005583 + 0.004917 + 0.006167 + 0.006583 + 0.008375 + 0.007917 + 0.007667 + 0.007833 + 0.00775 + 0.007709 + 0.009833 + 0.008 + 0.007834 + 0.00825 + 0.008209 + 0.008125 + 0.009542 + 0.009125 + 0.006917 + 0.006958 + 0.006542 + 0.0075 + 0.006542 + 0.006916 + 0.026083 + 0.015917 + 0.008833 + 0.005583 + 0.006458 + 0.013125 + 0.005958 + 0.006 + 0.006125 + 0.005708 + 0.005791 + 0.005834 + 0.005667 + 0.00525 + 0.00525 + 0.00525 + 0.0055 + 0.005542 + 0.005375 + 0.005833 + 0.005167 + 0.011792 + 0.005916 + 0.005708 + 0.006959 + 0.006167 + 0.005041 + 0.005583 + 0.005542 + 0.005166 + 0.005375 + 0.005375 + 0.005416 + 0.005292 + 0.004875 + 0.005208 + 0.0055 + 0.005 + 0.005042 + 0.005583 + 0.005 + 0.005458 + 0.007458 + 0.005958 + 0.005958 + 0.005917 + 0.0055 + 0.005583 + 0.005875 + 0.006084 + 0.005042 + 0.006875 + 0.009375 + 0.01125 + 0.008 + 0.007292 + 0.007333 + 0.005958 + 0.005875 + 0.005583 + 0.005 + 0.005459 + 0.005125 + 0.007042 + 0.006625 + 0.006375 + 0.006375 + 0.006042 + 0.005541 + 0.005792 + 0.00525 + 0.006042 + 0.005625 + 0.005042 + 0.005417 + 0.005833 + 0.005292 + 0.005709 + 0.006459 + 0.010375 + 0.006042 + 0.0055 + 0.005042 + 0.004875 + 0.006625 + 0.006958 + 0.005875 + 0.006667 + 0.006666 + 0.006208 + 0.006208 + 0.00675 + 0.007625 + 0.00775 + 0.006375 + 0.005833 + 0.005875 + 0.005542 + 0.0055 + 0.005792 + 0.006375 + 0.0065 + 0.006667 + 0.005375 + 0.005375 + 0.006042 + 0.006417 + 0.005458 + 0.004166 + 0.004833 + 0.005208 + 0.004167 + 0.004166 + 0.00425 + 0.004292 + 0.0045 + 0.004583 + 0.004625 + 0.003875 + 0.004167 + 0.004083 + 0.00425 + 0.004875 + 0.004625 + 0.004625 + 0.004292 + 0.004 + 0.004375 + 0.004083 + 0.004292 + 0.0045 + 0.003917 + 0.003875 + 0.004291 + 0.00425 + 0.004958 + 0.005792 + 0.006125 + 0.005083 + 0.004542 + 0.004333 + 0.004041 + 0.005042 + 0.0045 + 0.0045 + 0.004708 + 0.004333 + 0.004292 + 0.004875 + 0.005167 + 0.006334 + 0.006667 + 0.009416 + 0.008375 + 0.007791 + 0.007125 + 0.007292 + 0.006917 + 0.008916 + 0.006583 + 0.007417 + 0.006667 + 0.008041 + 0.005042 + 0.004875 + 0.005792 + 0.006959 + 0.006334 + 0.006292 + 0.005958 + 0.005333 + 0.004792 + 0.004959 + 0.004917 + 0.011166 + 0.007583 + 0.007 + 0.007417 + 0.011458 + 0.008375 + 0.007833 + 0.006916 + 0.007125 + 0.007041 + 0.008542 + 0.008625 + 0.007209 + 0.008209 + 0.008 + 0.008625 + 0.008166 + 0.009708 + 0.008167 + 0.007792 + 0.006458 + 0.005916 + 0.005875 + 0.005541 + 0.005917 + 0.006041 + 0.006417 + 0.008 + 0.007709 + 0.006292 + 0.00625 + 0.0075 + 0.007167 + 0.006125 + 0.005292 + 0.00525 + 0.005417 + 0.005125 + 0.005292 + 0.00525 + 0.005333 + 0.005417 + 0.005709 + 0.005166 + 0.005292 + 0.00475 + 0.0055 + 0.005292 + 0.005458 + 0.005416 + 0.005542 + 0.005791 + 0.005375 + 0.005166 + 0.005167 + 0.00525 + 0.005041 + 0.005125 + 0.006333 + 0.005708 + 0.005458 + 0.005375 + 0.005916 + 0.005333 + 0.005542 + 0.005792 + 0.00525 + 0.005625 + 0.005208 + 0.005667 + 0.005584 + 0.005542 + 0.005458 + 0.006083 + 0.0055 + 0.0055 + 0.005584 + 0.005708 + 0.009083 + 0.008208 + 0.008083 + 0.008083 + 0.007667 + 0.007792 + 0.008541 + 0.007292 + 0.006792 + 0.006834 + 0.006834 + 0.006875 + 0.007333 + 0.006375 + 0.0055 + 0.005459 + 0.005042 + 0.005333 + 0.005625 + 0.005583 + 0.00525 + 0.005416 + 0.005542 + 0.005458 + 0.005625 + 0.005833 + 0.005041 + 0.008625 + 0.008334 + 0.007625 + 0.0085 + 0.007708 + 0.007833 + 0.008583 + 0.007 + 0.007875 + 0.007917 + 0.008375 + 0.008042 + 0.006917 + 0.007541 + 0.008542 + 0.008167 + 0.006625 + 0.006667 + 0.006708 + 0.006291 + 0.005208 + 0.004958 + 0.005167 + 0.005459 + 0.005416 + 0.004709 + 0.004958 + 0.005416 + 0.005667 + 0.005209 + 0.004875 + 0.005125 + 0.005417 + 0.005458 + 0.005583 + 0.005417 + 0.006084 + 0.006542 + 0.006708 + 0.006708 + 0.007042 + 0.006584 + 0.006667 + 0.0065 + 0.006167 + 0.006792 + 0.005833 + 0.005083 + 0.005416 + 0.005125 + 0.005 + 0.005125 + 0.004833 + 0.005208 + 0.00675 + 0.006208 + 0.006375 + 0.006458 + 0.006125 + 0.006125 + 0.006542 + 0.0055 + 0.005209 + 0.005459 + 0.005709 + 0.005458 + 0.0055 + 0.005375 + 0.00525 + 0.005 + 0.005209 + 0.005125 + 0.005583 + 0.008458 + 0.007833 + 0.008 + 0.007625 + 0.007583 + 0.007959 + 0.009334 + 0.008459 + 0.006792 + 0.00775 + 0.006417 + 0.006875 + 0.00675 + 0.006667 + 0.006375 + 0.005417 + 0.005167 + 0.005625 + 0.005416 + 0.005375 + 0.005666 + 0.005708 + 0.005417 + 0.010459 + 0.007292 + 0.007167 + 0.006375 + 0.008375 + 0.00775 + 0.008125 + 0.005958 + 0.005958 + 0.008667 + 0.008375 + 0.006875 + 0.007541 + 0.008042 + 0.008417 + 0.007542 + 0.00625 + 0.008792 + 0.008208 + 0.008125 + 0.006875 + 0.007375 + 0.007208 + 0.006208 + 0.005833 + 0.005167 + 0.006166 + 0.006083 + 0.006709 + 0.005792 + 0.005708 + 0.006041 + 0.005959 + 0.005834 + 0.006959 + 0.005458 + 0.005958 + 0.005666 + 0.005875 + 0.006958 + 0.007291 + 0.008167 + 0.007584 + 0.006875 + 0.007417 + 0.007459 + 0.007417 + 0.006959 + 0.006834 + 0.00625 + 0.006333 + 0.005375 + 0.005416 + 0.005625 + 0.005791 + 0.005542 + 0.005958 + 0.006 + 0.006084 + 0.004959 + 0.005917 + 0.005791 + 0.005833 + 0.005792 + 0.006416 + 0.005583 + 0.005542 + 0.005458 + 0.006 + 0.006041 + 0.005375 + 0.005792 + 0.005833 + 0.0055 + 0.006375 + 0.00625 + 0.006958 + 0.008416 + 0.008792 + 0.008583 + 0.0085 + 0.007916 + 0.008292 + 0.009125 + 0.008792 + 0.007917 + 0.007333 + 0.007791 + 0.007625 + 0.008375 + 0.00575 + 0.005625 + 0.005542 + 0.0055 + 0.006167 + 0.006458 + 0.006375 + 0.006416 + 0.007292 + 0.006709 + 0.007042 + 0.0085 + 0.007167 + 0.007375 + 0.008375 + 0.006125 + 0.006042 + 0.006917 + 0.006 + 0.00775 + 0.008458 + 0.007792 + 0.006209 + 0.006625 + 0.00625 + 0.006 + 0.007375 + 0.007208 + 0.0075 + 0.007417 + 0.005625 + 0.005083 + 0.005083 + 0.004583 + 0.0055 + 0.005333 + 0.005125 + 0.005 + 0.00525 + 0.006042 + 0.005084 + 0.005166 + 0.0055 + 0.005958 + 0.005417 + 0.005792 + 0.006 + 0.005209 + 0.004709 + 0.005291 + 0.005292 + 0.005375 + 0.00525 + 0.005083 + 0.005 + 0.006084 + 0.006292 + 0.006583 + 0.006417 + 0.006125 + 0.006709 + 0.006167 + 0.006417 + 0.006375 + 0.00525 + 0.0055 + 0.004917 + 0.005167 + 0.005708 + 0.005042 + 0.005 + 0.00525 + 0.00625 + 0.005916 + 0.00625 + 0.006208 + 0.006333 + 0.005875 + 0.006125 + 0.006042 + 0.006208 + 0.00525 + 0.005209 + 0.00575 + 0.005167 + 0.005209 + 0.007084 + 0.008042 + 0.00825 + 0.00775 + 0.007708 + 0.007375 + 0.00675 + 0.007875 + 0.008709 + 0.007167 + 0.006875 + 0.007375 + 0.00725 + 0.006875 + 0.006958 + 0.007209 + 0.007084 + 0.0065 + 0.006 + 0.006042 + 0.006208 + 0.007375 + 0.00725 + 0.006334 + 0.006375 + 0.007792 + 0.007209 + 0.006792 + 0.006875 + 0.008458 + 0.008 + 0.007625 + 0.007208 + 0.008166 + 0.007583 + 0.007541 + 0.0065 + 0.007041 + 0.006875 + 0.007 + 0.008083 + 0.007042 + 0.007458 + 0.007333 + 0.005875 + 0.006083 + 0.005083 + 0.005417 + 0.005125 + 0.004959 + 0.005125 + 0.005667 + 0.005625 + 0.004791 + 0.004583 + 0.00575 + 0.005417 + 0.005125 + 0.005208 + 0.005083 + 0.004958 + 0.004959 + 0.005 + 0.005334 + 0.004125 + 0.005334 + 0.00525 + 0.005 + 0.006167 + 0.006333 + 0.006208 + 0.006 + 0.006833 + 0.006791 + 0.00675 + 0.006 + 0.006709 + 0.006834 + 0.006667 + 0.00625 + 0.006791 + 0.006542 + 0.006417 + 0.006791 + 0.005458 + 0.005958 + 0.006167 + 0.006709 + 0.00625 + 0.006541 + 0.007 + 0.006625 + 0.007334 + 0.006625 + 0.005958 + 0.005417 + 0.006292 + 0.007916 + 0.007417 + 0.006125 + 0.006125 + 0.006291 + 0.007167 + 0.007833 + 0.007458 + 0.006625 + 0.007458 + 0.007916 + 0.008291 + 0.006542 + 0.006042 + 0.005708 + 0.005666 + 0.005833 + 0.005625 + 0.006292 + 0.006041 + 0.005417 + 0.005833 + 0.006333 + 0.005167 + 0.00525 + 0.006583 + 0.006417 + 0.005917 + 0.00575 + 0.006541 + 0.005667 + 0.005166 + 0.005958 + 0.007292 + 0.00725 + 0.007667 + 0.008959 + 0.00825 + 0.008208 + 0.006792 + 0.008042 + 0.00675 + 0.006417 + 0.007375 + 0.007542 + 0.007458 + 0.007625 + 0.006708 + 0.0055 + 0.005208 + 0.005333 + 0.004958 + 0.005292 + 0.005125 + 0.005042 + 0.005541 + 0.005042 + 0.0055 + 0.004958 + 0.004917 + 0.004917 + 0.004875 + 0.004791 + 0.005042 + 0.005208 + 0.005166 + 0.005167 + 0.004875 + 0.004917 + 0.004917 + 0.004709 + 0.004666 + 0.004875 + 0.005292 + 0.005 + 0.004958 + 0.004958 + 0.005083 + 0.00525 + 0.004916 + 0.005334 + 0.005458 + 0.005 + 0.004625 + 0.004667 + 0.00475 + 0.004833 + 0.0045 + 0.004875 + 0.005917 + 0.005167 + 0.004875 + 0.004834 + 0.005208 + 0.005583 + 0.006208 + 0.005917 + 0.005166 + 0.005208 + 0.004458 + 0.007708 + 0.007042 + 0.006709 + 0.006542 + 0.006708 + 0.007916 + 0.007583 + 0.007625 + 0.00775 + 0.012542 + 0.007625 + 0.006833 + 0.006959 + 0.006584 + 0.007167 + 0.007333 + 0.006833 + 0.007042 + 0.006334 + 0.006958 + 0.007583 + 0.00875 + 0.007333 + 0.0065 + 0.006958 + 0.006541 + 0.006583 + 0.006667 + 0.006334 + 0.006834 + 0.00575 + 0.005333 + 0.00625 + 0.005833 + 0.006417 + 0.009 + 0.00725 + 0.007208 + 0.007916 + 0.006833 + 0.006333 + 0.007916 + 0.008042 + 0.008084 + 0.008209 + 0.007917 + 0.00775 + 0.0075 + 0.005917 + 0.005917 + 0.006166 + 0.006125 + 0.025541 + 0.022708 + 0.007625 + 0.006375 + 0.006208 + 0.005417 + 0.005542 + 0.00525 + 0.0065 + 0.006667 + 0.006584 + 0.006834 + 0.0065 + 0.006458 + 0.005917 + 0.005917 + 0.005917 + 0.005666 + 0.006875 + 0.007291 + 0.006375 + 0.006083 + 0.007125 + 0.006583 + 0.006041 + 0.004833 + 0.006333 + 0.006375 + 0.00675 + 0.006042 + 0.006208 + 0.00675 + 0.006292 + 0.0055 + 0.006542 + 0.00625 + 0.006625 + 0.006083 + 0.005666 + 0.005875 + 0.005458 + 0.00525 + 0.005625 + 0.005917 + 0.009042 + 0.006708 + 0.006416 + 0.006583 + 0.00625 + 0.009208 + 0.006916 + 0.006417 + 0.006 + 0.00625 + 0.0095 + 0.005916 + 0.006 + 0.006333 + 0.007792 + 0.008334 + 0.006834 + 0.00625 + 0.005583 + 0.005958 + 0.004667 + 0.0055 + 0.00575 + 0.004917 + 0.005375 + 0.005416 + 0.005125 + 0.005042 + 0.004709 + 0.004917 + 0.00725 + 0.006 + 0.006041 + 0.006167 + 0.005875 + 0.005625 + 0.00625 + 0.006167 + 0.005959 + 0.006333 + 0.007625 + 0.007167 + 0.007333 + 0.006666 + 0.006 + 0.006291 + 0.006125 + 0.007333 + 0.0075 + 0.007125 + 0.006833 + 0.007917 + 0.006125 + 0.005625 + 0.005416 + 0.006125 + 0.006084 + 0.006167 + 0.006166 + 0.006458 + 0.006833 + 0.006333 + 0.006167 + 0.007042 + 0.006791 + 0.007083 + 0.006541 + 0.006042 + 0.0065 + 0.006542 + 0.0055 + 0.005334 + 0.004666 + 0.004792 + 0.005083 + 0.004625 + 0.004708 + 0.005042 + 0.00475 + 0.005083 + 0.006042 + 0.006709 + 0.006834 + 0.006709 + 0.006459 + 0.006166 + 0.005583 + 0.005083 + 0.005833 + 0.005292 + 0.005583 + 0.006333 + 0.006625 + 0.007166 + 0.00675 + 0.006542 + 0.00625 + 0.005542 + 0.005375 + 0.005167 + 0.005959 + 0.006125 + 0.005667 + 0.005584 + 0.005875 + 0.006917 + 0.005833 + 0.005791 + 0.006042 + 0.006958 + 0.008625 + 0.008792 + 0.00825 + 0.007542 + 0.026333 + 0.010833 + 0.007208 + 0.006791 + 0.006667 + 0.007125 + 0.007333 + 0.006833 + 0.00675 + 0.00625 + 0.005542 + 0.006 + 0.006625 + 0.007084 + 0.007125 + 0.007375 + 0.02325 + 0.009541 + 0.006666 + 0.0065 + 0.006791 + 0.00675 + 0.005791 + 0.0075 + 0.007375 + 0.007458 + 0.007083 + 0.009292 + 0.009334 + 0.007417 + 0.006375 + 0.007375 + 0.00775 + 0.00775 + 0.009417 + 0.007291 + 0.007833 + 0.007292 + 0.007042 + 0.006208 + 0.0065 + 0.006292 + 0.006708 + 0.006541 + 0.006708 + 0.006375 + 0.00725 + 0.006083 + 0.006375 + 0.007375 + 0.006625 + 0.0065 + 0.006208 + 0.006333 + 0.006833 + 0.0075 + 0.032833 + 0.008042 + 0.005708 + 0.006416 + 0.006667 + 0.006208 + 0.006 + 0.006084 + 0.00625 + 0.006292 + 0.005875 + 0.006334 + 0.006084 + 0.006334 + 0.00575 + 0.005458 + 0.006083 + 0.005625 + 0.0065 + 0.006417 + 0.006083 + 0.005708 + 0.006333 + 0.005709 + 0.005625 + 0.005875 + 0.005458 + 0.005417 + 0.005333 + 0.004916 + 0.005083 + 0.005917 + 0.005625 + 0.005334 + 0.005292 + 0.007958 + 0.006792 + 0.010959 + 0.006833 + 0.00625 + 0.008916 + 0.011417 + 0.00625 + 0.006458 + 0.00825 + 0.009 + 0.008208 + 0.008375 + 0.007125 + 0.007125 + 0.006041 + 0.005708 + 0.0055 + 0.0055 + 0.005416 + 0.005333 + 0.00775 + 0.006958 + 0.007042 + 0.007 + 0.007125 + 0.007084 + 0.006834 + 0.005584 + 0.005417 + 0.005458 + 0.005375 + 0.006875 + 0.005958 + 0.006083 + 0.008583 + 0.008041 + 0.0075 + 0.008208 + 0.00725 + 0.00825 + 0.0075 + 0.009666 + 0.00975 + 0.007 + 0.006709 + 0.006958 + 0.00725 + 0.006167 + 0.005542 + 0.005667 + 0.005875 + 0.005667 + 0.005958 + 0.005667 + 0.006042 + 0.006542 + 0.006708 + 0.006625 + 0.005917 + 0.006416 + 0.007333 + 0.007375 + 0.005958 + 0.006541 + 0.01675 + 0.020333 + 0.006834 + 0.006792 + 0.006333 + 0.007708 + 0.005625 + 0.005 + 0.005417 + 0.005 + 0.005125 + 0.005459 + 0.005042 + 0.005417 + 0.00625 + 0.006542 + 0.00675 + 0.007 + 0.006625 + 0.005583 + 0.006583 + 0.007042 + 0.006208 + 0.006041 + 0.006208 + 0.006417 + 0.005666 + 0.005333 + 0.005042 + 0.005375 + 0.005417 + 0.005625 + 0.0065 + 0.007375 + 0.007708 + 0.009583 + 0.006041 + 0.005917 + 0.006709 + 0.006667 + 0.008875 + 0.008792 + 0.008458 + 0.008792 + 0.011833 + 0.009917 + 0.008292 + 0.008125 + 0.006916 + 0.006625 + 0.0065 + 0.006667 + 0.006625 + 0.006291 + 0.00625 + 0.005584 + 0.007292 + 0.006666 + 0.006583 + 0.006625 + 0.006666 + 0.005958 + 0.00575 + 0.006041 + 0.005875 + 0.006625 + 0.006458 + 0.006917 + 0.0065 + 0.007042 + 0.010416 + 0.0085 + 0.007791 + 0.008458 + 0.007667 + 0.006125 + 0.006542 + 0.008166 + 0.00725 + 0.0075 + 0.008875 + 0.009958 + 0.008667 + 0.00775 + 0.00725 + 0.006584 + 0.006583 + 0.008125 + 0.007542 + 0.007208 + 0.007292 + 0.007666 + 0.006167 + 0.005708 + 0.007166 + 0.007167 + 0.007417 + 0.006083 + 0.00625 + 0.006208 + 0.006791 + 0.006958 + 0.006833 + 0.006959 + 0.006709 + 0.005834 + 0.005 + 0.005209 + 0.005167 + 0.005917 + 0.005583 + 0.006083 + 0.005667 + 0.005541 + 0.005375 + 0.00625 + 0.006583 + 0.00675 + 0.007333 + 0.007334 + 0.00625 + 0.006708 + 0.006625 + 0.006083 + 0.005083 + 0.005666 + 0.005667 + 0.006708 + 0.006666 + 0.006625 + 0.006625 + 0.005959 + 0.008 + 0.008042 + 0.007417 + 0.007708 + 0.006958 + 0.006875 + 0.00625 + 0.006042 + 0.007916 + 0.009083 + 0.009042 + 0.008084 + 0.008417 + 0.008416 + 0.00825 + 0.007416 + 0.007167 + 0.006667 + 0.007542 + 0.00675 + 0.005625 + 0.005583 + 0.005292 + 0.005834 + 0.006792 + 0.006875 + 0.007417 + 0.00575 + 0.005292 + 0.005791 + 0.005833 + 0.00525 + 0.005667 + 0.006083 + 0.005916 + 0.005917 + 0.006208 + 0.006042 + 0.008 + 0.00825 + 0.007875 + 0.007792 + 0.006125 + 0.005541 + 0.006459 + 0.007375 + 0.00825 + 0.007916 + 0.007417 + 0.007084 + 0.006042 + 0.005792 + 0.005166 + 0.005792 + 0.005542 + 0.00575 + 0.0055 + 0.00575 + 0.005792 + 0.006458 + 0.005583 + 0.004959 + 0.005375 + 0.005542 + 0.005375 + 0.004792 + 0.005916 + 0.005542 + 0.00525 + 0.005333 + 0.005417 + 0.005584 + 0.005292 + 0.00525 + 0.005208 + 0.0055 + 0.005666 + 0.005667 + 0.005375 + 0.005334 + 0.005209 + 0.005416 + 0.005625 + 0.005375 + 0.004833 + 0.005208 + 0.005 + 0.00525 + 0.004958 + 0.005542 + 0.005541 + 0.005375 + 0.005125 + 0.005625 + 0.005292 + 0.012 + 0.005166 + 0.005292 + 0.005458 + 0.0055 + 0.00525 + 0.007917 + 0.006792 + 0.006584 + 0.006292 + 0.006041 + 0.006042 + 0.005792 + 0.005583 + 0.006708 + 0.00625 + 0.005875 + 0.008291 + 0.007459 + 0.007917 + 0.007166 + 0.006208 + 0.005417 + 0.005875 + 0.006458 + 0.006583 + 0.006 + 0.006167 + 0.005625 + 0.005417 + 0.005917 + 0.006916 + 0.006875 + 0.007083 + 0.007666 + 0.007375 + 0.007125 + 0.007458 + 0.007458 + 0.005916 + 0.005083 + 0.00825 + 0.008416 + 0.008125 + 0.008917 + 0.008166 + 0.007667 + 0.0075 + 0.007167 + 0.008292 + 0.007333 + 0.009833 + 0.007375 + 0.006917 + 0.007875 + 0.008167 + 0.008083 + 0.006125 + 0.006 + 0.006375 + 0.006334 + 0.006333 + 0.006375 + 0.006167 + 0.0075 + 0.006291 + 0.007 + 0.006708 + 0.007083 + 0.007875 + 0.00675 + 0.006667 + 0.00675 + 0.006875 + 0.006208 + 0.005416 + 0.005667 + 0.00675 + 0.006958 + 0.006333 + 0.006083 + 0.005458 + 0.005792 + 0.006833 + 0.006917 + 0.006541 + 0.005875 + 0.005458 + 0.005666 + 0.005 + 0.005375 + 0.005375 + 0.005584 + 0.005541 + 0.005417 + 0.004875 + 0.005209 + 0.004791 + 0.006083 + 0.006208 + 0.005917 + 0.004834 + 0.00475 + 0.005167 + 0.006041 + 0.005334 + 0.006084 + 0.005625 + 0.005458 + 0.005875 + 0.006083 + 0.0065 + 0.006125 + 0.005292 + 0.006459 + 0.006292 + 0.006209 + 0.008041 + 0.008 + 0.00875 + 0.008 + 0.007875 + 0.007042 + 0.006583 + 0.005625 + 0.005292 + 0.005541 + 0.007167 + 0.007042 + 0.00675 + 0.007 + 0.006667 + 0.007042 + 0.00675 + 0.006333 + 0.005417 + 0.005083 + 0.0055 + 0.005834 + 0.005542 + 0.0055 + 0.006541 + 0.007 + 0.007916 + 0.008208 + 0.007542 + 0.007709 + 0.00775 + 0.006792 + 0.007 + 0.008042 + 0.007792 + 0.008 + 0.008083 + 0.007958 + 0.008 + 0.008375 + 0.007083 + 0.006333 + 0.00625 + 0.00625 + 0.006417 + 0.006334 + 0.006167 + 0.005458 + 0.005375 + 0.005459 + 0.00475 + 0.005166 + 0.005083 + 0.00525 + 0.005541 + 0.004917 + 0.004833 + 0.004583 + 0.004958 + 0.005041 + 0.005292 + 0.005458 + 0.005125 + 0.004958 + 0.004875 + 0.005209 + 0.005042 + 0.005083 + 0.004791 + 0.004958 + 0.005 + 0.005125 + 0.005083 + 0.004875 + 0.004625 + 0.004875 + 0.004708 + 0.005083 + 0.00525 + 0.004833 + 0.005 + 0.005333 + 0.006291 + 0.006625 + 0.006333 + 0.006541 + 0.005833 + 0.006583 + 0.007042 + 0.007 + 0.006583 + 0.0065 + 0.006791 + 0.006125 + 0.005292 + 0.004875 + 0.005416 + 0.006208 + 0.005792 + 0.008625 + 0.0085 + 0.007708 + 0.0075 + 0.007708 + 0.007667 + 0.006166 + 0.005 + 0.006875 + 0.007083 + 0.006708 + 0.006541 + 0.0065 + 0.006833 + 0.006583 + 0.005833 + 0.005041 + 0.005208 + 0.005584 + 0.005042 + 0.005125 + 0.005125 + 0.005542 + 0.005458 + 0.005083 + 0.006583 + 0.007917 + 0.008 + 0.007916 + 0.007167 + 0.007709 + 0.007834 + 0.008292 + 0.007041 + 0.00775 + 0.007625 + 0.007667 + 0.0085 + 0.00675 + 0.007667 + 0.00825 + 0.00675 + 0.006334 + 0.00675 + 0.006125 + 0.006334 + 0.005209 + 0.005542 + 0.005375 + 0.004583 + 0.005375 + 0.00475 + 0.005125 + 0.0055 + 0.005083 + 0.005083 + 0.004916 + 0.004667 + 0.004542 + 0.005 + 0.00525 + 0.00525 + 0.005125 + 0.005333 + 0.005417 + 0.005041 + 0.00525 + 0.005083 + 0.005542 + 0.005041 + 0.004792 + 0.005334 + 0.00525 + 0.0045 + 0.006083 + 0.004875 + 0.004916 + 0.00475 + 0.004208 + 0.005333 + 0.0055 + 0.006125 + 0.006542 + 0.006375 + 0.006458 + 0.006416 + 0.006167 + 0.005917 + 0.004458 + 0.005 + 0.005375 + 0.0055 + 0.005542 + 0.005416 + 0.005334 + 0.005541 + 0.005 + 0.005 + 0.005417 + 0.00625 + 0.008417 + 0.007125 + 0.007417 + 0.007625 + 0.007667 + 0.007541 + 0.006625 + 0.005166 + 0.005917 + 0.0075 + 0.007125 + 0.006834 + 0.006625 + 0.006417 + 0.006958 + 0.006958 + 0.006333 + 0.005125 + 0.005459 + 0.005042 + 0.005083 + 0.005416 + 0.005625 + 0.005209 + 0.005416 + 0.006292 + 0.008084 + 0.0085 + 0.007917 + 0.008333 + 0.007417 + 0.007625 + 0.008208 + 0.007167 + 0.007583 + 0.007667 + 0.007541 + 0.008042 + 0.007 + 0.008583 + 0.008 + 0.006917 + 0.006417 + 0.0065 + 0.006667 + 0.006959 + 0.005167 + 0.006041 + 0.008 + 0.007291 + 0.006542 + 0.006584 + 0.0065 + 0.006417 + 0.006209 + 0.006208 + 0.006458 + 0.006334 + 0.00525 + 0.004916 + 0.005 + 0.005125 + 0.005208 + 0.005042 + 0.005291 + 0.004875 + 0.005292 + 0.005208 + 0.005333 + 0.005166 + 0.005 + 0.005417 + 0.005 + 0.005291 + 0.004833 + 0.004459 + 0.004334 + 0.005 + 0.004791 + 0.004875 + 0.004791 + 0.005417 + 0.004625 + 0.00525 + 0.00525 + 0.005041 + 0.005083 + 0.005208 + 0.004792 + 0.005541 + 0.005708 + 0.005542 + 0.005083 + 0.00525 + 0.005375 + 0.005291 + 0.005667 + 0.005292 + 0.005791 + 0.006417 + 0.007875 + 0.008416 + 0.007875 + 0.007333 + 0.007791 + 0.008208 + 0.00675 + 0.0055 + 0.006083 + 0.007166 + 0.007208 + 0.006792 + 0.006792 + 0.007 + 0.007375 + 0.006958 + 0.005125 + 0.005333 + 0.005542 + 0.005959 + 0.005292 + 0.005708 + 0.005208 + 0.005792 + 0.006416 + 0.005667 + 0.006666 + 0.009375 + 0.008334 + 0.018084 + 0.008167 + 0.008041 + 0.00725 + 0.006334 + 0.008541 + 0.007875 + 0.008125 + 0.00875 + 0.007875 + 0.008375 + 0.009334 + 0.008792 + 0.008458 + 0.007333 + 0.008417 + 0.007208 + 0.006375 + 0.006042 + 0.005875 + 0.0085 + 0.006875 + 0.031417 + 0.011125 + 0.007625 + 0.007667 + 0.006792 + 0.005583 + 0.004875 + 0.005541 + 0.005541 + 0.005709 + 0.005791 + 0.006375 + 0.005375 + 0.005917 + 0.005 + 0.005125 + 0.005 + 0.006125 + 0.005541 + 0.0055 + 0.005333 + 0.005875 + 0.005542 + 0.005333 + 0.005541 + 0.006083 + 0.005042 + 0.004916 + 0.005375 + 0.005333 + 0.005333 + 0.005875 + 0.006583 + 0.006375 + 0.005584 + 0.005459 + 0.005875 + 0.005917 + 0.005958 + 0.007416 + 0.006125 + 0.005416 + 0.005584 + 0.0065 + 0.006625 + 0.006166 + 0.0065 + 0.005916 + 0.007583 + 0.008333 + 0.007625 + 0.007375 + 0.007458 + 0.007167 + 0.005834 + 0.008125 + 0.007292 + 0.00675 + 0.006292 + 0.006291 + 0.00525 + 0.005792 + 0.005459 + 0.005709 + 0.005583 + 0.005959 + 0.005209 + 0.00525 + 0.005542 + 0.005334 + 0.005042 + 0.005917 + 0.005792 + 0.005333 + 0.005333 + 0.008542 + 0.007875 + 0.007333 + 0.007542 + 0.006375 + 0.006 + 0.007916 + 0.006208 + 0.00725 + 0.007083 + 0.007333 + 0.006667 + 0.006084 + 0.0075 + 0.007333 + 0.007625 + 0.006208 + 0.005708 + 0.006208 + 0.005958 + 0.006208 + 0.005209 + 0.005209 + 0.006208 + 0.005416 + 0.00525 + 0.0055 + 0.004667 + 0.005666 + 0.005959 + 0.0065 + 0.006417 + 0.006667 + 0.006625 + 0.006917 + 0.006292 + 0.006666 + 0.005708 + 0.005333 + 0.0105 + 0.005291 + 0.005125 + 0.005375 + 0.005459 + 0.005333 + 0.005 + 0.006041 + 0.00525 + 0.006 + 0.006584 + 0.006792 + 0.006625 + 0.006583 + 0.005875 + 0.005875 + 0.005667 + 0.005791 + 0.00525 + 0.005333 + 0.006333 + 0.006208 + 0.006375 + 0.006417 + 0.005792 + 0.006334 + 0.005959 + 0.00575 + 0.005958 + 0.005417 + 0.005666 + 0.005042 + 0.005166 + 0.005083 + 0.008 + 0.008459 + 0.008209 + 0.007792 + 0.007625 + 0.008209 + 0.009 + 0.008125 + 0.00725 + 0.004875 + 0.006417 + 0.005834 + 0.006125 + 0.005917 + 0.006209 + 0.006333 + 0.00725 + 0.007292 + 0.0075 + 0.007792 + 0.007166 + 0.006167 + 0.006209 + 0.006 + 0.007666 + 0.006917 + 0.007292 + 0.008083 + 0.006791 + 0.006375 + 0.006208 + 0.007167 + 0.009166 + 0.008042 + 0.008125 + 0.007167 + 0.008333 + 0.008083 + 0.007125 + 0.00825 + 0.007792 + 0.007417 + 0.008083 + 0.007 + 0.006958 + 0.00675 + 0.006042 + 0.006125 + 0.006459 + 0.007167 + 0.007625 + 0.006417 + 0.006084 + 0.006 + 0.0065 + 0.007041 + 0.0065 + 0.007041 + 0.006083 + 0.006583 + 0.006667 + 0.0065 + 0.005958 + 0.006041 + 0.006583 + 0.007125 + 0.006708 + 0.006 + 0.005875 + 0.007042 + 0.006292 + 0.006875 + 0.006292 + 0.0065 + 0.00675 + 0.005625 + 0.005417 + 0.005791 + 0.005792 + 0.005209 + 0.005792 + 0.005167 + 0.004875 + 0.005667 + 0.005667 + 0.005708 + 0.005125 + 0.005458 + 0.006042 + 0.00525 + 0.004875 + 0.006167 + 0.006125 + 0.005834 + 0.00525 + 0.005667 + 0.005625 + 0.005125 + 0.005459 + 0.005542 + 0.005667 + 0.008375 + 0.00825 + 0.006875 + 0.007667 + 0.007375 + 0.007292 + 0.008875 + 0.0075 + 0.006417 + 0.006833 + 0.007208 + 0.006541 + 0.006 + 0.006292 + 0.005541 + 0.005167 + 0.005459 + 0.005209 + 0.005292 + 0.00525 + 0.005417 + 0.005125 + 0.007584 + 0.004917 + 0.004792 + 0.005 + 0.005667 + 0.005083 + 0.005417 + 0.006958 + 0.012291 + 0.007083 + 0.007042 + 0.006834 + 0.005959 + 0.00625 + 0.007334 + 0.006958 + 0.006958 + 0.006917 + 0.007125 + 0.00725 + 0.006208 + 0.00475 + 0.004291 + 0.004125 + 0.004416 + 0.004208 + 0.004292 + 0.004917 + 0.004458 + 0.004291 + 0.003917 + 0.004791 + 0.004292 + 0.004166 + 0.004542 + 0.004292 + 0.004083 + 0.004167 + 0.003875 + 0.004792 + 0.004916 + 0.004833 + 0.005083 + 0.004292 + 0.004167 + 0.00475 + 0.005625 + 0.005917 + 0.005625 + 0.0055 + 0.005542 + 0.005208 + 0.006083 + 0.0055 + 0.005083 + 0.004417 + 0.004125 + 0.004208 + 0.004625 + 0.00425 + 0.004042 + 0.004458 + 0.004333 + 0.004 + 0.003917 + 0.004042 + 0.004083 + 0.004791 + 0.005167 + 0.005125 + 0.007417 + 0.006791 + 0.006667 + 0.006083 + 0.005834 + 0.00575 + 0.006209 + 0.005959 + 0.005917 + 0.005125 + 0.005375 + 0.007125 + 0.007625 + 0.007333 + 0.00725 + 0.006334 + 0.006375 + 0.005667 + 0.005292 + 0.004833 + 0.004667 + 0.004333 + 0.004791 + 0.004459 + 0.005959 + 0.006709 + 0.006875 + 0.006958 + 0.00675 + 0.006542 + 0.007083 + 0.006417 + 0.005084 + 0.005667 + 0.005375 + 0.0065 + 0.006542 + 0.006166 + 0.008 + 0.008042 + 0.008083 + 0.007916 + 0.007 + 0.008208 + 0.007 + 0.008292 + 0.008416 + 0.007875 + 0.007791 + 0.007917 + 0.008042 + 0.0055 + 0.005292 + 0.005417 + 0.005292 + 0.005042 + 0.005166 + 0.005167 + 0.005291 + 0.005334 + 0.004583 + 0.005458 + 0.005667 + 0.00525 + 0.004917 + 0.0055 + 0.006541 + 0.006208 + 0.006584 + 0.006459 + 0.006 + 0.006125 + 0.006333 + 0.00475 + 0.005209 + 0.004959 + 0.005125 + 0.004875 + 0.005125 + 0.00525 + 0.004584 + 0.004791 + 0.004708 + 0.004917 + 0.005041 + 0.004917 + 0.004833 + 0.005042 + 0.004917 + 0.004791 + 0.005209 + 0.005042 + 0.005208 + 0.00525 + 0.005292 + 0.004709 + 0.004959 + 0.005 + 0.005333 + 0.005792 + 0.007708 + 0.007041 + 0.007 + 0.006916 + 0.006875 + 0.006583 + 0.00675 + 0.0055 + 0.007084 + 0.0065 + 0.00625 + 0.008459 + 0.007958 + 0.007959 + 0.007625 + 0.008541 + 0.007167 + 0.006333 + 0.004916 + 0.005292 + 0.005542 + 0.008833 + 0.007167 + 0.006875 + 0.006959 + 0.007042 + 0.006916 + 0.006583 + 0.006292 + 0.006 + 0.005875 + 0.006875 + 0.008 + 0.021542 + 0.012083 + 0.009666 + 0.007417 + 0.00975 + 0.008375 + 0.008917 + 0.007916 + 0.007125 + 0.005916 + 0.006875 + 0.009 + 0.008042 + 0.008375 + 0.007709 + 0.00675 + 0.0065 + 0.008166 + 0.007209 + 0.006292 + 0.006791 + 0.006417 + 0.006708 + 0.007125 + 0.006375 + 0.005416 + 0.006417 + 0.005375 + 0.005875 + 0.007125 + 0.006625 + 0.006 + 0.00675 + 0.006167 + 0.0055 + 0.005334 + 0.006041 + 0.005625 + 0.0055 + 0.006167 + 0.004917 + 0.005041 + 0.005 + 0.005041 + 0.006 + 0.0055 + 0.005541 + 0.006167 + 0.005333 + 0.005208 + 0.005542 + 0.005125 + 0.005625 + 0.00525 + 0.005084 + 0.005459 + 0.005709 + 0.005667 + 0.00625 + 0.005416 + 0.005 + 0.005291 + 0.006042 + 0.005625 + 0.005125 + 0.005041 + 0.00575 + 0.005916 + 0.004583 + 0.005333 + 0.00575 + 0.005583 + 0.005291 + 0.00525 + 0.005416 + 0.005833 + 0.006208 + 0.0085 + 0.007833 + 0.007167 + 0.007542 + 0.006666 + 0.006333 + 0.006125 + 0.00475 + 0.006167 + 0.00675 + 0.007083 + 0.006625 + 0.006167 + 0.006708 + 0.006375 + 0.006542 + 0.005541 + 0.005709 + 0.0055 + 0.005792 + 0.005666 + 0.00675 + 0.005333 + 0.005625 + 0.00575 + 0.006542 + 0.008375 + 0.008417 + 0.008583 + 0.007917 + 0.008084 + 0.008542 + 0.00975 + 0.007042 + 0.007583 + 0.007791 + 0.007833 + 0.008375 + 0.007916 + 0.01 + 0.008541 + 0.006625 + 0.0065 + 0.006875 + 0.006583 + 0.006208 + 0.006041 + 0.0065 + 0.005417 + 0.005916 + 0.005667 + 0.00475 + 0.005542 + 0.005542 + 0.005167 + 0.005084 + 0.005084 + 0.004916 + 0.005125 + 0.005084 + 0.005459 + 0.005292 + 0.005 + 0.00525 + 0.004875 + 0.005 + 0.005417 + 0.005834 + 0.005208 + 0.005458 + 0.005042 + 0.005167 + 0.00575 + 0.006125 + 0.0055 + 0.005083 + 0.005 + 0.004875 + 0.005084 + 0.005291 + 0.004917 + 0.005333 + 0.00475 + 0.005166 + 0.005 + 0.004917 + 0.005292 + 0.005083 + 0.005083 + 0.006166 + 0.005542 + 0.005291 + 0.005458 + 0.0055 + 0.00525 + 0.005791 + 0.00525 + 0.00525 + 0.00625 + 0.007625 + 0.009125 + 0.00825 + 0.007625 + 0.00775 + 0.007792 + 0.008125 + 0.006083 + 0.005375 + 0.005792 + 0.008042 + 0.007209 + 0.006625 + 0.006667 + 0.006667 + 0.007 + 0.006709 + 0.006958 + 0.006833 + 0.006708 + 0.007042 + 0.005709 + 0.005583 + 0.005958 + 0.00575 + 0.006542 + 0.007416 + 0.008875 + 0.007166 + 0.007042 + 0.025834 + 0.022583 + 0.011 + 0.011875 + 0.00775 + 0.008292 + 0.00775 + 0.007958 + 0.007666 + 0.007584 + 0.00925 + 0.008375 + 0.006542 + 0.005916 + 0.006167 + 0.006708 + 0.006042 + 0.004958 + 0.005083 + 0.006042 + 0.005875 + 0.005084 + 0.005334 + 0.006041 + 0.006209 + 0.00575 + 0.005208 + 0.004708 + 0.00525 + 0.005 + 0.005625 + 0.005209 + 0.005084 + 0.005167 + 0.005292 + 0.0045 + 0.004834 + 0.004834 + 0.0055 + 0.005417 + 0.005208 + 0.005875 + 0.0055 + 0.005417 + 0.005416 + 0.005583 + 0.004875 + 0.004875 + 0.005042 + 0.005208 + 0.005333 + 0.0055 + 0.005875 + 0.0055 + 0.005042 + 0.004958 + 0.005292 + 0.005583 + 0.005583 + 0.004916 + 0.005792 + 0.00575 + 0.005833 + 0.005458 + 0.00525 + 0.005291 + 0.005 + 0.005042 + 0.005875 + 0.005333 + 0.005875 + 0.008125 + 0.007916 + 0.007083 + 0.007875 + 0.008083 + 0.008125 + 0.007083 + 0.006583 + 0.006792 + 0.007542 + 0.006375 + 0.005667 + 0.005458 + 0.005125 + 0.005166 + 0.005542 + 0.005375 + 0.004833 + 0.005167 + 0.0055 + 0.005167 + 0.005 + 0.004959 + 0.005458 + 0.005208 + 0.004917 + 0.006042 + 0.007542 + 0.007625 + 0.006959 + 0.006916 + 0.006417 + 0.005875 + 0.00525 + 0.007458 + 0.007166 + 0.007333 + 0.006459 + 0.006209 + 0.006042 + 0.007458 + 0.006875 + 0.006083 + 0.005917 + 0.005875 + 0.004584 + 0.005042 + 0.005208 + 0.004917 + 0.005041 + 0.004875 + 0.0055 + 0.004416 + 0.005334 + 0.005959 + 0.006125 + 0.005667 + 0.00475 + 0.004834 + 0.005208 + 0.005208 + 0.004917 + 0.0045 + 0.007292 + 0.005167 + 0.005 + 0.005375 + 0.005917 + 0.005416 + 0.005875 + 0.005791 + 0.005583 + 0.005167 + 0.004833 + 0.005125 + 0.004666 + 0.004708 + 0.004625 + 0.005041 + 0.005167 + 0.005166 + 0.004792 + 0.004959 + 0.005125 + 0.004917 + 0.00475 + 0.005 + 0.004625 + 0.004791 + 0.00475 + 0.005583 + 0.0055 + 0.004708 + 0.005 + 0.005208 + 0.00525 + 0.005 + 0.004791 + 0.004958 + 0.007042 + 0.008334 + 0.00825 + 0.007666 + 0.00775 + 0.008042 + 0.007958 + 0.009 + 0.005584 + 0.005291 + 0.005334 + 0.0055 + 0.005166 + 0.008375 + 0.005291 + 0.005084 + 0.005209 + 0.005125 + 0.005208 + 0.005208 + 0.005042 + 0.004959 + 0.004875 + 0.004917 + 0.004792 + 0.005166 + 0.005083 + 0.008542 + 0.006792 + 0.007166 + 0.007083 + 0.006375 + 0.005541 + 0.007542 + 0.006416 + 0.005583 + 0.007125 + 0.006917 + 0.005792 + 0.005833 + 0.005875 + 0.0075 + 0.006917 + 0.005333 + 0.005375 + 0.005709 + 0.005459 + 0.004625 + 0.004667 + 0.004666 + 0.005292 + 0.005 + 0.004375 + 0.005 + 0.005167 + 0.005041 + 0.005375 + 0.00575 + 0.006042 + 0.005958 + 0.005666 + 0.005792 + 0.005875 + 0.005625 + 0.005 + 0.00475 + 0.004542 + 0.0045 + 0.004625 + 0.004834 + 0.00475 + 0.004959 + 0.004792 + 0.005 + 0.004833 + 0.00475 + 0.004708 + 0.004875 + 0.004375 + 0.004791 + 0.00475 + 0.00475 + 0.004625 + 0.004958 + 0.004792 + 0.004958 + 0.004625 + 0.004709 + 0.004958 + 0.005083 + 0.004959 + 0.005417 + 0.005125 + 0.005125 + 0.005125 + 0.004958 + 0.005 + 0.004792 + 0.004417 + 0.004958 + 0.004666 + 0.006334 + 0.008 + 0.007292 + 0.006958 + 0.007291 + 0.006833 + 0.005875 + 0.005584 + 0.005375 + 0.00525 + 0.007167 + 0.006625 + 0.006167 + 0.006292 + 0.006041 + 0.00625 + 0.006041 + 0.006 + 0.006083 + 0.007083 + 0.006708 + 0.007125 + 0.007625 + 0.007709 + 0.007125 + 0.006583 + 0.007333 + 0.009541 + 0.008458 + 0.007875 + 0.007833 + 0.007333 + 0.006666 + 0.008792 + 0.057041 + 0.082042 + 0.010042 + 0.00725 + 0.007667 + 0.006625 + 0.014042 + 0.008459 + 0.008125 + 0.005708 + 0.005625 + 0.005458 + 0.005916 + 0.005167 + 0.005334 + 0.006959 + 0.006 + 0.006042 + 0.006041 + 0.005459 + 0.007834 + 0.006125 + 0.005958 + 0.00625 + 0.005541 + 0.005417 + 0.005875 + 0.005541 + 0.006042 + 0.005791 + 0.006083 + 0.005625 + 0.006334 + 0.005625 + 0.005667 + 0.005875 + 0.005791 + 0.004917 + 0.005084 + 0.005167 + 0.005375 + 0.005042 + 0.00575 + 0.006125 + 0.006167 + 0.005083 + 0.005625 + 0.005709 + 0.005417 + 0.005875 + 0.005625 + 0.00525 + 0.0055 + 0.0055 + 0.005667 + 0.005 + 0.007375 + 0.007541 + 0.005583 + 0.006333 + 0.005792 + 0.005542 + 0.005292 + 0.005667 + 0.0055 + 0.007042 + 0.009042 + 0.009583 + 0.007125 + 0.007166 + 0.006292 + 0.007292 + 0.006708 + 0.007625 + 0.005833 + 0.007541 + 0.005708 + 0.005875 + 0.005709 + 0.008625 + 0.005875 + 0.005917 + 0.007209 + 0.007 + 0.005834 + 0.005583 + 0.006291 + 0.006042 + 0.005792 + 0.005625 + 0.006042 + 0.007708 + 0.005667 + 0.007334 + 0.00625 + 0.009542 + 0.006875 + 0.006583 + 0.006458 + 0.006292 + 0.006667 + 0.005875 + 0.008958 + 0.0065 + 0.00625 + 0.005791 + 0.00625 + 0.006375 + 0.005834 + 0.005416 + 0.005584 + 0.005375 + 0.004791 + 0.004958 + 0.004792 + 0.004375 + 0.006542 + 0.0045 + 0.00525 + 0.005291 + 0.005458 + 0.0055 + 0.005333 + 0.005292 + 0.005083 + 0.005083 + 0.005167 + 0.005333 + 0.005458 + 0.00575 + 0.005459 + 0.005167 + 0.005041 + 0.005292 + 0.004916 + 0.005417 + 0.005083 + 0.005291 + 0.00525 + 0.005041 + 0.005084 + 0.005 + 0.005334 + 0.005459 + 0.005375 + 0.0055 + 0.00525 + 0.005083 + 0.005042 + 0.005166 + 0.005375 + 0.005083 + 0.005208 + 0.004959 + 0.005083 + 0.005125 + 0.005292 + 0.005375 + 0.008834 + 0.00575 + 0.00575 + 0.006 + 0.005459 + 0.005167 + 0.005125 + 0.0045 + 0.006209 + 0.006208 + 0.006083 + 0.00925 + 0.006667 + 0.010833 + 0.006333 + 0.006458 + 0.005875 + 0.005542 + 0.00575 + 0.00575 + 0.005334 + 0.005667 + 0.005541 + 0.009792 + 0.005459 + 0.005708 + 0.006125 + 0.00575 + 0.005625 + 0.005333 + 0.005625 + 0.005833 + 0.005708 + 0.005375 + 0.005334 + 0.006167 + 0.005833 + 0.00575 + 0.008125 + 0.006625 + 0.006209 + 0.005959 + 0.005834 + 0.005917 + 0.006375 + 0.006167 + 0.00875 + 0.006334 + 0.006708 + 0.006541 + 0.00625 + 0.005458 + 0.005083 + 0.005 + 0.004667 + 0.005042 + 0.004833 + 0.005458 + 0.005208 + 0.005125 + 0.005208 + 0.005125 + 0.00525 + 0.005208 + 0.005 + 0.004875 + 0.005125 + 0.005 + 0.005042 + 0.004791 + 0.004834 + 0.005 + 0.005042 + 0.005083 + 0.005125 + 0.005292 + 0.005208 + 0.004958 + 0.005166 + 0.005042 + 0.004709 + 0.004375 + 0.004917 + 0.005333 + 0.005 + 0.004875 + 0.004708 + 0.004833 + 0.005167 + 0.005125 + 0.005458 + 0.004917 + 0.004959 + 0.004916 + 0.005125 + 0.004875 + 0.004917 + 0.005041 + 0.005042 + 0.005459 + 0.00575 + 0.005459 + 0.005209 + 0.005417 + 0.005583 + 0.005209 + 0.009834 + 0.005541 + 0.00625 + 0.005917 + 0.005791 + 0.005417 + 0.006167 + 0.008916 + 0.0065 + 0.006375 + 0.005208 + 0.005292 + 0.005208 + 0.006458 + 0.008292 + 0.008166 + 0.006375 + 0.006958 + 0.007042 + 0.008541 + 0.007959 + 0.007959 + 0.007291 + 0.007083 + 0.006583 + 0.009792 + 0.007292 + 0.006875 + 0.005833 + 0.0065 + 0.007666 + 0.008 + 0.007166 + 0.006875 + 0.011292 + 0.008083 + 0.009208 + 0.005292 + 0.006375 + 0.006125 + 0.005917 + 0.009667 + 0.006833 + 0.006417 + 0.005291 + 0.00575 + 0.004958 + 0.005333 + 0.005 + 0.005125 + 0.005209 + 0.004916 + 0.005 + 0.005333 + 0.004917 + 0.005208 + 0.005458 + 0.005584 + 0.004834 + 0.0055 + 0.004875 + 0.005 + 0.005208 + 0.005208 + 0.005166 + 0.005292 + 0.005 + 0.004959 + 0.004916 + 0.005625 + 0.004791 + 0.004917 + 0.005166 + 0.0055 + 0.004958 + 0.004916 + 0.004833 + 0.005292 + 0.004875 + 0.004916 + 0.005084 + 0.004917 + 0.005083 + 0.005083 + 0.005083 + 0.005 + 0.004667 + 0.004917 + 0.005 + 0.004792 + 0.00475 + 0.005041 + 0.005125 + 0.005209 + 0.005375 + 0.00525 + 0.005541 + 0.005542 + 0.005125 + 0.005542 + 0.005083 + 0.005417 + 0.005542 + 0.0095 + 0.006375 + 0.006 + 0.00625 + 0.005791 + 0.005959 + 0.00925 + 0.005625 + 0.0055 + 0.005875 + 0.00525 + 0.005167 + 0.005333 + 0.005166 + 0.005167 + 0.00525 + 0.005208 + 0.005792 + 0.005334 + 0.0055 + 0.005208 + 0.005375 + 0.004875 + 0.0055 + 0.005334 + 0.005125 + 0.009334 + 0.006667 + 0.006416 + 0.00625 + 0.006208 + 0.005917 + 0.006167 + 0.008833 + 0.0055 + 0.005959 + 0.006208 + 0.005958 + 0.005666 + 0.006042 + 0.00925 + 0.006375 + 0.005291 + 0.00475 + 0.005083 + 0.004959 + 0.004917 + 0.004875 + 0.004875 + 0.005709 + 0.005458 + 0.004792 + 0.004667 + 0.005125 + 0.004792 + 0.005083 + 0.004583 + 0.004833 + 0.005333 + 0.004917 + 0.00475 + 0.004708 + 0.005083 + 0.004917 + 0.004834 + 0.004834 + 0.00475 + 0.004709 + 0.004958 + 0.005042 + 0.005125 + 0.004459 + 0.005 + 0.004625 + 0.005 + 0.004333 + 0.004875 + 0.00475 + 0.00425 + 0.004417 + 0.004834 + 0.004833 + 0.00475 + 0.004709 + 0.004667 + 0.004792 + 0.004875 + 0.004375 + 0.004625 + 0.004666 + 0.005042 + 0.0085 + 0.005125 + 0.005459 + 0.004709 + 0.00775 + 0.004792 + 0.004791 + 0.010708 + 0.005292 + 0.009375 + 0.005792 + 0.008542 + 0.005958 + 0.006042 + 0.005541 + 0.005292 + 0.005416 + 0.005333 + 0.008042 + 0.004875 + 0.005083 + 0.005417 + 0.004875 + 0.005459 + 0.005417 + 0.005083 + 0.011583 + 0.005083 + 0.005583 + 0.00525 + 0.005125 + 0.005333 + 0.005 + 0.005333 + 0.004583 + 0.004916 + 0.009125 + 0.005708 + 0.00575 + 0.012667 + 0.005791 + 0.006083 + 0.005167 + 0.005083 + 0.006083 + 0.005416 + 0.0055 + 0.012458 + 0.00625 + 0.006292 + 0.005875 + 0.006 + 0.004959 + 0.004542 + 0.004792 + 0.005167 + 0.004791 + 0.004542 + 0.004875 + 0.00475 + 0.004833 + 0.004208 + 0.004792 + 0.005334 + 0.005 + 0.005167 + 0.004584 + 0.004875 + 0.004875 + 0.004625 + 0.004459 + 0.004834 + 0.004791 + 0.005375 + 0.004458 + 0.004875 + 0.004458 + 0.004875 + 0.004375 + 0.005083 + 0.004333 + 0.005 + 0.004708 + 0.004875 + 0.004416 + 0.004458 + 0.004417 + 0.004333 + 0.004791 + 0.004833 + 0.0045 + 0.004709 + 0.0075 + 0.004708 + 0.004958 + 0.004583 + 0.004625 + 0.004708 + 0.004875 + 0.004958 + 0.008291 + 0.005208 + 0.005042 + 0.005084 + 0.004875 + 0.004875 + 0.00525 + 0.007875 + 0.005083 + 0.006042 + 0.006084 + 0.00575 + 0.005625 + 0.005458 + 0.009916 + 0.006584 + 0.006334 + 0.005416 + 0.005 + 0.0055 + 0.004875 + 0.004833 + 0.004833 + 0.01075 + 0.004875 + 0.005292 + 0.005167 + 0.005334 + 0.005333 + 0.005333 + 0.004958 + 0.005292 + 0.007667 + 0.0055 + 0.0055 + 0.004542 + 0.006084 + 0.00625 + 0.006125 + 0.006 + 0.005958 + 0.008625 + 0.005833 + 0.005166 + 0.00625 + 0.005792 + 0.005708 + 0.005667 + 0.005291 + 0.0085 + 0.006 + 0.006 + 0.005292 + 0.004791 + 0.005334 + 0.004667 + 0.004958 + 0.00475 + 0.005084 + 0.004958 + 0.004916 + 0.004667 + 0.004625 + 0.005542 + 0.004666 + 0.004125 + 0.004916 + 0.005167 + 0.004875 + 0.00475 + 0.004667 + 0.00475 + 0.004791 + 0.005042 + 0.004959 + 0.004625 + 0.004625 + 0.005292 + 0.004667 + 0.005333 + 0.004584 + 0.005292 + 0.004583 + 0.004917 + 0.004708 + 0.004375 + 0.004583 + 0.004167 + 0.004334 + 0.00425 + 0.005 + 0.004416 + 0.004792 + 0.004959 + 0.004709 + 0.004834 + 0.004833 + 0.0045 + 0.004833 + 0.004708 + 0.005417 + 0.0055 + 0.005333 + 0.004709 + 0.005209 + 0.005125 + 0.005333 + 0.004875 + 0.005334 + 0.005834 + 0.008916 + 0.005875 + 0.006166 + 0.00575 + 0.005542 + 0.005584 + 0.005584 + 0.005458 + 0.004791 + 0.006875 + 0.004875 + 0.005125 + 0.004625 + 0.005125 + 0.005125 + 0.004958 + 0.005167 + 0.005209 + 0.005 + 0.005125 + 0.004833 + 0.005084 + 0.005084 + 0.005042 + 0.004958 + 0.005208 + 0.005833 + 0.008416 + 0.005584 + 0.006209 + 0.005459 + 0.005334 + 0.006125 + 0.005583 + 0.005042 + 0.007709 + 0.005958 + 0.005625 + 0.00575 + 0.005542 + 0.005541 + 0.005667 + 0.004958 + 0.004791 + 0.004709 + 0.005042 + 0.005167 + 0.004708 + 0.0045 + 0.004875 + 0.004625 + 0.00525 + 0.004583 + 0.00525 + 0.005042 + 0.004667 + 0.004792 + 0.004792 + 0.004542 + 0.004667 + 0.00475 + 0.005166 + 0.00525 + 0.005041 + 0.00475 + 0.004708 + 0.004708 + 0.004541 + 0.00475 + 0.005208 + 0.004917 + 0.004791 + 0.0045 + 0.005167 + 0.004709 + 0.004667 + 0.004 + 0.004375 + 0.004333 + 0.004333 + 0.004292 + 0.004583 + 0.004125 + 0.004875 + 0.004666 + 0.0045 + 0.004708 + 0.004916 + 0.004667 + 0.004833 + 0.00475 + 0.005125 + 0.008084 + 0.005125 + 0.005417 + 0.004958 + 0.005375 + 0.005084 + 0.005166 + 0.00525 + 0.006083 + 0.005833 + 0.005625 + 0.0085 + 0.006208 + 0.006292 + 0.0055 + 0.005208 + 0.004916 + 0.004625 + 0.004666 + 0.005125 + 0.005208 + 0.004833 + 0.004958 + 0.00525 + 0.005334 + 0.005459 + 0.004958 + 0.0055 + 0.008167 + 0.005 + 0.00575 + 0.0065 + 0.006125 + 0.005666 + 0.006333 + 0.006167 + 0.005875 + 0.006042 + 0.008583 + 0.006417 + 0.005958 + 0.006 + 0.005209 + 0.006 + 0.005375 + 0.005625 + 0.008375 + 0.005625 + 0.005667 + 0.0055 + 0.005208 + 0.004958 + 0.004541 + 0.004792 + 0.0045 + 0.004625 + 0.005083 + 0.00525 + 0.004875 + 0.004458 + 0.004916 + 0.004917 + 0.004958 + 0.004667 + 0.0045 + 0.004625 + 0.005167 + 0.004833 + 0.004541 + 0.005042 + 0.004708 + 0.004791 + 0.005167 + 0.00475 + 0.004542 + 0.004667 + 0.004917 + 0.004791 + 0.004917 + 0.004917 + 0.00475 + 0.004542 + 0.004834 + 0.005 + 0.004542 + 0.005083 + 0.005 + 0.004458 + 0.00425 + 0.004625 + 0.004625 + 0.004542 + 0.004541 + 0.004459 + 0.004958 + 0.004791 + 0.005125 + 0.004541 + 0.005542 + 0.005208 + 0.005541 + 0.005 + 0.005417 + 0.005166 + 0.005167 + 0.004875 + 0.005666 + 0.005125 + 0.00875 + 0.00625 + 0.006167 + 0.00575 + 0.005584 + 0.0055 + 0.00575 + 0.004875 + 0.004667 + 0.004875 + 0.007417 + 0.005125 + 0.005125 + 0.004667 + 0.005083 + 0.005459 + 0.005209 + 0.004417 + 0.005042 + 0.005167 + 0.00525 + 0.005291 + 0.005083 + 0.005208 + 0.005 + 0.00525 + 0.00475 + 0.005917 + 0.008417 + 0.006208 + 0.006 + 0.00575 + 0.005667 + 0.005916 + 0.004542 + 0.00575 + 0.0085 + 0.006375 + 0.005708 + 0.005541 + 0.005792 + 0.00575 + 0.005709 + 0.005292 + 0.005125 + 0.005041 + 0.005042 + 0.00475 + 0.005125 + 0.004833 + 0.004958 + 0.004333 + 0.004667 + 0.005 + 0.005334 + 0.004875 + 0.004958 + 0.00475 + 0.004833 + 0.00475 + 0.005041 + 0.005084 + 0.004916 + 0.005083 + 0.004875 + 0.004875 + 0.004584 + 0.004667 + 0.005083 + 0.004833 + 0.004833 + 0.004917 + 0.004875 + 0.004834 + 0.004625 + 0.004917 + 0.005916 + 0.0055 + 0.004583 + 0.005125 + 0.004916 + 0.004417 + 0.004833 + 0.004458 + 0.005084 + 0.004792 + 0.004708 + 0.005 + 0.005834 + 0.005042 + 0.017916 + 0.005708 + 0.009708 + 0.005667 + 0.006541 + 0.004917 + 0.005416 + 0.004375 + 0.005375 + 0.006209 + 0.007416 + 0.00675 + 0.007541 + 0.009792 + 0.007292 + 0.0075 + 0.006917 + 0.00675 + 0.006083 + 0.005833 + 0.005333 + 0.005583 + 0.005208 + 0.00575 + 0.008292 + 0.005291 + 0.005375 + 0.005875 + 0.006125 + 0.005583 + 0.005292 + 0.005667 + 0.005791 + 0.005584 + 0.005459 + 0.006333 + 0.005542 + 0.006833 + 0.006041 + 0.008833 + 0.006792 + 0.006916 + 0.006292 + 0.0065 + 0.004833 + 0.005958 + 0.006042 + 0.009458 + 0.006375 + 0.006166 + 0.006 + 0.005875 + 0.006959 + 0.005291 + 0.004875 + 0.005458 + 0.005791 + 0.005 + 0.006083 + 0.006208 + 0.005708 + 0.0045 + 0.004792 + 0.037333 + 0.015292 + 0.005625 + 0.005459 + 0.005167 + 0.005958 + 0.0055 + 0.005167 + 0.004875 + 0.005166 + 0.005625 + 0.005708 + 0.0055 + 0.005959 + 0.005709 + 0.006 + 0.005542 + 0.005584 + 0.005459 + 0.00525 + 0.005584 + 0.006 + 0.006292 + 0.005792 + 0.010208 + 0.005416 + 0.005417 + 0.005333 + 0.005458 + 0.005458 + 0.005375 + 0.005208 + 0.005 + 0.005292 + 0.005042 + 0.005291 + 0.005 + 0.00525 + 0.008083 + 0.005708 + 0.005667 + 0.006625 + 0.01025 + 0.007792 + 0.006709 + 0.007084 + 0.006125 + 0.01025 + 0.007125 + 0.006292 + 0.006541 + 0.009583 + 0.008792 + 0.007667 + 0.007083 + 0.00725 + 0.006708 + 0.006208 + 0.005958 + 0.005917 + 0.005417 + 0.005625 + 0.005791 + 0.007875 + 0.008458 + 0.008666 + 0.006667 + 0.006209 + 0.006875 + 0.006042 + 0.006041 + 0.006375 + 0.00725 + 0.008375 + 0.00975 + 0.00725 + 0.009292 + 0.008083 + 0.007542 + 0.007209 + 0.007792 + 0.0075 + 0.007416 + 0.009958 + 0.00825 + 0.007666 + 0.007625 + 0.007125 + 0.007208 + 0.00875 + 0.006708 + 0.005958 + 0.005292 + 0.005 + 0.005208 + 0.004875 + 0.005083 + 0.005333 + 0.005125 + 0.005667 + 0.005291 + 0.00475 + 0.005416 + 0.005125 + 0.005208 + 0.004958 + 0.0045 + 0.004917 + 0.00525 + 0.005 + 0.005 + 0.005084 + 0.005042 + 0.005041 + 0.005208 + 0.00525 + 0.005209 + 0.005375 + 0.005125 + 0.005167 + 0.005042 + 0.004958 + 0.005125 + 0.005125 + 0.005209 + 0.005125 + 0.004917 + 0.005 + 0.00525 + 0.004667 + 0.00525 + 0.005041 + 0.005167 + 0.004667 + 0.004958 + 0.005166 + 0.005125 + 0.004458 + 0.006458 + 0.005542 + 0.005292 + 0.005208 + 0.005125 + 0.005792 + 0.005167 + 0.005542 + 0.005375 + 0.005792 + 0.007375 + 0.007334 + 0.008292 + 0.00825 + 0.008084 + 0.007625 + 0.008209 + 0.007041 + 0.005792 + 0.005666 + 0.007083 + 0.006958 + 0.007167 + 0.006209 + 0.006084 + 0.006625 + 0.007583 + 0.01 + 0.00825 + 0.00725 + 0.007792 + 0.006875 + 0.006209 + 0.006709 + 0.006 + 0.006833 + 0.006792 + 0.006875 + 0.006083 + 0.008 + 0.007958 + 0.007791 + 0.007417 + 0.007167 + 0.005416 + 0.006167 + 0.006958 + 0.008583 + 0.00825 + 0.007834 + 0.007667 + 0.006416 + 0.005292 + 0.00575 + 0.005916 + 0.005459 + 0.005666 + 0.005292 + 0.005459 + 0.0065 + 0.006416 + 0.005375 + 0.005291 + 0.005959 + 0.006375 + 0.005334 + 0.005542 + 0.005791 + 0.005292 + 0.005125 + 0.004833 + 0.004875 + 0.005042 + 0.005458 + 0.005208 + 0.005833 + 0.004917 + 0.005041 + 0.005042 + 0.005375 + 0.005834 + 0.006084 + 0.006833 + 0.007583 + 0.006792 + 0.006666 + 0.006125 + 0.006542 + 0.0065 + 0.006875 + 0.006208 + 0.005708 + 0.005166 + 0.00525 + 0.005208 + 0.005166 + 0.004834 + 0.00525 + 0.005584 + 0.005709 + 0.005375 + 0.005625 + 0.005292 + 0.005334 + 0.005458 + 0.007166 + 0.006666 + 0.006208 + 0.006417 + 0.006291 + 0.006542 + 0.006458 + 0.006166 + 0.006417 + 0.008209 + 0.007625 + 0.007041 + 0.006708 + 0.006333 + 0.006292 + 0.007042 + 0.006541 + 0.006542 + 0.005292 + 0.0055 + 0.005416 + 0.004292], + :filter-tx-shared-memo + [0.004583 + 0.0035 + 0.003541 + 0.003416 + 0.003833 + 0.004 + 0.003875 + 0.003958 + 0.003541 + 0.003208 + 0.003083 + 0.003625 + 0.003333 + 0.003458 + 0.00325 + 0.003541 + 0.003708 + 0.003209 + 0.003209 + 0.003959 + 0.003792 + 0.003083 + 0.003166 + 0.003375 + 0.003417 + 0.003625 + 0.003291 + 0.003459 + 0.00325 + 0.003375 + 0.003167 + 0.003958 + 0.004083 + 0.003791 + 0.004083 + 0.003334 + 0.003167 + 0.004125 + 0.00375 + 0.003917 + 0.00375 + 0.007583 + 0.004625 + 0.004875 + 0.004292 + 0.004375 + 0.005584 + 0.005042 + 0.00425 + 0.0045 + 0.005459 + 0.005209 + 0.005209 + 0.004959 + 0.004584 + 0.004 + 0.004542 + 0.003709 + 0.003792 + 0.003583 + 0.003583 + 0.004375 + 0.004125 + 0.004083 + 0.004292 + 0.004625 + 0.004667 + 0.004542 + 0.004333 + 0.004333 + 0.004708 + 0.00425 + 0.004208 + 0.004625 + 0.0045 + 0.004084 + 0.005875 + 0.005791 + 0.005667 + 0.005708 + 0.005333 + 0.004833 + 0.004583 + 0.00575 + 0.005625 + 0.005375 + 0.005208 + 0.005291 + 0.005083 + 0.003458 + 0.003084 + 0.0035 + 0.003334 + 0.003833 + 0.003041 + 0.004208 + 0.004125 + 0.005 + 0.004417 + 0.004083 + 0.003875 + 0.004042 + 0.004375 + 0.004375 + 0.004208 + 0.003958 + 0.004083 + 0.004625 + 0.004792 + 0.00425 + 0.00425 + 0.0045 + 0.003708 + 0.004083 + 0.004084 + 0.004459 + 0.004167 + 0.004167 + 0.004583 + 0.004416 + 0.003959 + 0.00425 + 0.004 + 0.003958 + 0.004125 + 0.004083 + 0.004208 + 0.004 + 0.004166 + 0.003958 + 0.004125 + 0.004208 + 0.003917 + 0.004334 + 0.004209 + 0.004166 + 0.003625 + 0.004583 + 0.004708 + 0.004417 + 0.005333 + 0.005209 + 0.005375 + 0.004917 + 0.005166 + 0.005209 + 0.00475 + 0.004958 + 0.005375 + 0.005042 + 0.005166 + 0.006333 + 0.0065 + 0.005708 + 0.005041 + 0.004833 + 0.004917 + 0.004834 + 0.004709 + 0.004084 + 0.004167 + 0.003875 + 0.004209 + 0.004167 + 0.004458 + 0.004625 + 0.00425 + 0.005208 + 0.005291 + 0.00525 + 0.005 + 0.005333 + 0.005292 + 0.004834 + 0.005125 + 0.00525 + 0.004917 + 0.006083 + 0.006125 + 0.005834 + 0.006208 + 0.005458 + 0.005958 + 0.005167 + 0.0065 + 0.006292 + 0.005667 + 0.005917 + 0.005958 + 0.004959 + 0.003959 + 0.004375 + 0.004292 + 0.003708 + 0.004042 + 0.004041 + 0.004792 + 0.005166 + 0.004 + 0.004 + 0.004333 + 0.004083 + 0.003958 + 0.003959 + 0.004166 + 0.004375 + 0.004084 + 0.00375 + 0.004042 + 0.003959 + 0.004166 + 0.004 + 0.003791 + 0.004375 + 0.003875 + 0.003791 + 0.004 + 0.004125 + 0.003791 + 0.004375 + 0.004125 + 0.004292 + 0.004084 + 0.004292 + 0.003875 + 0.004083 + 0.003958 + 0.004084 + 0.004084 + 0.004416 + 0.00375 + 0.004416 + 0.004 + 0.003667 + 0.00375 + 0.004416 + 0.004334 + 0.004291 + 0.004584 + 0.004292 + 0.004125 + 0.004208 + 0.004292 + 0.005917 + 0.005208 + 0.005166 + 0.005375 + 0.005917 + 0.005709 + 0.004917 + 0.005 + 0.004916 + 0.00975 + 0.006083 + 0.0055 + 0.004958 + 0.005084 + 0.005209 + 0.004708 + 0.004375 + 0.00425 + 0.004 + 0.003916 + 0.0035 + 0.004083 + 0.004667 + 0.004292 + 0.00425 + 0.003917 + 0.004291 + 0.004334 + 0.005041 + 0.004875 + 0.005125 + 0.006084 + 0.006084 + 0.005625 + 0.005083 + 0.005125 + 0.00625 + 0.005958 + 0.0055 + 0.006125 + 0.006 + 0.005541 + 0.005292 + 0.006125 + 0.005958 + 0.006 + 0.006 + 0.004875 + 0.004417 + 0.004291 + 0.004042 + 0.0035 + 0.00375 + 0.004125 + 0.004208 + 0.004292 + 0.003625 + 0.004 + 0.003958 + 0.004125 + 0.004 + 0.004 + 0.004084 + 0.003916 + 0.003667 + 0.003584 + 0.00375 + 0.004 + 0.004084 + 0.003959 + 0.00425 + 0.003542 + 0.003959 + 0.003584 + 0.004458 + 0.004 + 0.004 + 0.003583 + 0.004166 + 0.003917 + 0.003916 + 0.003583 + 0.004041 + 0.003958 + 0.004083 + 0.003791 + 0.004167 + 0.00375 + 0.004084 + 0.004 + 0.00425 + 0.004625 + 0.0045 + 0.004334 + 0.004959 + 0.004667 + 0.004708 + 0.004708 + 0.004375 + 0.004417 + 0.004208 + 0.004125 + 0.003958 + 0.006209 + 0.006458 + 0.005708 + 0.005958 + 0.005667 + 0.005292 + 0.005166 + 0.00575 + 0.006042 + 0.005208 + 0.005167 + 0.005166 + 0.005042 + 0.004166 + 0.004333 + 0.004333 + 0.004375 + 0.004292 + 0.004375 + 0.003709 + 0.004167 + 0.004458 + 0.004375 + 0.004166 + 0.004125 + 0.008375 + 0.00525 + 0.004958 + 0.006166 + 0.00575 + 0.005875 + 0.005042 + 0.005041 + 0.005959 + 0.006417 + 0.005125 + 0.005458 + 0.006125 + 0.005375 + 0.005375 + 0.006667 + 0.014584 + 0.006125 + 0.006209 + 0.0045 + 0.003875 + 0.004625 + 0.004458 + 0.004917 + 0.004417 + 0.003791 + 0.004167 + 0.0055 + 0.00525 + 0.004833 + 0.008084 + 0.005459 + 0.005042 + 0.005125 + 0.004917 + 0.004 + 0.003875 + 0.0035 + 0.003417 + 0.003583 + 0.0035 + 0.004042 + 0.004125 + 0.003875 + 0.004125 + 0.004167 + 0.003625 + 0.003708 + 0.00425 + 0.004166 + 0.004 + 0.003958 + 0.004042 + 0.003791 + 0.003875 + 0.003875 + 0.003916 + 0.00375 + 0.004 + 0.003958 + 0.004584 + 0.0035 + 0.003625 + 0.003708 + 0.004334 + 0.00425 + 0.004208 + 0.004792 + 0.005042 + 0.005041 + 0.005708 + 0.005334 + 0.005291 + 0.005334 + 0.006792 + 0.005916 + 0.006208 + 0.006667 + 0.006375 + 0.006833 + 0.006333 + 0.007459 + 0.006917 + 0.005916 + 0.0045 + 0.003792 + 0.004875 + 0.004458 + 0.004292 + 0.004292 + 0.004125 + 0.004375 + 0.004333 + 0.00475 + 0.004208 + 0.004458 + 0.003916 + 0.005208 + 0.005583 + 0.005208 + 0.005917 + 0.0055 + 0.005958 + 0.006625 + 0.006708 + 0.005833 + 0.005333 + 0.006625 + 0.00675 + 0.006625 + 0.006291 + 0.005333 + 0.006542 + 0.006084 + 0.007 + 0.006833 + 0.006375 + 0.00625 + 0.006584 + 0.005125 + 0.004667 + 0.005416 + 0.004375 + 0.003916 + 0.003792 + 0.004292 + 0.003833 + 0.004083 + 0.004917 + 0.003917 + 0.004084 + 0.004333 + 0.003791 + 0.004 + 0.003875 + 0.004042 + 0.0035 + 0.003709 + 0.003709 + 0.003792 + 0.00375 + 0.003417 + 0.003834 + 0.003875 + 0.003958 + 0.004 + 0.004167 + 0.004167 + 0.004333 + 0.004125 + 0.003625 + 0.003959 + 0.003875 + 0.003667 + 0.003916 + 0.003875 + 0.004167 + 0.003916 + 0.003959 + 0.003875 + 0.003708 + 0.004125 + 0.004292 + 0.003917 + 0.004333 + 0.004041 + 0.003958 + 0.004625 + 0.004042 + 0.004125 + 0.003917 + 0.004208 + 0.005375 + 0.006667 + 0.005792 + 0.005167 + 0.005791 + 0.006792 + 0.006584 + 0.00575 + 0.004708 + 0.006209 + 0.006375 + 0.006792 + 0.005458 + 0.005583 + 0.005458 + 0.006292 + 0.006125 + 0.005208 + 0.004416 + 0.004208 + 0.004333 + 0.004375 + 0.004083 + 0.004042 + 0.004167 + 0.004125 + 0.005333 + 0.005625 + 0.005792 + 0.005083 + 0.00575 + 0.006375 + 0.00625 + 0.006041 + 0.006125 + 0.006334 + 0.006959 + 0.006584 + 0.006375 + 0.0055 + 0.006708 + 0.006083 + 0.007042 + 0.006292 + 0.006583 + 0.006167 + 0.00675 + 0.004875 + 0.004542 + 0.004958 + 0.005042 + 0.004292 + 0.003791 + 0.004 + 0.004625 + 0.004541 + 0.00375 + 0.004042 + 0.004292 + 0.004541 + 0.00425 + 0.004417 + 0.004 + 0.004084 + 0.004125 + 0.004709 + 0.003792 + 0.003917 + 0.003959 + 0.003958 + 0.003708 + 0.003792 + 0.004084 + 0.004167 + 0.003916 + 0.004042 + 0.004292 + 0.00425 + 0.004083 + 0.004292 + 0.00475 + 0.00425 + 0.004042 + 0.004292 + 0.004041 + 0.004208 + 0.004125 + 0.004042 + 0.00425 + 0.003791 + 0.00425 + 0.004 + 0.004625 + 0.004 + 0.003833 + 0.004458 + 0.004083 + 0.004 + 0.003875 + 0.004208 + 0.0055 + 0.00525 + 0.004917 + 0.005667 + 0.005458 + 0.005625 + 0.005292 + 0.005666 + 0.005208 + 0.007042 + 0.006583 + 0.006625 + 0.005292 + 0.005459 + 0.00575 + 0.0055 + 0.0055 + 0.005208 + 0.005 + 0.004459 + 0.004459 + 0.004417 + 0.004416 + 0.004208 + 0.004459 + 0.004166 + 0.003958 + 0.004292 + 0.005792 + 0.00575 + 0.00575 + 0.005417 + 0.006375 + 0.006583 + 0.006167 + 0.006 + 0.005792 + 0.006666 + 0.006708 + 0.0055 + 0.006125 + 0.006375 + 0.006667 + 0.006083 + 0.00775 + 0.007167 + 0.006708 + 0.00625 + 0.005125 + 0.005 + 0.004959 + 0.004959 + 0.005041 + 0.005333 + 0.003875 + 0.0045 + 0.004041 + 0.0045 + 0.003583 + 0.0045 + 0.004125 + 0.004166 + 0.004375 + 0.004167 + 0.004125 + 0.004458 + 0.004 + 0.00425 + 0.004125 + 0.003875 + 0.004042 + 0.003625 + 0.00375 + 0.003917 + 0.00375 + 0.003917 + 0.00425 + 0.004458 + 0.004291 + 0.004167 + 0.004334 + 0.004125 + 0.004125 + 0.003833 + 0.004208 + 0.004291 + 0.004042 + 0.003875 + 0.004125 + 0.003709 + 0.00425 + 0.003875 + 0.003916 + 0.004125 + 0.004458 + 0.004083 + 0.004875 + 0.004583 + 0.003958 + 0.004083 + 0.004125 + 0.004208 + 0.00425 + 0.004292 + 0.004333 + 0.006917 + 0.006375 + 0.006416 + 0.006 + 0.006583 + 0.006166 + 0.006583 + 0.00725 + 0.00575 + 0.005542 + 0.005583 + 0.005666 + 0.00525 + 0.00525 + 0.005708 + 0.005125 + 0.004416 + 0.004042 + 0.004542 + 0.004417 + 0.004167 + 0.004 + 0.004166 + 0.004709 + 0.00425 + 0.004125 + 0.004458 + 0.006583 + 0.006333 + 0.006417 + 0.006792 + 0.006583 + 0.006292 + 0.007416 + 0.005417 + 0.005125 + 0.006542 + 0.006459 + 0.007125 + 0.0065 + 0.006458 + 0.007917 + 0.006417 + 0.005375 + 0.004625 + 0.005 + 0.005042 + 0.004834 + 0.004625 + 0.004708 + 0.004875 + 0.004459 + 0.003917 + 0.004084 + 0.003875 + 0.004667 + 0.004083 + 0.004292 + 0.004292 + 0.004041 + 0.003959 + 0.003917 + 0.004167 + 0.004167 + 0.004042 + 0.003584 + 0.004375 + 0.004084 + 0.003833 + 0.003667 + 0.004208 + 0.003833 + 0.003917 + 0.004375 + 0.003917 + 0.004083 + 0.003958 + 0.003542 + 0.004125 + 0.003833 + 0.00475 + 0.004917 + 0.005833 + 0.015708 + 0.005333 + 0.004417 + 0.004333 + 0.004209 + 0.0045 + 0.00425 + 0.003959 + 0.004625 + 0.0045 + 0.004125 + 0.004542 + 0.004 + 0.00425 + 0.003834 + 0.004 + 0.003958 + 0.003667 + 0.005458 + 0.007125 + 0.006125 + 0.005833 + 0.006208 + 0.006542 + 0.00575 + 0.004458 + 0.004458 + 0.005167 + 0.004208 + 0.004167 + 0.004458 + 0.0055 + 0.005166 + 0.005125 + 0.005041 + 0.005167 + 0.005333 + 0.005292 + 0.005333 + 0.004666 + 0.005 + 0.005208 + 0.004167 + 0.004334 + 0.005458 + 0.0055 + 0.00675 + 0.00675 + 0.007125 + 0.006333 + 0.006125 + 0.006209 + 0.0055 + 0.006458 + 0.007042 + 0.006792 + 0.006417 + 0.006083 + 0.006125 + 0.004792 + 0.004542 + 0.003875 + 0.00375 + 0.003958 + 0.004667 + 0.004125 + 0.004208 + 0.003917 + 0.005084 + 0.004417 + 0.004291 + 0.003875 + 0.010375 + 0.004541 + 0.00425 + 0.004083 + 0.004708 + 0.011125 + 0.004625 + 0.004041 + 0.004 + 0.004333 + 0.004459 + 0.004042 + 0.004291 + 0.004875 + 0.004458 + 0.005042 + 0.004375 + 0.0045 + 0.004208 + 0.00475 + 0.004208 + 0.005625 + 0.004958 + 0.004667 + 0.004875 + 0.004834 + 0.004542 + 0.004834 + 0.004916 + 0.004167 + 0.004416 + 0.004667 + 0.0045 + 0.004458 + 0.004167 + 0.004208 + 0.005167 + 0.004334 + 0.004584 + 0.004584 + 0.005167 + 0.0065 + 0.005292 + 0.00475 + 0.0055 + 0.004958 + 0.006125 + 0.005709 + 0.006042 + 0.00575 + 0.006791 + 0.008542 + 0.007791 + 0.006833 + 0.006333 + 0.006083 + 0.005583 + 0.005041 + 0.004542 + 0.004875 + 0.004375 + 0.004667 + 0.004291 + 0.004958 + 0.004083 + 0.004958 + 0.006625 + 0.006708 + 0.005708 + 0.005291 + 0.006042 + 0.005875 + 0.004959 + 0.006667 + 0.007 + 0.006542 + 0.007042 + 0.008541 + 0.008 + 0.007083 + 0.005333 + 0.006375 + 0.006125 + 0.005375 + 0.006125 + 0.00675 + 0.0065 + 0.006375 + 0.006042 + 0.004916 + 0.005625 + 0.005166 + 0.004833 + 0.004208 + 0.004041 + 0.004042 + 0.004458 + 0.003708 + 0.003917 + 0.004 + 0.004541 + 0.004208 + 0.004083 + 0.004041 + 0.00425 + 0.003958 + 0.00475 + 0.003958 + 0.004417 + 0.004875 + 0.003667 + 0.004166 + 0.004167 + 0.004291 + 0.003792 + 0.003958 + 0.003875 + 0.003958 + 0.004375 + 0.004208 + 0.004083 + 0.004 + 0.004083 + 0.004291 + 0.004375 + 0.004333 + 0.004084 + 0.004166 + 0.003792 + 0.004125 + 0.003625 + 0.004041 + 0.003792 + 0.004 + 0.004125 + 0.003959 + 0.004 + 0.004084 + 0.0045 + 0.004125 + 0.004417 + 0.004333 + 0.004208 + 0.004333 + 0.005917 + 0.004584 + 0.005917 + 0.005916 + 0.005667 + 0.00575 + 0.004792 + 0.00675 + 0.006041 + 0.006459 + 0.005334 + 0.005084 + 0.004459 + 0.004291 + 0.004375 + 0.004291 + 0.004042 + 0.004333 + 0.004291 + 0.004208 + 0.004417 + 0.004291 + 0.006292 + 0.005125 + 0.005417 + 0.00525 + 0.005167 + 0.004791 + 0.004333 + 0.004834 + 0.005042 + 0.004875 + 0.004958 + 0.006125 + 0.006458 + 0.005667 + 0.005584 + 0.006125 + 0.005333 + 0.004708 + 0.005875 + 0.00625 + 0.006333 + 0.005875 + 0.005958 + 0.004417 + 0.004084 + 0.003959 + 0.003958 + 0.003417 + 0.004 + 0.00375 + 0.004167 + 0.003916 + 0.004084 + 0.003834 + 0.003709 + 0.004084 + 0.00375 + 0.003959 + 0.003625 + 0.004083 + 0.003959 + 0.003917 + 0.003625 + 0.00425 + 0.003959 + 0.003958 + 0.003916 + 0.004125 + 0.003959 + 0.004042 + 0.004166 + 0.003917 + 0.003833 + 0.004042 + 0.003917 + 0.003875 + 0.003875 + 0.003958 + 0.003959 + 0.004041 + 0.004083 + 0.004 + 0.004375 + 0.003833 + 0.003708 + 0.004042 + 0.0045 + 0.003917 + 0.003833 + 0.003916 + 0.004125 + 0.004333 + 0.0045 + 0.004291 + 0.004417 + 0.004208 + 0.0055 + 0.00525 + 0.005208 + 0.004917 + 0.005625 + 0.005667 + 0.005 + 0.004625 + 0.005542 + 0.006334 + 0.006125 + 0.005708 + 0.005125 + 0.0045 + 0.004375 + 0.004167 + 0.004041 + 0.004292 + 0.004 + 0.00425 + 0.004208 + 0.004584 + 0.005542 + 0.004833 + 0.005 + 0.005375 + 0.004709 + 0.004959 + 0.004292 + 0.004125 + 0.004083 + 0.004625 + 0.004875 + 0.004583 + 0.007667 + 0.006125 + 0.005583 + 0.004583 + 0.005 + 0.00425 + 0.005166 + 0.004959 + 0.006042 + 0.006125 + 0.005959 + 0.005625 + 0.005667 + 0.004417 + 0.00425 + 0.003916 + 0.004 + 0.004375 + 0.004208 + 0.003792 + 0.003291 + 0.004125 + 0.0035 + 0.003875 + 0.004875 + 0.004584 + 0.004 + 0.004708 + 0.003917 + 0.003792 + 0.003917 + 0.004459 + 0.003708 + 0.004 + 0.004 + 0.004333 + 0.003875 + 0.004583 + 0.004083 + 0.004208 + 0.004166 + 0.004459 + 0.004583 + 0.004875 + 0.00475 + 0.003625 + 0.003875 + 0.007417 + 0.00425 + 0.004125 + 0.004167 + 0.004125 + 0.004417 + 0.005125 + 0.005083 + 0.005042 + 0.005375 + 0.005583 + 0.005292 + 0.005542 + 0.005791 + 0.005292 + 0.0065 + 0.005625 + 0.006291 + 0.006958 + 0.006 + 0.005791 + 0.005583 + 0.004875 + 0.004917 + 0.005416 + 0.005083 + 0.005375 + 0.005 + 0.005792 + 0.006459 + 0.006625 + 0.005792 + 0.006125 + 0.005625 + 0.005583 + 0.005375 + 0.004667 + 0.004583 + 0.004708 + 0.004083 + 0.004416 + 0.004292 + 0.004625 + 0.004167 + 0.004583 + 0.004375 + 0.005916 + 0.0055 + 0.005541 + 0.00625 + 0.007166 + 0.006458 + 0.00725 + 0.007041 + 0.006083 + 0.009333 + 0.007375 + 0.006583 + 0.005625 + 0.00675 + 0.0055 + 0.005625 + 0.006625 + 0.006792 + 0.006333 + 0.006291 + 0.005083 + 0.005125 + 0.00525 + 0.005208 + 0.005042 + 0.005125 + 0.004709 + 0.004958 + 0.004667 + 0.003875 + 0.003875 + 0.004 + 0.004459 + 0.004084 + 0.004416 + 0.003792 + 0.004583 + 0.004167 + 0.004125 + 0.004042 + 0.004583 + 0.003708 + 0.003625 + 0.004 + 0.004375 + 0.003958 + 0.004083 + 0.004209 + 0.004208 + 0.003958 + 0.004291 + 0.003833 + 0.003708 + 0.00375 + 0.003792 + 0.00375 + 0.003875 + 0.003792 + 0.003917 + 0.004125 + 0.004167 + 0.003958 + 0.004 + 0.00425 + 0.003791 + 0.003667 + 0.004083 + 0.003958 + 0.004167 + 0.004083 + 0.003958 + 0.0045 + 0.004084 + 0.004292 + 0.004041 + 0.004083 + 0.003917 + 0.004041 + 0.006625 + 0.005916 + 0.005375 + 0.004666 + 0.004833 + 0.004959 + 0.005833 + 0.005875 + 0.006 + 0.005334 + 0.005625 + 0.005708 + 0.005708 + 0.005792 + 0.006083 + 0.006333 + 0.006375 + 0.005625 + 0.006125 + 0.00525 + 0.005042 + 0.00475 + 0.005166 + 0.005458 + 0.005542 + 0.005042 + 0.004583 + 0.006667 + 0.00775 + 0.006708 + 0.006125 + 0.006041 + 0.005375 + 0.005125 + 0.00475 + 0.007916 + 0.007208 + 0.006375 + 0.00625 + 0.006333 + 0.00525 + 0.00525 + 0.005958 + 0.005042 + 0.005209 + 0.0055 + 0.00525 + 0.006375 + 0.005625 + 0.005667 + 0.004792 + 0.005167 + 0.004459 + 0.005125 + 0.004542 + 0.0045 + 0.004667 + 0.004417 + 0.004625 + 0.004875 + 0.004833 + 0.005042 + 0.005541 + 0.005792 + 0.004625 + 0.004917 + 0.005208 + 0.004917 + 0.004625 + 0.004166 + 0.00475 + 0.004375 + 0.004708 + 0.004667 + 0.004708 + 0.004167 + 0.004 + 0.004292 + 0.004209 + 0.00425 + 0.004333 + 0.004167 + 0.00425 + 0.004875 + 0.005042 + 0.005833 + 0.005041 + 0.00525 + 0.004916 + 0.0055 + 0.005958 + 0.006292 + 0.005625 + 0.006459 + 0.005542 + 0.006125 + 0.0055 + 0.00525 + 0.00575 + 0.006541 + 0.009709 + 0.010625 + 0.009667 + 0.009542 + 0.009125 + 0.009334 + 0.00825 + 0.0085 + 0.008792 + 0.007167 + 0.006416 + 0.005583 + 0.005042 + 0.005375 + 0.005833 + 0.005625 + 0.007666 + 0.007209 + 0.006959 + 0.005792 + 0.00625 + 0.005 + 0.00625 + 0.006083 + 0.006 + 0.005375 + 0.004167 + 0.005625 + 0.005459 + 0.004834 + 0.006375 + 0.006792 + 0.005833 + 0.006 + 0.004958 + 0.005542 + 0.006125 + 0.007375 + 0.006208 + 0.007292 + 0.006625 + 0.006375 + 0.006625 + 0.0055 + 0.005041 + 0.005417 + 0.004375 + 0.004125 + 0.004125 + 0.004166 + 0.004667 + 0.004083 + 0.004375 + 0.004041 + 0.004709 + 0.003959 + 0.003959 + 0.004291 + 0.004583 + 0.004209 + 0.004417 + 0.004333 + 0.00375 + 0.003958 + 0.00375 + 0.004041 + 0.004209 + 0.003917 + 0.004167 + 0.003917 + 0.004167 + 0.004 + 0.00425 + 0.004667 + 0.004167 + 0.004333 + 0.003708 + 0.004042 + 0.004084 + 0.0045 + 0.004709 + 0.004875 + 0.004042 + 0.0045 + 0.004958 + 0.005458 + 0.005333 + 0.005208 + 0.005208 + 0.005542 + 0.004917 + 0.005709 + 0.006709 + 0.005334 + 0.004917 + 0.004958 + 0.00525 + 0.004917 + 0.005041 + 0.0045 + 0.005084 + 0.00525 + 0.005583 + 0.007333 + 0.006333 + 0.006417 + 0.006417 + 0.006333 + 0.005958 + 0.0055 + 0.005459 + 0.005459 + 0.005667 + 0.00425 + 0.005834 + 0.006125 + 0.005917 + 0.006041 + 0.005667 + 0.005459 + 0.006041 + 0.005834 + 0.00525 + 0.004458 + 0.004667 + 0.004833 + 0.004458 + 0.005166 + 0.0055 + 0.00675 + 0.006708 + 0.006875 + 0.006375 + 0.006625 + 0.006209 + 0.00575 + 0.008875 + 0.007459 + 0.006584 + 0.006459 + 0.006125 + 0.00675 + 0.008208 + 0.006 + 0.005208 + 0.005208 + 0.005708 + 0.005208 + 0.005125 + 0.005042 + 0.0055 + 0.004958 + 0.005125 + 0.004416 + 0.004458 + 0.004375 + 0.004 + 0.003792 + 0.004167 + 0.003958 + 0.003792 + 0.00425 + 0.00425 + 0.004208 + 0.004083 + 0.003875 + 0.004291 + 0.004209 + 0.004334 + 0.004 + 0.004333 + 0.004 + 0.004292 + 0.004333 + 0.005125 + 0.0055 + 0.005 + 0.004583 + 0.005 + 0.005291 + 0.005125 + 0.00475 + 0.005166 + 0.003958 + 0.00425 + 0.004375 + 0.0045 + 0.005541 + 0.004708 + 0.004958 + 0.004625 + 0.004125 + 0.004416 + 0.005 + 0.004417 + 0.0045 + 0.004375 + 0.004708 + 0.004375 + 0.00425 + 0.004542 + 0.005166 + 0.005 + 0.00675 + 0.006875 + 0.006708 + 0.006792 + 0.006625 + 0.005916 + 0.005458 + 0.005708 + 0.004542 + 0.004459 + 0.004417 + 0.004333 + 0.00625 + 0.005958 + 0.008708 + 0.007958 + 0.005541 + 0.005708 + 0.005292 + 0.005166 + 0.0045 + 0.004708 + 0.004292 + 0.005 + 0.005542 + 0.00525 + 0.007333 + 0.006792 + 0.006541 + 0.006708 + 0.006334 + 0.0065 + 0.006042 + 0.006375 + 0.005958 + 0.006625 + 0.006459 + 0.006042 + 0.006083 + 0.006291 + 0.005542 + 0.005125 + 0.004459 + 0.004417 + 0.004208 + 0.004083 + 0.004083 + 0.005542 + 0.005416 + 0.003917 + 0.004334 + 0.004416 + 0.004584 + 0.004291 + 0.003917 + 0.003958 + 0.00425 + 0.003791 + 0.009667 + 0.0045 + 0.003791 + 0.004125 + 0.003917 + 0.004042 + 0.004167 + 0.003875 + 0.003834 + 0.004334 + 0.00425 + 0.004292 + 0.004333 + 0.004042 + 0.004292 + 0.004416 + 0.004167 + 0.004125 + 0.004167 + 0.00425 + 0.004 + 0.00375 + 0.004 + 0.003875 + 0.00425 + 0.00425 + 0.004208 + 0.004083 + 0.004 + 0.004166 + 0.0045 + 0.0045 + 0.004583 + 0.005833 + 0.005791 + 0.005542 + 0.0055 + 0.005417 + 0.005625 + 0.0055 + 0.006625 + 0.005959 + 0.005 + 0.005 + 0.006209 + 0.007208 + 0.0065 + 0.005709 + 0.005041 + 0.005875 + 0.005292 + 0.0055 + 0.004584 + 0.004416 + 0.004375 + 0.0045 + 0.004458 + 0.003792 + 0.004541 + 0.004917 + 0.004667 + 0.006459 + 0.005291 + 0.005 + 0.005083 + 0.005167 + 0.004083 + 0.005291 + 0.005083 + 0.005459 + 0.004959 + 0.00625 + 0.006833 + 0.006292 + 0.006542 + 0.006791 + 0.006583 + 0.006125 + 0.007166 + 0.00675 + 0.006334 + 0.020209 + 0.014209 + 0.006209 + 0.005875 + 0.005458 + 0.00475 + 0.004083 + 0.00425 + 0.004084 + 0.004417 + 0.0035 + 0.004042 + 0.003708 + 0.004708 + 0.003875 + 0.003708 + 0.004 + 0.003792 + 0.003417 + 0.004 + 0.004167 + 0.003958 + 0.004125 + 0.004333 + 0.004625 + 0.005125 + 0.004917 + 0.005 + 0.005125 + 0.0055 + 0.004958 + 0.005125 + 0.005416 + 0.004875 + 0.0055 + 0.004916 + 0.004375 + 0.003708 + 0.004125 + 0.003958 + 0.00375 + 0.004125 + 0.003333 + 0.004541 + 0.005042 + 0.004667 + 0.004334 + 0.004 + 0.004 + 0.004333 + 0.004375 + 0.00475 + 0.004458 + 0.004375 + 0.00425 + 0.005959 + 0.005334 + 0.005209 + 0.005 + 0.006083 + 0.00575 + 0.006166 + 0.005208 + 0.006292 + 0.006833 + 0.005917 + 0.005916 + 0.005208 + 0.004709 + 0.004042 + 0.004375 + 0.003916 + 0.004584 + 0.00375 + 0.004291 + 0.003875 + 0.004542 + 0.005584 + 0.005417 + 0.005042 + 0.005084 + 0.005375 + 0.005292 + 0.004416 + 0.00475 + 0.00425 + 0.00575 + 0.005459 + 0.004917 + 0.005875 + 0.0065 + 0.005917 + 0.006167 + 0.004958 + 0.005625 + 0.005041 + 0.006417 + 0.00625 + 0.005833 + 0.005834 + 0.005708 + 0.005958 + 0.004042 + 0.003959 + 0.004209 + 0.004375 + 0.003959 + 0.003958 + 0.003958 + 0.004709 + 0.004041 + 0.003292 + 0.00425 + 0.003958 + 0.004083 + 0.004084 + 0.003792 + 0.003625 + 0.003792 + 0.004042 + 0.00375 + 0.003834 + 0.004 + 0.004167 + 0.003833 + 0.004375 + 0.004083 + 0.003791 + 0.003792 + 0.004125 + 0.004 + 0.0045 + 0.004958 + 0.004417 + 0.004667 + 0.004083 + 0.003875 + 0.003917 + 0.003875 + 0.003834 + 0.003958 + 0.003875 + 0.003958 + 0.003875 + 0.003917 + 0.004209 + 0.003959 + 0.004958 + 0.005208 + 0.005458 + 0.005292 + 0.005541 + 0.005458 + 0.005292 + 0.005709 + 0.005834 + 0.0055 + 0.005166 + 0.004917 + 0.005833 + 0.00525 + 0.004875 + 0.004583 + 0.00625 + 0.006125 + 0.006625 + 0.00575 + 0.005208 + 0.004625 + 0.0045 + 0.004167 + 0.004166 + 0.003959 + 0.004041 + 0.003875 + 0.005458 + 0.005583 + 0.004916 + 0.005042 + 0.005125 + 0.004959 + 0.004209 + 0.004667 + 0.004167 + 0.004375 + 0.004416 + 0.005042 + 0.004959 + 0.006292 + 0.006167 + 0.005917 + 0.005916 + 0.006 + 0.004959 + 0.004292 + 0.006417 + 0.006084 + 0.00575 + 0.007 + 0.006625 + 0.006 + 0.006083 + 0.0055 + 0.005291 + 0.005125 + 0.005166 + 0.0045 + 0.004625 + 0.006083 + 0.0045 + 0.00375 + 0.003875 + 0.003833 + 0.003542 + 0.004 + 0.003333 + 0.003375 + 0.003458 + 0.003625 + 0.0035 + 0.003625 + 0.003292 + 0.003541 + 0.004083 + 0.003958 + 0.003959 + 0.0035 + 0.003083 + 0.0035 + 0.003542 + 0.003417 + 0.003417 + 0.003167 + 0.003209 + 0.003292 + 0.003042 + 0.003542 + 0.003667 + 0.003209 + 0.003416 + 0.003334 + 0.003209 + 0.003208 + 0.003583 + 0.003583 + 0.003542 + 0.003542 + 0.00325 + 0.003584 + 0.003334 + 0.003917 + 0.006875 + 0.0035 + 0.00425 + 0.003958 + 0.004166 + 0.004042 + 0.003458 + 0.003917 + 0.004292 + 0.004708 + 0.007625 + 0.004916 + 0.004625 + 0.00425 + 0.004667 + 0.005 + 0.003709 + 0.003625 + 0.003958 + 0.003667 + 0.003708 + 0.003542 + 0.00675 + 0.004083 + 0.003833 + 0.004375 + 0.003833 + 0.003583 + 0.003792 + 0.004709 + 0.004334 + 0.004209 + 0.003958 + 0.003791 + 0.003917 + 0.007791 + 0.0045 + 0.007208 + 0.004541 + 0.004833 + 0.004375 + 0.004375 + 0.004333 + 0.003666 + 0.009792 + 0.007667 + 0.004875 + 0.005333 + 0.00425 + 0.004375 + 0.004292 + 0.004125 + 0.00425 + 0.006833 + 0.004083 + 0.003666 + 0.003875 + 0.004042 + 0.004833 + 0.004791 + 0.003917 + 0.003583 + 0.004083 + 0.00475 + 0.004416 + 0.003875 + 0.004375 + 0.003959 + 0.003959 + 0.004084 + 0.00375 + 0.00375 + 0.003417 + 0.004041 + 0.004125 + 0.004041 + 0.003875 + 0.004458 + 0.003875 + 0.004375 + 0.00375 + 0.004125 + 0.004291 + 0.00425 + 0.004 + 0.003834 + 0.004125 + 0.004042 + 0.003708 + 0.004167 + 0.003709 + 0.004167 + 0.00375 + 0.004125 + 0.003834 + 0.004042 + 0.003708 + 0.003791 + 0.00375 + 0.003708 + 0.00425 + 0.004042 + 0.003958 + 0.003708 + 0.007375 + 0.00425 + 0.004666 + 0.004083 + 0.004417 + 0.00525 + 0.005167 + 0.0045 + 0.005125 + 0.011625 + 0.005375 + 0.00525 + 0.004458 + 0.003833 + 0.004875 + 0.004083 + 0.004125 + 0.0045 + 0.006625 + 0.014416 + 0.004625 + 0.003667 + 0.004125 + 0.004959 + 0.004208 + 0.004542 + 0.003708 + 0.00475 + 0.007709 + 0.004709 + 0.004375 + 0.004375 + 0.0055 + 0.005333 + 0.005125 + 0.016584 + 0.005166 + 0.008208 + 0.005042 + 0.00475 + 0.005667 + 0.005209 + 0.005209 + 0.00525 + 0.005208 + 0.012125 + 0.005375 + 0.005125 + 0.004083 + 0.004167 + 0.004208 + 0.004166 + 0.003875 + 0.004083 + 0.004167 + 0.004792 + 0.003958 + 0.0035 + 0.003917 + 0.003625 + 0.004 + 0.004167 + 0.004208 + 0.00375 + 0.003917 + 0.004292 + 0.004 + 0.003958 + 0.003583 + 0.004042 + 0.003667 + 0.003333 + 0.003875 + 0.003875 + 0.003875 + 0.004416 + 0.003917 + 0.004125 + 0.003583 + 0.00375 + 0.003833 + 0.006875 + 0.003541 + 0.003917 + 0.003875 + 0.003875 + 0.00375 + 0.00425 + 0.003792 + 0.003792 + 0.003833 + 0.003875 + 0.004042 + 0.004 + 0.003708 + 0.004125 + 0.004083 + 0.004167 + 0.003625 + 0.004209 + 0.004542 + 0.003917 + 0.004125 + 0.003875 + 0.004083 + 0.005 + 0.008208 + 0.005292 + 0.0055 + 0.005042 + 0.005042 + 0.004791 + 0.005125 + 0.004292 + 0.004375 + 0.00775 + 0.004375 + 0.004625 + 0.003875 + 0.004209 + 0.0045 + 0.004542 + 0.00725 + 0.004125 + 0.0045 + 0.004375 + 0.003791 + 0.003708 + 0.004125 + 0.00425 + 0.004042 + 0.007292 + 0.0055 + 0.008375 + 0.005583 + 0.005625 + 0.00475 + 0.005084 + 0.004959 + 0.004042 + 0.00475 + 0.00825 + 0.004917 + 0.005667 + 0.004875 + 0.004875 + 0.004917 + 0.005542 + 0.003708 + 0.007 + 0.00375 + 0.004125 + 0.003792 + 0.004 + 0.003708 + 0.003833 + 0.004542 + 0.004125 + 0.003708 + 0.004042 + 0.004 + 0.003875 + 0.004459 + 0.003875 + 0.004 + 0.004 + 0.004417 + 0.003792 + 0.00425 + 0.004083 + 0.00375 + 0.003625 + 0.003875 + 0.004 + 0.004042 + 0.004125 + 0.00375 + 0.003833 + 0.004042 + 0.004042 + 0.003791 + 0.004042 + 0.003834 + 0.003584 + 0.004042 + 0.004041 + 0.003542 + 0.003958 + 0.003833 + 0.004458 + 0.004291 + 0.004 + 0.003875 + 0.004416 + 0.00375 + 0.004 + 0.00425 + 0.006959 + 0.004042 + 0.004541 + 0.004084 + 0.004 + 0.003875 + 0.004209 + 0.00425 + 0.004792 + 0.004959 + 0.004667 + 0.008167 + 0.0055 + 0.004792 + 0.004833 + 0.005 + 0.004541 + 0.003833 + 0.004 + 0.004625 + 0.004417 + 0.0045 + 0.006792 + 0.004791 + 0.004417 + 0.003959 + 0.004709 + 0.004667 + 0.004167 + 0.004584 + 0.0045 + 0.004 + 0.003708 + 0.004 + 0.004542 + 0.005209 + 0.005417 + 0.008166 + 0.005625 + 0.005375 + 0.005 + 0.004709 + 0.005125 + 0.004334 + 0.005334 + 0.008292 + 0.004833 + 0.005333 + 0.004833 + 0.00525 + 0.00475 + 0.004125 + 0.004083 + 0.003833 + 0.0035 + 0.003541 + 0.00425 + 0.004 + 0.0035 + 0.004667 + 0.004125 + 0.0035 + 0.003833 + 0.004125 + 0.003958 + 0.004208 + 0.003833 + 0.004083 + 0.004292 + 0.003875 + 0.004083 + 0.004208 + 0.003792 + 0.004042 + 0.003708 + 0.003625 + 0.0035 + 0.003625 + 0.003959 + 0.00425 + 0.003875 + 0.003916 + 0.004041 + 0.004042 + 0.004084 + 0.003958 + 0.003833 + 0.003708 + 0.004041 + 0.003875 + 0.003583 + 0.003958 + 0.003833 + 0.003917 + 0.004167 + 0.003875 + 0.003625 + 0.003875 + 0.003792 + 0.004083 + 0.004417 + 0.004209 + 0.004 + 0.004041 + 0.007875 + 0.003917 + 0.00425 + 0.003916 + 0.004292 + 0.005 + 0.005375 + 0.004708 + 0.005083 + 0.0085 + 0.005166 + 0.00575 + 0.00425 + 0.004458 + 0.0045 + 0.004459 + 0.003875 + 0.004542 + 0.004791 + 0.004375 + 0.004167 + 0.003917 + 0.00475 + 0.004542 + 0.004125 + 0.004042 + 0.00425 + 0.004084 + 0.006584 + 0.004292 + 0.004291 + 0.005042 + 0.005334 + 0.004875 + 0.004916 + 0.004917 + 0.007709 + 0.005208 + 0.005708 + 0.004209 + 0.00525 + 0.00475 + 0.004917 + 0.004916 + 0.007333 + 0.005209 + 0.005167 + 0.003666 + 0.003584 + 0.003916 + 0.003625 + 0.003417 + 0.003833 + 0.003666 + 0.004292 + 0.004416 + 0.00425 + 0.003459 + 0.003791 + 0.004292 + 0.003916 + 0.003792 + 0.003875 + 0.004084 + 0.003833 + 0.004375 + 0.004041 + 0.004042 + 0.004042 + 0.004167 + 0.003625 + 0.003541 + 0.003833 + 0.003375 + 0.003708 + 0.003833 + 0.003833 + 0.003959 + 0.003833 + 0.003791 + 0.00425 + 0.003916 + 0.003542 + 0.004375 + 0.003959 + 0.003917 + 0.004125 + 0.004291 + 0.003833 + 0.003959 + 0.004166 + 0.003917 + 0.003917 + 0.003875 + 0.003834 + 0.003917 + 0.004458 + 0.004291 + 0.004042 + 0.004375 + 0.003917 + 0.00375 + 0.004041 + 0.004083 + 0.004083 + 0.00525 + 0.007542 + 0.005042 + 0.00575 + 0.004833 + 0.004666 + 0.004958 + 0.004125 + 0.004208 + 0.004208 + 0.004417 + 0.004292 + 0.004625 + 0.009208 + 0.0045 + 0.004125 + 0.004042 + 0.004125 + 0.0045 + 0.004167 + 0.004125 + 0.004 + 0.004416 + 0.004084 + 0.004042 + 0.004209 + 0.003958 + 0.004875 + 0.005083 + 0.007416 + 0.005167 + 0.005208 + 0.005042 + 0.004958 + 0.004208 + 0.005041 + 0.005208 + 0.007834 + 0.004875 + 0.005042 + 0.004792 + 0.005084 + 0.004459 + 0.004459 + 0.003709 + 0.003834 + 0.003625 + 0.003708 + 0.003667 + 0.003541 + 0.004125 + 0.003916 + 0.0045 + 0.00375 + 0.0045 + 0.003833 + 0.004333 + 0.004209 + 0.003958 + 0.003875 + 0.004125 + 0.003917 + 0.004084 + 0.004084 + 0.004333 + 0.003958 + 0.003875 + 0.003416 + 0.003625 + 0.00375 + 0.003958 + 0.003958 + 0.003792 + 0.003375 + 0.003917 + 0.003666 + 0.004209 + 0.004167 + 0.004208 + 0.004125 + 0.003875 + 0.004208 + 0.004125 + 0.004292 + 0.004125 + 0.004167 + 0.003916 + 0.004125 + 0.003958 + 0.004417 + 0.004 + 0.004875 + 0.004166 + 0.004292 + 0.003958 + 0.00675 + 0.00425 + 0.004084 + 0.004 + 0.00425 + 0.005083 + 0.004625 + 0.004792 + 0.00475 + 0.007 + 0.005083 + 0.005041 + 0.004792 + 0.00425 + 0.004209 + 0.004334 + 0.003875 + 0.004208 + 0.004083 + 0.00425 + 0.004041 + 0.004375 + 0.004208 + 0.004458 + 0.0045 + 0.007166 + 0.004542 + 0.00525 + 0.00425 + 0.004417 + 0.004167 + 0.004 + 0.004791 + 0.005125 + 0.00475 + 0.005209 + 0.007959 + 0.005 + 0.005083 + 0.004334 + 0.00375 + 0.005083 + 0.004625 + 0.004625 + 0.005208 + 0.008292 + 0.005459 + 0.005125 + 0.004 + 0.003708 + 0.004458 + 0.003958 + 0.004 + 0.003834 + 0.003625 + 0.004417 + 0.004417 + 0.003833 + 0.003458 + 0.003458 + 0.003709 + 0.00425 + 0.003708 + 0.003709 + 0.004125 + 0.00425 + 0.003917 + 0.003709 + 0.004042 + 0.004166 + 0.003542 + 0.004 + 0.003958 + 0.0045 + 0.003625 + 0.003834 + 0.004041 + 0.003792 + 0.003583 + 0.003667 + 0.00375 + 0.003625 + 0.003417 + 0.003833 + 0.003542 + 0.003667 + 0.003583 + 0.003792 + 0.003834 + 0.003292 + 0.003708 + 0.003875 + 0.003875 + 0.003708 + 0.003791 + 0.003542 + 0.004084 + 0.004 + 0.003792 + 0.003875 + 0.004167 + 0.004083 + 0.004166 + 0.003958 + 0.003792 + 0.003125 + 0.005083 + 0.007333 + 0.005 + 0.004917 + 0.005292 + 0.004583 + 0.004792 + 0.004125 + 0.004333 + 0.004375 + 0.004 + 0.003792 + 0.004292 + 0.006292 + 0.0045 + 0.004125 + 0.004167 + 0.004083 + 0.004083 + 0.004084 + 0.004166 + 0.004 + 0.004209 + 0.004167 + 0.004083 + 0.004042 + 0.005 + 0.004792 + 0.007125 + 0.004792 + 0.00525 + 0.004708 + 0.004916 + 0.004667 + 0.004459 + 0.005917 + 0.008 + 0.01375 + 0.007 + 0.007 + 0.005667 + 0.006791 + 0.005542 + 0.00525 + 0.005584 + 0.005292 + 0.005208 + 0.005833 + 0.010417 + 0.009791 + 0.006458 + 0.005208 + 0.00425 + 0.004708 + 0.004833 + 0.004625 + 0.004959 + 0.004625 + 0.004709 + 0.005084 + 0.004459 + 0.013333 + 0.004416 + 0.005167 + 0.006166 + 0.005083 + 0.005584 + 0.0055 + 0.0055 + 0.011709 + 0.004625 + 0.004292 + 0.005125 + 0.004292 + 0.004875 + 0.004875 + 0.004667 + 0.005125 + 0.004791 + 0.00525 + 0.004917 + 0.004875 + 0.005625 + 0.005042 + 0.004875 + 0.006292 + 0.005417 + 0.004916 + 0.005917 + 0.005625 + 0.006042 + 0.005459 + 0.007959 + 0.004209 + 0.00475 + 0.0045 + 0.003875 + 0.004 + 0.004667 + 0.004709 + 0.005292 + 0.004917 + 0.005 + 0.007917 + 0.005084 + 0.005541 + 0.004708 + 0.004542 + 0.00425 + 0.004459 + 0.003959 + 0.004084 + 0.004125 + 0.004375 + 0.004125 + 0.004125 + 0.004584 + 0.0045 + 0.004041 + 0.00425 + 0.00725 + 0.004542 + 0.004417 + 0.004458 + 0.004333 + 0.004542 + 0.004833 + 0.00475 + 0.004833 + 0.004625 + 0.00875 + 0.005083 + 0.005125 + 0.004792 + 0.003791 + 0.005042 + 0.004833 + 0.005083 + 0.01175 + 0.004958 + 0.0055 + 0.004709 + 0.004375 + 0.003833 + 0.007083 + 0.004375 + 0.003958 + 0.004208 + 0.0045 + 0.004375 + 0.005625 + 0.011875 + 0.005125 + 0.005167 + 0.005709 + 0.004958 + 0.00575 + 0.005167 + 0.005125 + 0.004917 + 0.00425 + 0.005125 + 0.005083 + 0.004958 + 0.004333 + 0.005542 + 0.005125 + 0.003875 + 0.004125 + 0.004417 + 0.004166 + 0.003958 + 0.004333 + 0.00375 + 0.009875 + 0.007167 + 0.003541 + 0.003709 + 0.004041 + 0.003792 + 0.003875 + 0.003791 + 0.003833 + 0.003958 + 0.004083 + 0.003917 + 0.003875 + 0.003834 + 0.003833 + 0.003917 + 0.004042 + 0.004166 + 0.004625 + 0.004292 + 0.00425 + 0.004042 + 0.004208 + 0.007125 + 0.01025 + 0.004083 + 0.005917 + 0.005 + 0.004834 + 0.004875 + 0.004875 + 0.011334 + 0.005459 + 0.005417 + 0.004708 + 0.0045 + 0.004084 + 0.004041 + 0.004208 + 0.004417 + 0.004 + 0.004334 + 0.004125 + 0.0045 + 0.004333 + 0.005125 + 0.004208 + 0.004416 + 0.004458 + 0.016875 + 0.004458 + 0.004042 + 0.042042 + 0.050959 + 0.015459 + 0.008584 + 0.006792 + 0.006 + 0.012208 + 0.007583 + 0.015083 + 0.008167 + 0.006959 + 0.006959 + 0.006875 + 0.0075 + 0.007125 + 0.006416 + 0.006167 + 0.006708 + 0.004458 + 0.005 + 0.005125 + 0.004917 + 0.005417 + 0.005708 + 0.00775 + 0.005625 + 0.005625 + 0.005208 + 0.006958 + 0.004958 + 0.005 + 0.004792 + 0.004584 + 0.004792 + 0.004666 + 0.00475 + 0.004542 + 0.005084 + 0.005041 + 0.004625 + 0.005083 + 0.004375 + 0.004916 + 0.0045 + 0.004667 + 0.005375 + 0.0045 + 0.0045 + 0.00475 + 0.004625 + 0.004625 + 0.004833 + 0.004458 + 0.00525 + 0.005 + 0.004458 + 0.004791 + 0.004792 + 0.004667 + 0.004875 + 0.005042 + 0.004666 + 0.004792 + 0.004708 + 0.004667 + 0.006875 + 0.005208 + 0.00475 + 0.005333 + 0.005459 + 0.004875 + 0.004875 + 0.006792 + 0.006042 + 0.009 + 0.006958 + 0.006792 + 0.005625 + 0.006083 + 0.0185 + 0.008291 + 0.007208 + 0.00775 + 0.007875 + 0.005709 + 0.006 + 0.006458 + 0.00625 + 0.0065 + 0.005958 + 0.006583 + 0.008292 + 0.007875 + 0.005 + 0.00675 + 0.006375 + 0.006042 + 0.0065 + 0.005875 + 0.005291 + 0.004959 + 0.005167 + 0.005625 + 0.00625 + 0.005833 + 0.007083 + 0.006542 + 0.006334 + 0.005625 + 0.005167 + 0.007041 + 0.007375 + 0.008166 + 0.007375 + 0.006 + 0.006125 + 0.006125 + 0.005791 + 0.005542 + 0.005708 + 0.0055 + 0.005417 + 0.005542 + 0.005083 + 0.004708 + 0.004875 + 0.004875 + 0.005084 + 0.004667 + 0.005542 + 0.00575 + 0.0055 + 0.0055 + 0.004834 + 0.005041 + 0.005125 + 0.004833 + 0.004917 + 0.004791 + 0.004417 + 0.0045 + 0.004542 + 0.004666 + 0.004833 + 0.00525 + 0.005125 + 0.005042 + 0.0045 + 0.004334 + 0.004667 + 0.004875 + 0.0045 + 0.004667 + 0.004666 + 0.00425 + 0.004625 + 0.004333 + 0.004375 + 0.004459 + 0.005125 + 0.0045 + 0.005042 + 0.004541 + 0.00475 + 0.004291 + 0.005042 + 0.00525 + 0.004959 + 0.005209 + 0.006541 + 0.005791 + 0.006083 + 0.005917 + 0.005708 + 0.006459 + 0.007791 + 0.007125 + 0.006167 + 0.005167 + 0.01075 + 0.007459 + 0.007 + 0.006375 + 0.005875 + 0.007125 + 0.005875 + 0.005875 + 0.004583 + 0.00525 + 0.005166 + 0.005042 + 0.005125 + 0.00475 + 0.00475 + 0.005166 + 0.005167 + 0.005791 + 0.006625 + 0.006375 + 0.006458 + 0.006708 + 0.007167 + 0.00725 + 0.006583 + 0.00725 + 0.0085 + 0.007708 + 0.006375 + 0.006333 + 0.005916 + 0.006334 + 0.005709 + 0.007292 + 0.00775 + 0.008208 + 0.007958 + 0.006583 + 0.006 + 0.005583 + 0.004916 + 0.004375 + 0.004917 + 0.004875 + 0.004833 + 0.00425 + 0.005583 + 0.004375 + 0.004208 + 0.00475 + 0.00475 + 0.004834 + 0.004375 + 0.005125 + 0.004875 + 0.004875 + 0.004709 + 0.004875 + 0.004667 + 0.004834 + 0.004417 + 0.005167 + 0.005167 + 0.00475 + 0.004708 + 0.004791 + 0.0045 + 0.004458 + 0.004458 + 0.005167 + 0.005 + 0.004792 + 0.00425 + 0.004209 + 0.004625 + 0.004625 + 0.0045 + 0.00475 + 0.005458 + 0.004625 + 0.004541 + 0.004583 + 0.004417 + 0.004042 + 0.004625 + 0.004959 + 0.005416 + 0.004625 + 0.004583 + 0.004625 + 0.0045 + 0.004917 + 0.009417 + 0.006666 + 0.006167 + 0.006417 + 0.007291 + 0.006708 + 0.006875 + 0.0065 + 0.00575 + 0.007917 + 0.007916 + 0.006125 + 0.006 + 0.006042 + 0.005792 + 0.006417 + 0.005917 + 0.005333 + 0.004792 + 0.004625 + 0.004708 + 0.004458 + 0.005083 + 0.005416 + 0.005459 + 0.00625 + 0.006083 + 0.006958 + 0.006167 + 0.005417 + 0.004209 + 0.006292 + 0.005833 + 0.005333 + 0.00575 + 0.005625 + 0.006625 + 0.007375 + 0.006208 + 0.007208 + 0.006791 + 0.007458 + 0.006875 + 0.007125 + 0.007083 + 0.006708 + 0.006792 + 0.005833 + 0.00575 + 0.0055 + 0.005417 + 0.004875 + 0.0045 + 0.004208 + 0.005167 + 0.004458 + 0.004583 + 0.005209 + 0.00525 + 0.004667 + 0.004167 + 0.004291 + 0.00425 + 0.004666 + 0.003833 + 0.004417 + 0.005083 + 0.004458 + 0.00475 + 0.00425 + 0.004417 + 0.004541 + 0.004167 + 0.0045 + 0.004625 + 0.00425 + 0.004459 + 0.004292 + 0.004625 + 0.00475 + 0.004333 + 0.004875 + 0.004167 + 0.004292 + 0.004875 + 0.004292 + 0.004292 + 0.004208 + 0.004333 + 0.004708 + 0.004417 + 0.004417 + 0.004583 + 0.004459 + 0.004583 + 0.004667 + 0.00475 + 0.004875 + 0.004791 + 0.004417 + 0.004917 + 0.004625 + 0.008959 + 0.005959 + 0.006667 + 0.006834 + 0.00675 + 0.006792 + 0.00675 + 0.007958 + 0.006958 + 0.0065 + 0.006 + 0.006333 + 0.00575 + 0.006166 + 0.005917 + 0.005083 + 0.004791 + 0.004958 + 0.004417 + 0.005 + 0.004792 + 0.004791 + 0.006625 + 0.005916 + 0.005583 + 0.005959 + 0.005792 + 0.005875 + 0.005709 + 0.0065 + 0.005584 + 0.004958 + 0.005125 + 0.006625 + 0.0085 + 0.007417 + 0.005583 + 0.006333 + 0.0065 + 0.006833 + 0.007375 + 0.006834 + 0.00625 + 0.006125 + 0.006125 + 0.005542 + 0.00525 + 0.005042 + 0.0045 + 0.0045 + 0.004542 + 0.004 + 0.003833 + 0.004667 + 0.004334 + 0.004334 + 0.004583 + 0.004542 + 0.004458 + 0.004125 + 0.004292 + 0.003791 + 0.003833 + 0.004208 + 0.012792 + 0.004584 + 0.00425 + 0.004458 + 0.004542 + 0.004291 + 0.004167 + 0.004167 + 0.004416 + 0.004417 + 0.004666 + 0.004542 + 0.015459 + 0.004417 + 0.004458 + 0.004791 + 0.00525 + 0.003958 + 0.004375 + 0.004167 + 0.004 + 0.0045 + 0.004875 + 0.018583 + 0.004333 + 0.004917 + 0.005208 + 0.005417 + 0.00475 + 0.013917 + 0.019375 + 0.019291 + 0.007292 + 0.006584 + 0.018959 + 0.0065 + 0.005333 + 0.005667 + 0.008375 + 0.005834 + 0.005666 + 0.005542 + 0.015416 + 0.00775 + 0.007583 + 0.006291 + 0.00525 + 0.004333 + 0.004875 + 0.004792 + 0.005125 + 0.005167 + 0.00475 + 0.005208 + 0.007083 + 0.006333 + 0.006375 + 0.006958 + 0.006292 + 0.005917 + 0.006 + 0.005709 + 0.00575 + 0.005458 + 0.004334 + 0.005709 + 0.005541 + 0.005208 + 0.007583 + 0.00625 + 0.006666 + 0.006458 + 0.006375 + 0.005291 + 0.006333 + 0.007042 + 0.006583 + 0.006708 + 0.00625 + 0.00625 + 0.006208 + 0.004208 + 0.004792 + 0.004125 + 0.003958 + 0.004209 + 0.004292 + 0.003666 + 0.004334 + 0.004125 + 0.004125 + 0.004042 + 0.00475 + 0.005291 + 0.005375 + 0.005208 + 0.004041 + 0.004625 + 0.005208 + 0.004417 + 0.00425 + 0.004833 + 0.004416 + 0.004916 + 0.004 + 0.004375 + 0.004042 + 0.004542 + 0.004333 + 0.005125 + 0.004458 + 0.004209 + 0.004167 + 0.00425 + 0.004209 + 0.004084 + 0.004542 + 0.004083 + 0.004208 + 0.004167 + 0.004291 + 0.003875 + 0.00375 + 0.004375 + 0.004041 + 0.003958 + 0.004042 + 0.004125 + 0.004584 + 0.003708 + 0.004292 + 0.004792 + 0.004541 + 0.006459 + 0.005 + 0.005375 + 0.005042 + 0.004459 + 0.004459 + 0.006 + 0.005667 + 0.005375 + 0.005 + 0.007542 + 0.007375 + 0.00675 + 0.006542 + 0.006417 + 0.006292 + 0.005958 + 0.005291 + 0.005458 + 0.005334 + 0.005541 + 0.005167 + 0.00575 + 0.005875 + 0.005458 + 0.005667 + 0.005417 + 0.005625 + 0.005708 + 0.006042 + 0.005709 + 0.005834 + 0.006459 + 0.0075 + 0.0065 + 0.00625 + 0.008459 + 0.007583 + 0.006708 + 0.006583 + 0.005125 + 0.006083 + 0.0055 + 0.00675 + 0.005792 + 0.00525 + 0.005375 + 0.005208 + 0.0045 + 0.004291 + 0.004083 + 0.004166 + 0.004459 + 0.004208 + 0.004041 + 0.005083 + 0.004792 + 0.004083 + 0.003958 + 0.004083 + 0.004292 + 0.00425 + 0.003541 + 0.003583 + 0.003958 + 0.00375 + 0.004083 + 0.004333 + 0.004333 + 0.004 + 0.004208 + 0.003958 + 0.004209 + 0.004334 + 0.003958 + 0.004291 + 0.013083 + 0.003958 + 0.004042 + 0.004375 + 0.004292 + 0.004084 + 0.004125 + 0.003917 + 0.004 + 0.003917 + 0.003666 + 0.004375 + 0.003625 + 0.003542 + 0.004167 + 0.003791 + 0.003875 + 0.003791 + 0.003875 + 0.00375 + 0.004291 + 0.004417 + 0.004375 + 0.004167 + 0.004292 + 0.005542 + 0.005166 + 0.004833 + 0.00525 + 0.005167 + 0.006291 + 0.004708 + 0.005417 + 0.00525 + 0.006042 + 0.006458 + 0.005916 + 0.005292 + 0.005042 + 0.005 + 0.004584 + 0.004375 + 0.004334 + 0.004584 + 0.004417 + 0.004333 + 0.004417 + 0.003583 + 0.004333 + 0.005333 + 0.00425 + 0.004291 + 0.008792 + 0.005291 + 0.00475 + 0.004958 + 0.004667 + 0.005791 + 0.004833 + 0.004834 + 0.004584 + 0.005 + 0.006083 + 0.005625 + 0.0045 + 0.006083 + 0.005625 + 0.00575 + 0.00475 + 0.005917 + 0.006167 + 0.005584 + 0.005459 + 0.004709 + 0.00475 + 0.004 + 0.003917 + 0.003709 + 0.00375 + 0.003875 + 0.00425 + 0.003666 + 0.004042 + 0.003916 + 0.004417 + 0.00425 + 0.00375 + 0.003625 + 0.004 + 0.003584 + 0.00375 + 0.003541 + 0.004292 + 0.004458 + 0.003667 + 0.003834 + 0.003834 + 0.003667 + 0.004083 + 0.003875 + 0.004083 + 0.003875 + 0.003875 + 0.003792 + 0.004125 + 0.004375 + 0.003833 + 0.003584 + 0.003917 + 0.003625 + 0.00425 + 0.004208 + 0.00375 + 0.003958 + 0.00375 + 0.003917 + 0.00375 + 0.003917 + 0.003916 + 0.003667 + 0.00425 + 0.004667 + 0.004042 + 0.004833 + 0.0045 + 0.004083 + 0.004291 + 0.004625 + 0.003875 + 0.005625 + 0.006417 + 0.006542 + 0.005708 + 0.005833 + 0.005334 + 0.006542 + 0.006375 + 0.007084 + 0.005041 + 0.004875 + 0.005125 + 0.004208 + 0.004833 + 0.00425 + 0.003625 + 0.004417 + 0.009792 + 0.004334 + 0.00425 + 0.004292 + 0.005917 + 0.005125 + 0.012417 + 0.005167 + 0.00875 + 0.004208 + 0.00425 + 0.006291 + 0.005958 + 0.006125 + 0.006375 + 0.007958 + 0.006333 + 0.005834 + 0.004625 + 0.005125 + 0.005333 + 0.008833 + 0.006542 + 0.006209 + 0.005625 + 0.005875 + 0.005792 + 0.004875 + 0.004 + 0.003708 + 0.003958 + 0.003917 + 0.003916 + 0.003834 + 0.004166 + 0.0035 + 0.0035 + 0.004 + 0.004 + 0.00375 + 0.013708 + 0.004958 + 0.004709 + 0.004375 + 0.003625 + 0.003875 + 0.003792 + 0.004042 + 0.003917 + 0.004083 + 0.003583 + 0.0035 + 0.004625 + 0.004917 + 0.005125 + 0.005167 + 0.005292 + 0.00525 + 0.00475 + 0.005125 + 0.005041 + 0.004792 + 0.005208 + 0.005458 + 0.005166 + 0.005125 + 0.00475 + 0.005208 + 0.00525 + 0.004834 + 0.003959 + 0.004375 + 0.004042 + 0.004167 + 0.003875 + 0.00425 + 0.003792 + 0.003875 + 0.003958 + 0.004166 + 0.004292 + 0.004083 + 0.005167 + 0.005541 + 0.006375 + 0.006042 + 0.005959 + 0.006292 + 0.006666 + 0.0075 + 0.006541 + 0.006125 + 0.0055 + 0.004834 + 0.0055 + 0.005542 + 0.0055 + 0.005292 + 0.005167 + 0.005208 + 0.005458 + 0.006083 + 0.005666 + 0.006125 + 0.005875 + 0.00675 + 0.004541 + 0.004792 + 0.00475 + 0.004458 + 0.005 + 0.005166 + 0.004792 + 0.004917 + 0.004666 + 0.006125 + 0.005666 + 0.00575 + 0.00475 + 0.004084 + 0.004917 + 0.005167 + 0.004666 + 0.005875 + 0.006 + 0.005583 + 0.005667 + 0.004334 + 0.004167 + 0.003958 + 0.003791 + 0.00375 + 0.003583 + 0.003792 + 0.003833 + 0.004291 + 0.003958 + 0.003667 + 0.003291 + 0.004167 + 0.004 + 0.003833 + 0.004167 + 0.003833 + 0.003458 + 0.003959 + 0.003583 + 0.003583 + 0.003541 + 0.003833 + 0.003542 + 0.003791 + 0.003708 + 0.003625 + 0.003875 + 0.004 + 0.00375 + 0.003959 + 0.00375 + 0.0035 + 0.003792 + 0.003875 + 0.003417 + 0.003792 + 0.003625 + 0.003625 + 0.003375 + 0.003666 + 0.003458 + 0.003875 + 0.003708 + 0.003833 + 0.003583 + 0.003375 + 0.003291 + 0.003667 + 0.004041 + 0.004 + 0.004166 + 0.004542 + 0.00475 + 0.004375 + 0.004417 + 0.005583 + 0.005375 + 0.006209 + 0.006333 + 0.006208 + 0.006333 + 0.005875 + 0.006875 + 0.006458 + 0.006042 + 0.005084 + 0.005542 + 0.005583 + 0.004875 + 0.004917 + 0.004708 + 0.004791 + 0.004375 + 0.004458 + 0.004209 + 0.004 + 0.004041 + 0.004459 + 0.00475 + 0.005083 + 0.005375 + 0.006458 + 0.005333 + 0.00575 + 0.005375 + 0.005416 + 0.0055 + 0.00575 + 0.018916 + 0.011458 + 0.006041 + 0.004958 + 0.005792 + 0.005583 + 0.004958 + 0.00825 + 0.006416 + 0.007333 + 0.005875 + 0.004 + 0.00625 + 0.003667 + 0.003208 + 0.004125 + 0.004083 + 0.003667 + 0.00425 + 0.004625 + 0.003542 + 0.003833 + 0.00375 + 0.003666 + 0.003667 + 0.003834 + 0.003791 + 0.003292 + 0.003417 + 0.003458 + 0.003833 + 0.003917 + 0.003875 + 0.003708 + 0.00325 + 0.003542 + 0.0035 + 0.004708 + 0.004292 + 0.0045 + 0.004042 + 0.003625 + 0.003208 + 0.003625 + 0.003625 + 0.00375 + 0.003834 + 0.003042 + 0.00375 + 0.003375 + 0.00375 + 0.00375 + 0.004292 + 0.004 + 0.0035 + 0.003459 + 0.003459 + 0.003416 + 0.0035 + 0.003833 + 0.004 + 0.003833 + 0.004125 + 0.009459 + 0.004 + 0.00375 + 0.003625 + 0.004417 + 0.003959 + 0.005459 + 0.00575 + 0.006333 + 0.005917 + 0.006 + 0.006083 + 0.006042 + 0.0055 + 0.005084 + 0.004709 + 0.003667 + 0.004208 + 0.00425 + 0.005083 + 0.005125 + 0.004541 + 0.004959 + 0.004459 + 0.004916 + 0.005 + 0.005125 + 0.004875 + 0.004792 + 0.003875 + 0.003667 + 0.003625 + 0.003542 + 0.005125 + 0.005709 + 0.005875 + 0.006209 + 0.005667 + 0.005833 + 0.005917 + 0.005166 + 0.005958 + 0.007458 + 0.006042 + 0.0065 + 0.006209 + 0.006791 + 0.006375 + 0.007125 + 0.005542 + 0.005208 + 0.004125 + 0.003834 + 0.004834 + 0.004541 + 0.004292 + 0.004583 + 0.004 + 0.004209 + 0.005458 + 0.004375 + 0.004625 + 0.004417 + 0.004209 + 0.003875 + 0.004042 + 0.004292 + 0.004333 + 0.00425 + 0.004333 + 0.004292 + 0.004167 + 0.003875 + 0.003875 + 0.004208 + 0.003833 + 0.00425 + 0.004125 + 0.0045 + 0.003917 + 0.004291 + 0.004459 + 0.004291 + 0.003834 + 0.003875 + 0.004125 + 0.003958 + 0.003666 + 0.003792 + 0.004041 + 0.004375 + 0.003916 + 0.004125 + 0.004333 + 0.004292 + 0.003958 + 0.004375 + 0.004583 + 0.004416 + 0.0045 + 0.004458 + 0.004167 + 0.004209 + 0.00425 + 0.004 + 0.004042 + 0.004917 + 0.005042 + 0.00675 + 0.006875 + 0.006458 + 0.006667 + 0.006917 + 0.006666 + 0.00575 + 0.005125 + 0.005417 + 0.005958 + 0.005083 + 0.005583 + 0.005458 + 0.005666 + 0.005458 + 0.006 + 0.005416 + 0.004667 + 0.004334 + 0.004375 + 0.004458 + 0.004709 + 0.00425 + 0.004916 + 0.004292 + 0.005292 + 0.006375 + 0.007125 + 0.006583 + 0.006291 + 0.006583 + 0.007041 + 0.005208 + 0.006833 + 0.0075 + 0.006333 + 0.0065 + 0.006666 + 0.007083 + 0.007375 + 0.006708 + 0.005375 + 0.005167 + 0.005375 + 0.005125 + 0.004833 + 0.005208 + 0.004708 + 0.004708 + 0.005292 + 0.003791 + 0.004333 + 0.003959 + 0.004291 + 0.004292 + 0.004709 + 0.003959 + 0.004416 + 0.004209 + 0.00425 + 0.004292 + 0.004625 + 0.004542 + 0.004458 + 0.004375 + 0.004083 + 0.004333 + 0.004208 + 0.004042 + 0.003875 + 0.00425 + 0.003958 + 0.004042 + 0.003916 + 0.004208 + 0.004084 + 0.003834 + 0.003958 + 0.004167 + 0.004542 + 0.004083 + 0.003834 + 0.004166 + 0.004459 + 0.004125 + 0.004458 + 0.004125 + 0.004 + 0.003959 + 0.004792 + 0.005 + 0.004083 + 0.004375 + 0.004167 + 0.004708 + 0.004416 + 0.004625 + 0.00425 + 0.005125 + 0.007208 + 0.006708 + 0.006541 + 0.006167 + 0.006709 + 0.0065 + 0.008542 + 0.00575 + 0.005333 + 0.005833 + 0.005791 + 0.005709 + 0.00525 + 0.005542 + 0.005542 + 0.005041 + 0.004583 + 0.004458 + 0.004458 + 0.004083 + 0.004542 + 0.004125 + 0.0045 + 0.004 + 0.005625 + 0.004875 + 0.007292 + 0.006959 + 0.006292 + 0.0065 + 0.006459 + 0.006625 + 0.007875 + 0.007084 + 0.00625 + 0.0065 + 0.006792 + 0.006334 + 0.0065 + 0.006125 + 0.00675 + 0.006959 + 0.005584 + 0.00525 + 0.005334 + 0.004834 + 0.005292 + 0.004958 + 0.004875 + 0.004 + 0.004417 + 0.004125 + 0.004125 + 0.004333 + 0.004333 + 0.00425 + 0.004625 + 0.004084 + 0.004042 + 0.004208 + 0.004292 + 0.004083 + 0.004375 + 0.004292 + 0.004417 + 0.004166 + 0.00425 + 0.004333 + 0.004792 + 0.0045 + 0.004417 + 0.00425 + 0.003958 + 0.004125 + 0.004458 + 0.00425 + 0.004 + 0.004334 + 0.003875 + 0.00425 + 0.0045 + 0.00375 + 0.00425 + 0.004167 + 0.003833 + 0.004 + 0.00425 + 0.004167 + 0.004042 + 0.004083 + 0.004875 + 0.004791 + 0.004333 + 0.004542 + 0.004417 + 0.004416 + 0.004125 + 0.004708 + 0.0045 + 0.004458 + 0.005292 + 0.007208 + 0.006666 + 0.006208 + 0.006417 + 0.006375 + 0.0065 + 0.005667 + 0.0055 + 0.005334 + 0.004625 + 0.004167 + 0.004709 + 0.005584 + 0.005625 + 0.005625 + 0.005583 + 0.005583 + 0.005375 + 0.005958 + 0.00575 + 0.005375 + 0.00625 + 0.037458 + 0.012542 + 0.005917 + 0.00825 + 0.006125 + 0.007875 + 0.00725 + 0.00625 + 0.00625 + 0.006209 + 0.005208 + 0.004583 + 0.006417 + 0.006417 + 0.006416 + 0.006458 + 0.006208 + 0.005792 + 0.006959 + 0.019 + 0.005584 + 0.004375 + 0.005084 + 0.005375 + 0.005042 + 0.004916 + 0.005125 + 0.005041 + 0.004208 + 0.003708 + 0.005209 + 0.011541 + 0.004333 + 0.004584 + 0.008459 + 0.00425 + 0.003917 + 0.004208 + 0.004208 + 0.004542 + 0.004667 + 0.004541 + 0.005167 + 0.01225 + 0.008417 + 0.0045 + 0.004 + 0.00425 + 0.011125 + 0.004416 + 0.00425 + 0.004542 + 0.004375 + 0.0045 + 0.004125 + 0.004542 + 0.003959 + 0.004375 + 0.005083 + 0.005042 + 0.00575 + 0.011666 + 0.004333 + 0.00425 + 0.0045 + 0.004458 + 0.004375 + 0.00475 + 0.006416 + 0.005167 + 0.004708 + 0.0055 + 0.016875 + 0.004541 + 0.004792 + 0.004709 + 0.005084 + 0.005625 + 0.006333 + 0.009583 + 0.007417 + 0.00675 + 0.006333 + 0.006666 + 0.0045 + 0.004417 + 0.005042 + 0.005333 + 0.012167 + 0.005 + 0.005833 + 0.00725 + 0.006041 + 0.005834 + 0.00575 + 0.006334 + 0.005834 + 0.00575 + 0.005542 + 0.00625 + 0.005 + 0.005 + 0.004667 + 0.004333 + 0.005333 + 0.005416 + 0.007083 + 0.007917 + 0.006166 + 0.006708 + 0.006875 + 0.007 + 0.006833 + 0.008666 + 0.008125 + 0.007708 + 0.006625 + 0.006417 + 0.006084 + 0.006084 + 0.005208 + 0.005208 + 0.005 + 0.004791 + 0.0045 + 0.004166 + 0.003833 + 0.004292 + 0.0045 + 0.004292 + 0.003875 + 0.004417 + 0.004167 + 0.004541 + 0.004417 + 0.004083 + 0.004375 + 0.004375 + 0.004292 + 0.00425 + 0.004167 + 0.005 + 0.004167 + 0.004292 + 0.003875 + 0.004167 + 0.003959 + 0.004041 + 0.003875 + 0.00425 + 0.00425 + 0.004292 + 0.004083 + 0.003958 + 0.004083 + 0.004 + 0.003958 + 0.004542 + 0.004334 + 0.004125 + 0.003875 + 0.004125 + 0.003958 + 0.003916 + 0.004708 + 0.003875 + 0.004041 + 0.011334 + 0.004458 + 0.006083 + 0.005583 + 0.005667 + 0.005375 + 0.004792 + 0.005209 + 0.004083 + 0.004666 + 0.004917 + 0.004834 + 0.005584 + 0.009084 + 0.005959 + 0.005959 + 0.006209 + 0.006167 + 0.004958 + 0.003958 + 0.004166 + 0.004417 + 0.004666 + 0.004542 + 0.004416 + 0.004667 + 0.005292 + 0.005625 + 0.005542 + 0.005208 + 0.005125 + 0.005458 + 0.004417 + 0.004291 + 0.004209 + 0.004375 + 0.004042 + 0.005291 + 0.00525 + 0.005041 + 0.006875 + 0.006083 + 0.005833 + 0.00575 + 0.0055 + 0.004041 + 0.005292 + 0.0055 + 0.00625 + 0.006458 + 0.006125 + 0.006375 + 0.005875 + 0.004667 + 0.004125 + 0.004292 + 0.004167 + 0.004208 + 0.003833 + 0.004375 + 0.003959 + 0.004334 + 0.004125 + 0.004542 + 0.004333 + 0.00425 + 0.004291 + 0.00425 + 0.004 + 0.004 + 0.004125 + 0.003792 + 0.004084 + 0.004291 + 0.004292 + 0.004125 + 0.004083 + 0.004083 + 0.004208 + 0.004 + 0.004167 + 0.004709 + 0.003625 + 0.003792 + 0.004041 + 0.003792 + 0.003875 + 0.003584 + 0.004083 + 0.003958 + 0.004042 + 0.004125 + 0.004208 + 0.004125 + 0.003708 + 0.004083 + 0.004083 + 0.00425 + 0.004333 + 0.004042 + 0.004167 + 0.004833 + 0.004458 + 0.004292 + 0.004416 + 0.005542 + 0.005084 + 0.00525 + 0.005125 + 0.005208 + 0.005208 + 0.004917 + 0.005334 + 0.004791 + 0.006542 + 0.006084 + 0.006084 + 0.006291 + 0.005375 + 0.004917 + 0.004708 + 0.004375 + 0.004583 + 0.004458 + 0.00425 + 0.004458 + 0.003917 + 0.004542 + 0.004541 + 0.004542 + 0.004167 + 0.008416 + 0.005083 + 0.004792 + 0.00525 + 0.005125 + 0.00475 + 0.005292 + 0.005417 + 0.005 + 0.004875 + 0.006708 + 0.006042 + 0.005959 + 0.005917 + 0.005291 + 0.005708 + 0.006375 + 0.006709 + 0.006292 + 0.005958 + 0.00525 + 0.005 + 0.004125 + 0.003875 + 0.00425 + 0.003791 + 0.00425 + 0.004 + 0.004166 + 0.003292 + 0.004541 + 0.004083 + 0.003375 + 0.003583 + 0.00425 + 0.004416 + 0.004167 + 0.003917 + 0.004041 + 0.004042 + 0.004041 + 0.004417 + 0.004541 + 0.004834 + 0.004334 + 0.004208 + 0.003833 + 0.004208 + 0.003917 + 0.003959 + 0.004 + 0.004792 + 0.004833 + 0.003542 + 0.003958 + 0.004083 + 0.003708 + 0.004084 + 0.004125 + 0.004583 + 0.004208 + 0.004333 + 0.003917 + 0.00425 + 0.004375 + 0.004083 + 0.004125 + 0.003834 + 0.004084 + 0.003916 + 0.004333 + 0.004542 + 0.005 + 0.004 + 0.004208 + 0.004541 + 0.009709 + 0.005292 + 0.006 + 0.005042 + 0.006708 + 0.005458 + 0.005333 + 0.005125 + 0.005 + 0.00675 + 0.0065 + 0.006375 + 0.005208 + 0.005542 + 0.005167 + 0.00475 + 0.004708 + 0.004792 + 0.004875 + 0.004833 + 0.004875 + 0.004958 + 0.004625 + 0.004625 + 0.004167 + 0.004333 + 0.00425 + 0.00525 + 0.005125 + 0.004875 + 0.005541 + 0.013667 + 0.005125 + 0.005125 + 0.005125 + 0.006166 + 0.005833 + 0.005958 + 0.005542 + 0.006125 + 0.006375 + 0.006416 + 0.006167 + 0.007459 + 0.006959 + 0.005875 + 0.005292 + 0.004583 + 0.004667 + 0.00375 + 0.004 + 0.003959 + 0.004166 + 0.004125 + 0.0045 + 0.003583 + 0.004208 + 0.004292 + 0.003791 + 0.004042 + 0.003792 + 0.004583 + 0.004 + 0.003917 + 0.003791 + 0.004042 + 0.003834 + 0.004333 + 0.004 + 0.004208 + 0.004292 + 0.013542 + 0.004333 + 0.004083 + 0.004167 + 0.004167 + 0.003959 + 0.004459 + 0.004042 + 0.003541 + 0.00375 + 0.003875 + 0.003792 + 0.003833 + 0.003708 + 0.003959 + 0.003958 + 0.003791 + 0.003834 + 0.00425 + 0.004209 + 0.003959 + 0.004042 + 0.003958 + 0.004208 + 0.004375 + 0.004583 + 0.004042 + 0.004417 + 0.004375 + 0.004459 + 0.004334 + 0.004667 + 0.004 + 0.009375 + 0.006083 + 0.005875 + 0.005833 + 0.005625 + 0.005834 + 0.004875 + 0.005208 + 0.005125 + 0.004667 + 0.005291 + 0.0055 + 0.005333 + 0.005084 + 0.004917 + 0.004875 + 0.004458 + 0.004583 + 0.004166 + 0.004917 + 0.00775 + 0.005375 + 0.005583 + 0.006042 + 0.006333 + 0.005792 + 0.004834 + 0.007417 + 0.007041 + 0.007583 + 0.007209 + 0.006334 + 0.006042 + 0.005875 + 0.005834 + 0.005834 + 0.006 + 0.005833 + 0.006166 + 0.006208 + 0.007042 + 0.005667 + 0.005708 + 0.004792 + 0.005584 + 0.005833 + 0.006 + 0.005667 + 0.00575 + 0.005708 + 0.006792 + 0.019875 + 0.007833 + 0.005417 + 0.004959 + 0.004375 + 0.004792 + 0.004334 + 0.004417 + 0.005375 + 0.004959 + 0.004875 + 0.004334 + 0.005042 + 0.004958 + 0.004958 + 0.004958 + 0.005791 + 0.006125 + 0.005833 + 0.005083 + 0.005333 + 0.005208 + 0.003875 + 0.00475 + 0.005208 + 0.004292 + 0.003916 + 0.00425 + 0.004042 + 0.003625 + 0.004125 + 0.004125 + 0.003917 + 0.004208 + 0.005291 + 0.005083 + 0.00575 + 0.005375 + 0.005667 + 0.00575 + 0.005625 + 0.005375 + 0.005583 + 0.006416 + 0.006125 + 0.005958 + 0.005208 + 0.005417 + 0.00525 + 0.007958 + 0.006291 + 0.006041 + 0.006875 + 0.006541 + 0.006833 + 0.008042 + 0.007459 + 0.00625 + 0.005583 + 0.0045 + 0.004291 + 0.005042 + 0.005375 + 0.004708 + 0.004708 + 0.004833 + 0.005833 + 0.004833 + 0.004708 + 0.00475 + 0.005541 + 0.006917 + 0.007959 + 0.005584 + 0.005833 + 0.005333 + 0.006584 + 0.007 + 0.006667 + 0.005916 + 0.006417 + 0.008333 + 0.006875 + 0.005708 + 0.005708 + 0.006709 + 0.006834 + 0.006209 + 0.005834 + 0.004958 + 0.006583 + 0.00675 + 0.006375 + 0.009333 + 0.0145 + 0.006708 + 0.005792 + 0.006667 + 0.007 + 0.006375 + 0.005834 + 0.006875 + 0.005959 + 0.005834 + 0.005292 + 0.004541 + 0.004375 + 0.004667 + 0.005166 + 0.004833 + 0.005708 + 0.004833 + 0.005833 + 0.005209 + 0.005625 + 0.004583 + 0.005042 + 0.004459 + 0.005084 + 0.005584 + 0.005583 + 0.005333 + 0.004791 + 0.004167 + 0.004042 + 0.004 + 0.004334 + 0.004166 + 0.003875 + 0.004125 + 0.003958 + 0.012125 + 0.003875 + 0.003458 + 0.004125 + 0.004416 + 0.004875 + 0.005083 + 0.00475 + 0.004625 + 0.004375 + 0.004709 + 0.004875 + 0.005 + 0.00475 + 0.004292 + 0.004125 + 0.012625 + 0.004541 + 0.00425 + 0.005208 + 0.007167 + 0.006459 + 0.006 + 0.0065 + 0.006917 + 0.006 + 0.005875 + 0.005542 + 0.005667 + 0.005375 + 0.006084 + 0.005334 + 0.005333 + 0.005583 + 0.005166 + 0.005667 + 0.00475 + 0.004583 + 0.004417 + 0.004459 + 0.004334 + 0.004708 + 0.004375 + 0.004334 + 0.004792 + 0.005875 + 0.007417 + 0.007208 + 0.007042 + 0.006834 + 0.006792 + 0.00675 + 0.0075 + 0.008166 + 0.007291 + 0.006375 + 0.00625 + 0.006583 + 0.007083 + 0.007625 + 0.00625 + 0.005375 + 0.005125 + 0.005209 + 0.005584 + 0.005125 + 0.005042 + 0.005542 + 0.005042 + 0.005292 + 0.003875 + 0.003791 + 0.004625 + 0.004458 + 0.004166 + 0.004125 + 0.0045 + 0.004542 + 0.0045 + 0.004792 + 0.004292 + 0.00475 + 0.004125 + 0.003959 + 0.003584 + 0.004459 + 0.003875 + 0.004375 + 0.004458 + 0.004708 + 0.004375 + 0.004334 + 0.004541 + 0.004625 + 0.004083 + 0.00425 + 0.004 + 0.004375 + 0.003667 + 0.003916 + 0.004333 + 0.004 + 0.004333 + 0.004334 + 0.004167 + 0.00425 + 0.003916 + 0.003917 + 0.004375 + 0.004584 + 0.0045 + 0.00425 + 0.004458 + 0.004375 + 0.004333 + 0.005458 + 0.005708 + 0.005166 + 0.005917 + 0.006875 + 0.006667 + 0.00775 + 0.006833 + 0.006417 + 0.006959 + 0.006375 + 0.0055 + 0.005791 + 0.00625 + 0.005916 + 0.00425 + 0.004916 + 0.006 + 0.005625 + 0.005709 + 0.00575 + 0.005417 + 0.0055 + 0.005375 + 0.005875 + 0.005209 + 0.004542 + 0.004583 + 0.0045 + 0.004542 + 0.00425 + 0.005459 + 0.005333 + 0.0065 + 0.007125 + 0.006375 + 0.006375 + 0.006334 + 0.0055 + 0.006541 + 0.007833 + 0.007166 + 0.006083 + 0.006667 + 0.006292 + 0.006416 + 0.00725 + 0.006084 + 0.00525 + 0.005083 + 0.005417 + 0.005667 + 0.004958 + 0.004875 + 0.006375 + 0.004667 + 0.004125 + 0.004375 + 0.004667 + 0.004 + 0.004333 + 0.003959 + 0.004125 + 0.003917 + 0.004334 + 0.004167 + 0.003958 + 0.004125 + 0.004458 + 0.004125 + 0.0045 + 0.004042 + 0.004417 + 0.004125 + 0.004292 + 0.0045 + 0.004583 + 0.004042 + 0.004334 + 0.004125 + 0.004417 + 0.004208 + 0.004125 + 0.004 + 0.004208 + 0.003875 + 0.003792 + 0.004 + 0.00375 + 0.004292 + 0.003917 + 0.003916 + 0.004084 + 0.003917 + 0.004375 + 0.004916 + 0.004375 + 0.004333 + 0.004333 + 0.004417 + 0.004375 + 0.004375 + 0.004417 + 0.00425 + 0.005292 + 0.004958 + 0.009333 + 0.006959 + 0.00625 + 0.007 + 0.0065 + 0.006541 + 0.005583 + 0.005625 + 0.004416 + 0.004167 + 0.004167 + 0.005541 + 0.005708 + 0.005416 + 0.005375 + 0.005833 + 0.005375 + 0.005542 + 0.0055 + 0.00575 + 0.004375 + 0.004375 + 0.004333 + 0.0045 + 0.004417 + 0.005458 + 0.005417 + 0.006541 + 0.006875 + 0.006291 + 0.006458 + 0.006333 + 0.005291 + 0.006417 + 0.007084 + 0.006959 + 0.006917 + 0.006625 + 0.006417 + 0.006375 + 0.008 + 0.005792 + 0.00525 + 0.005333 + 0.004958 + 0.005083 + 0.005167 + 0.005458 + 0.005208 + 0.005167 + 0.005 + 0.00375 + 0.006333 + 0.011333 + 0.004334 + 0.004292 + 0.004333 + 0.004792 + 0.004667 + 0.005125 + 0.016084 + 0.0095 + 0.005625 + 0.005584 + 0.005291 + 0.00475 + 0.00525 + 0.004959 + 0.0045 + 0.004333 + 0.004125 + 0.004292 + 0.004333 + 0.004083 + 0.004042 + 0.004084 + 0.003917 + 0.004083 + 0.003917 + 0.003959 + 0.004667 + 0.004625 + 0.004709 + 0.005042 + 0.005 + 0.004959 + 0.005167 + 0.005291 + 0.005167 + 0.00625 + 0.00575 + 0.005625 + 0.005709 + 0.005209 + 0.00475 + 0.004459 + 0.004666 + 0.017417 + 0.006417 + 0.005625 + 0.007792 + 0.006708 + 0.006875 + 0.006833 + 0.00675 + 0.006666 + 0.005833 + 0.00525 + 0.006541 + 0.004625 + 0.003875 + 0.004416 + 0.004209 + 0.00425 + 0.00425 + 0.004875 + 0.004042 + 0.004291 + 0.0045 + 0.00425 + 0.004541 + 0.004042 + 0.00425 + 0.004375 + 0.004209 + 0.005291 + 0.00675 + 0.00625 + 0.00625 + 0.005792 + 0.006084 + 0.005209 + 0.006541 + 0.005167 + 0.006209 + 0.005709 + 0.00625 + 0.005875 + 0.004875 + 0.005916 + 0.00575 + 0.005208 + 0.004833 + 0.005083 + 0.00475 + 0.004542 + 0.004 + 0.003667 + 0.00425 + 0.004375 + 0.003917 + 0.003583 + 0.003916 + 0.004333 + 0.004333 + 0.004042 + 0.003875 + 0.003834 + 0.004209 + 0.003875 + 0.003833 + 0.004292 + 0.004208 + 0.004042 + 0.004125 + 0.004167 + 0.004292 + 0.00425 + 0.004375 + 0.003834 + 0.003959 + 0.004042 + 0.004083 + 0.004125 + 0.003958 + 0.00375 + 0.004416 + 0.004 + 0.003875 + 0.004167 + 0.004083 + 0.003959 + 0.004 + 0.003708 + 0.003792 + 0.0125 + 0.003833 + 0.004083 + 0.00375 + 0.004375 + 0.005 + 0.004916 + 0.004917 + 0.004208 + 0.004125 + 0.00425 + 0.004209 + 0.004208 + 0.004458 + 0.0055 + 0.007 + 0.007 + 0.006458 + 0.00675 + 0.006458 + 0.006583 + 0.004375 + 0.004458 + 0.004666 + 0.004583 + 0.005333 + 0.005292 + 0.00475 + 0.004292 + 0.003875 + 0.004042 + 0.004041 + 0.004167 + 0.004125 + 0.004292 + 0.003916 + 0.004333 + 0.004209 + 0.004209 + 0.004542 + 0.004833 + 0.006625 + 0.005916 + 0.006084 + 0.006625 + 0.007375 + 0.006166 + 0.006292 + 0.00575 + 0.008542 + 0.006417 + 0.00575 + 0.008583 + 0.005583 + 0.005125 + 0.006625 + 0.004791 + 0.0045 + 0.004709 + 0.004667 + 0.004958 + 0.004417 + 0.004209 + 0.004584 + 0.0045 + 0.004167 + 0.003375 + 0.004167 + 0.004542 + 0.004042 + 0.004375 + 0.004167 + 0.003833 + 0.003667 + 0.004334 + 0.004125 + 0.003792 + 0.003709 + 0.003917 + 0.003833 + 0.00375 + 0.004083 + 0.003917 + 0.004042 + 0.004042 + 0.004125 + 0.004125 + 0.003958 + 0.004042 + 0.004042 + 0.004375 + 0.004209 + 0.003708 + 0.004083 + 0.004125 + 0.004083 + 0.004 + 0.00425 + 0.00425 + 0.004333 + 0.003458 + 0.011875 + 0.003791 + 0.003875 + 0.004625 + 0.0045 + 0.004125 + 0.0045 + 0.004625 + 0.00525 + 0.005375 + 0.00525 + 0.005792 + 0.005541 + 0.007042 + 0.006334 + 0.010166 + 0.006042 + 0.006 + 0.005833 + 0.006083 + 0.005458 + 0.004292 + 0.0045 + 0.004375 + 0.003917 + 0.004584 + 0.004459 + 0.006208 + 0.00475 + 0.004834 + 0.004334 + 0.005 + 0.005083 + 0.004833 + 0.00425 + 0.00425 + 0.004208 + 0.004042 + 0.00425 + 0.004041 + 0.005125 + 0.005208 + 0.005041 + 0.007167 + 0.006209 + 0.00575 + 0.005084 + 0.004375 + 0.004792 + 0.005084 + 0.005417 + 0.006708 + 0.006583 + 0.006291 + 0.006583 + 0.006458 + 0.005167 + 0.005042 + 0.005083 + 0.004375 + 0.004 + 0.004083 + 0.004 + 0.004458 + 0.004 + 0.004417 + 0.004042 + 0.004042 + 0.004125 + 0.004042 + 0.004 + 0.004125 + 0.003917 + 0.00375 + 0.004 + 0.004 + 0.003666 + 0.004084 + 0.004 + 0.003834 + 0.004125 + 0.003958 + 0.004 + 0.003625 + 0.003959 + 0.004334 + 0.004084 + 0.003875 + 0.003875 + 0.003875 + 0.004375 + 0.004 + 0.004084 + 0.003959 + 0.004 + 0.004125 + 0.004 + 0.004166 + 0.004458 + 0.003542 + 0.004 + 0.003792 + 0.004042 + 0.003541 + 0.004208 + 0.00425 + 0.004 + 0.003875 + 0.004083 + 0.005291 + 0.005458 + 0.005167 + 0.005291 + 0.006833 + 0.006541 + 0.006458 + 0.006333 + 0.006625 + 0.007042 + 0.0065 + 0.00625 + 0.005791 + 0.005417 + 0.005542 + 0.005458 + 0.004833 + 0.004166 + 0.00425 + 0.005 + 0.004709 + 0.004125 + 0.003958 + 0.004375 + 0.0055 + 0.005458 + 0.00525 + 0.005084 + 0.005167 + 0.005541 + 0.005458 + 0.006542 + 0.006375 + 0.004959 + 0.005375 + 0.006125 + 0.006958 + 0.006166 + 0.005458 + 0.006458 + 0.006292 + 0.006292 + 0.008041 + 0.007083 + 0.006833 + 0.006417 + 0.006584 + 0.005375 + 0.004834 + 0.00475 + 0.004667 + 0.004542 + 0.003791 + 0.004 + 0.004958 + 0.004041 + 0.003959 + 0.003667 + 0.004708 + 0.004 + 0.003959 + 0.004583 + 0.003875 + 0.004125 + 0.004083 + 0.003958 + 0.003708 + 0.004333 + 0.004042 + 0.003958 + 0.003625 + 0.003834 + 0.003792 + 0.004042 + 0.004042 + 0.004167 + 0.003875 + 0.004292 + 0.004083 + 0.003833 + 0.003958 + 0.003958 + 0.00375 + 0.004209 + 0.004292 + 0.004333 + 0.004417 + 0.004 + 0.004041 + 0.004417 + 0.003959 + 0.004209 + 0.004041 + 0.00425 + 0.003833 + 0.004583 + 0.004041 + 0.004083 + 0.0045 + 0.004792 + 0.006667 + 0.005459 + 0.005291 + 0.005208 + 0.006458 + 0.006291 + 0.006042 + 0.005334 + 0.006459 + 0.006416 + 0.006583 + 0.0065 + 0.005291 + 0.005208 + 0.005708 + 0.00575 + 0.005041 + 0.00425 + 0.004292 + 0.004375 + 0.005584 + 0.0055 + 0.005833 + 0.005041 + 0.004958 + 0.005209 + 0.00525 + 0.005458 + 0.00525 + 0.004166 + 0.0045 + 0.005208 + 0.005291 + 0.006542 + 0.009792 + 0.006167 + 0.006209 + 0.005375 + 0.005417 + 0.005333 + 0.006875 + 0.00625 + 0.005833 + 0.006792 + 0.005708 + 0.006083 + 0.006833 + 0.004875 + 0.00525 + 0.004416 + 0.004625 + 0.004375 + 0.004834 + 0.004959 + 0.005584 + 0.004959 + 0.004875 + 0.00475 + 0.004417 + 0.004625 + 0.005292 + 0.004833 + 0.004708 + 0.004417 + 0.004375 + 0.004625 + 0.005375 + 0.004542 + 0.004459 + 0.004542 + 0.005041 + 0.004459 + 0.004375 + 0.004459 + 0.004792 + 0.004625 + 0.004959 + 0.004917 + 0.00425 + 0.004541 + 0.00425 + 0.004833 + 0.004958 + 0.005417 + 0.017084 + 0.006542 + 0.005208 + 0.004666 + 0.004875 + 0.005208 + 0.004583 + 0.005 + 0.004541 + 0.005083 + 0.004875 + 0.0045 + 0.006042 + 0.013375 + 0.005167 + 0.004417 + 0.0045 + 0.005125 + 0.004625 + 0.004584 + 0.005166 + 0.006083 + 0.005833 + 0.013542 + 0.007416 + 0.006958 + 0.007125 + 0.016375 + 0.006458 + 0.005291 + 0.006292 + 0.006625 + 0.006792 + 0.006375 + 0.005042 + 0.006709 + 0.005792 + 0.005708 + 0.004958 + 0.005 + 0.004875 + 0.004917 + 0.005208 + 0.004625 + 0.004875 + 0.012791 + 0.005583 + 0.0065 + 0.006 + 0.006875 + 0.00675 + 0.007834 + 0.007209 + 0.006541 + 0.007 + 0.00625 + 0.01025 + 0.006875 + 0.0075 + 0.007167 + 0.007125 + 0.00675 + 0.006375 + 0.005042 + 0.00725 + 0.004459 + 0.004083 + 0.004041 + 0.004167 + 0.004166 + 0.005083 + 0.005208 + 0.003875 + 0.004083 + 0.00425 + 0.004666 + 0.004083 + 0.0045 + 0.004666 + 0.004583 + 0.004125 + 0.00425 + 0.004125 + 0.004208 + 0.004792 + 0.004333 + 0.004083 + 0.004459 + 0.004 + 0.004 + 0.004333 + 0.004333 + 0.004792 + 0.005667 + 0.00575 + 0.006375 + 0.005375 + 0.005042 + 0.004417 + 0.004208 + 0.004708 + 0.004708 + 0.004708 + 0.005209 + 0.005417 + 0.005333 + 0.00525 + 0.005291 + 0.005333 + 0.0055 + 0.004709 + 0.005208 + 0.007834 + 0.007084 + 0.006209 + 0.004917 + 0.004875 + 0.004709 + 0.005584 + 0.00475 + 0.005125 + 0.005916 + 0.005917 + 0.005792 + 0.007166 + 0.0075 + 0.006458 + 0.006583 + 0.00625 + 0.005708 + 0.005792 + 0.005541 + 0.006083 + 0.006125 + 0.005333 + 0.004417 + 0.004875 + 0.004459 + 0.004083 + 0.005167 + 0.005958 + 0.005708 + 0.005417 + 0.004875 + 0.005 + 0.0045 + 0.004625 + 0.00425 + 0.005583 + 0.005208 + 0.00525 + 0.0065 + 0.006084 + 0.006125 + 0.006042 + 0.005292 + 0.005708 + 0.005625 + 0.006583 + 0.007041 + 0.0065 + 0.00625 + 0.005875 + 0.006666 + 0.004417 + 0.005167 + 0.004959 + 0.004709 + 0.004792 + 0.004875 + 0.004709 + 0.006084 + 0.005 + 0.005125 + 0.004458 + 0.004625 + 0.0045 + 0.004667 + 0.004667 + 0.004208 + 0.003625 + 0.003709 + 0.003667 + 0.003958 + 0.003458 + 0.003583 + 0.00375 + 0.003417 + 0.003958 + 0.003708 + 0.003666 + 0.003375 + 0.003291 + 0.003208 + 0.003208 + 0.004 + 0.004 + 0.003708 + 0.00325 + 0.003459 + 0.003416 + 0.004 + 0.003583 + 0.003708 + 0.003125 + 0.003333 + 0.003667 + 0.003167 + 0.003667 + 0.004 + 0.003916 + 0.003459 + 0.004125 + 0.004 + 0.003708 + 0.00375 + 0.003625 + 0.003375 + 0.003666 + 0.00375 + 0.004875 + 0.005375 + 0.005375 + 0.005417 + 0.006 + 0.005791 + 0.005083 + 0.005875 + 0.0055 + 0.00475 + 0.004458 + 0.004667 + 0.004209 + 0.004 + 0.003917 + 0.003375 + 0.003625 + 0.003458 + 0.003708 + 0.003834 + 0.004084 + 0.004083 + 0.003458 + 0.003375 + 0.003625 + 0.003916 + 0.003959 + 0.004459 + 0.005792 + 0.0055 + 0.005167 + 0.005292 + 0.005291 + 0.005667 + 0.006125 + 0.005417 + 0.00625 + 0.006167 + 0.005917 + 0.005584 + 0.005417 + 0.006333 + 0.006458 + 0.006041 + 0.00525 + 0.004625 + 0.004583 + 0.004291 + 0.004125 + 0.004084 + 0.00425 + 0.004292 + 0.004875 + 0.004167 + 0.004583 + 0.004417 + 0.004334 + 0.004667 + 0.004375 + 0.004375 + 0.004125 + 0.004375 + 0.004 + 0.00475 + 0.004291 + 0.004208 + 0.003917 + 0.004083 + 0.004167 + 0.004041 + 0.004417 + 0.004375 + 0.004291 + 0.004417 + 0.004417 + 0.004375 + 0.004459 + 0.004333 + 0.004291 + 0.004042 + 0.004041 + 0.004209 + 0.004125 + 0.0045 + 0.004042 + 0.004166 + 0.004209 + 0.003834 + 0.003916 + 0.004459 + 0.004584 + 0.004167 + 0.004291 + 0.004458 + 0.004167 + 0.004875 + 0.006459 + 0.004542 + 0.004542 + 0.00425 + 0.004583 + 0.0065 + 0.005916 + 0.00575 + 0.006084 + 0.00575 + 0.005458 + 0.006125 + 0.006625 + 0.005125 + 0.004417 + 0.005375 + 0.005291 + 0.004625 + 0.004166 + 0.004042 + 0.004958 + 0.004333 + 0.005042 + 0.004334 + 0.004584 + 0.004292 + 0.004333 + 0.004291 + 0.004417 + 0.00575 + 0.005125 + 0.004875 + 0.006542 + 0.006334 + 0.005875 + 0.005333 + 0.004958 + 0.005959 + 0.006209 + 0.006334 + 0.005125 + 0.006125 + 0.005417 + 0.005459 + 0.006292 + 0.00575 + 0.005625 + 0.005875 + 0.005292 + 0.004709 + 0.004 + 0.003917 + 0.003833 + 0.004042 + 0.004042 + 0.003666 + 0.004458 + 0.004334 + 0.003792 + 0.003583 + 0.00425 + 0.004583 + 0.004209 + 0.003875 + 0.004084 + 0.004 + 0.003959 + 0.003959 + 0.004459 + 0.004042 + 0.004458 + 0.003958 + 0.003916 + 0.003792 + 0.004041 + 0.004125 + 0.004167 + 0.004334 + 0.004292 + 0.003417 + 0.003917 + 0.003917 + 0.003917 + 0.003791 + 0.003625 + 0.003958 + 0.004125 + 0.003958 + 0.004042 + 0.004334 + 0.004083 + 0.004458 + 0.004834 + 0.004834 + 0.004709 + 0.004459 + 0.004292 + 0.004833 + 0.004208 + 0.0045 + 0.004166 + 0.004292 + 0.004 + 0.004792 + 0.004167 + 0.004417 + 0.006958 + 0.007125 + 0.005667 + 0.005791 + 0.005709 + 0.005875 + 0.005083 + 0.004333 + 0.004833 + 0.0045 + 0.005709 + 0.005209 + 0.005292 + 0.005375 + 0.005209 + 0.005125 + 0.004625 + 0.004792 + 0.004542 + 0.004125 + 0.004792 + 0.004916 + 0.004833 + 0.004834 + 0.004709 + 0.004584 + 0.005 + 0.007 + 0.007125 + 0.005666 + 0.00675 + 0.006167 + 0.005583 + 0.006875 + 0.005708 + 0.00625 + 0.006167 + 0.006625 + 0.005583 + 0.00575 + 0.006375 + 0.006458 + 0.005 + 0.005167 + 0.005333 + 0.005 + 0.003916 + 0.004 + 0.003917 + 0.00475 + 0.004958 + 0.004042 + 0.003584 + 0.00425 + 0.004292 + 0.003708 + 0.004458 + 0.004292 + 0.004417 + 0.003958 + 0.004833 + 0.00475 + 0.00475 + 0.004833 + 0.005333 + 0.00525 + 0.005167 + 0.004958 + 0.005416 + 0.005167 + 0.005083 + 0.005166 + 0.005459 + 0.003875 + 0.004084 + 0.003709 + 0.00375 + 0.003875 + 0.004041 + 0.004 + 0.004167 + 0.004209 + 0.004167 + 0.004208 + 0.004 + 0.003792 + 0.004041 + 0.003958 + 0.004417 + 0.004084 + 0.004042 + 0.004833 + 0.004583 + 0.004042 + 0.004166 + 0.004417 + 0.004084 + 0.004209 + 0.004417 + 0.004291 + 0.005042 + 0.006667 + 0.006 + 0.006084 + 0.005709 + 0.006 + 0.005667 + 0.004333 + 0.004291 + 0.004542 + 0.004541 + 0.004167 + 0.004459 + 0.006167 + 0.005583 + 0.005167 + 0.005084 + 0.005625 + 0.00525 + 0.004334 + 0.004667 + 0.004041 + 0.004208 + 0.004125 + 0.004417 + 0.004375 + 0.004166 + 0.005125 + 0.005042 + 0.006292 + 0.006458 + 0.006208 + 0.005791 + 0.005958 + 0.004958 + 0.004833 + 0.004625 + 0.00675 + 0.006 + 0.006083 + 0.005708 + 0.00625 + 0.004708 + 0.004166 + 0.003917 + 0.004209 + 0.003791 + 0.003792 + 0.004084 + 0.004 + 0.004 + 0.003917 + 0.004417 + 0.004125 + 0.004542 + 0.003958 + 0.004209 + 0.004 + 0.004292 + 0.004875 + 0.004834 + 0.004458 + 0.004375 + 0.003792 + 0.00425 + 0.004042 + 0.003958 + 0.00375 + 0.004041 + 0.004083 + 0.005792 + 0.005083 + 0.005625 + 0.005958 + 0.005375 + 0.004917 + 0.0045 + 0.0045 + 0.004875 + 0.005125 + 0.004958 + 0.005667 + 0.005042 + 0.005042 + 0.005 + 0.005125 + 0.004375 + 0.004416 + 0.004917 + 0.005166 + 0.005292 + 0.005625 + 0.005833 + 0.0055 + 0.00475 + 0.005 + 0.005458 + 0.006333 + 0.005958 + 0.006208 + 0.006333 + 0.005833 + 0.005542 + 0.006708 + 0.006417 + 0.006666 + 0.005458 + 0.005167 + 0.005 + 0.004542 + 0.004458 + 0.004541 + 0.004042 + 0.004708 + 0.004708 + 0.004208 + 0.005667 + 0.00575 + 0.005917 + 0.006125 + 0.00575 + 0.005334 + 0.006292 + 0.006541 + 0.006417 + 0.005959 + 0.0045 + 0.005708 + 0.005708 + 0.0055 + 0.007209 + 0.007209 + 0.006292 + 0.005875 + 0.005584 + 0.004917 + 0.006583 + 0.006333 + 0.008083 + 0.007 + 0.006667 + 0.006959 + 0.006458 + 0.006041 + 0.004625 + 0.005208 + 0.005375 + 0.005333 + 0.005667 + 0.005375 + 0.004375 + 0.0055 + 0.005209 + 0.004625 + 0.00575 + 0.005084 + 0.00475 + 0.004459 + 0.0055 + 0.005625 + 0.005916 + 0.005667 + 0.005875 + 0.006083 + 0.0055 + 0.005042 + 0.004709 + 0.004375 + 0.004709 + 0.0045 + 0.005 + 0.004708 + 0.0045 + 0.004792 + 0.004292 + 0.004417 + 0.004125 + 0.00425 + 0.005375 + 0.005166 + 0.0055 + 0.005667 + 0.005125 + 0.005458 + 0.004833 + 0.004792 + 0.005 + 0.005209 + 0.005084 + 0.004792 + 0.004083 + 0.004167 + 0.004416 + 0.0045 + 0.004333 + 0.006708 + 0.005458 + 0.005625 + 0.005292 + 0.005834 + 0.006125 + 0.006375 + 0.009875 + 0.005167 + 0.007625 + 0.009375 + 0.008375 + 0.008709 + 0.00825 + 0.00775 + 0.007291 + 0.006375 + 0.00675 + 0.006125 + 0.00675 + 0.006375 + 0.006333 + 0.005875 + 0.006667 + 0.0065 + 0.006 + 0.006375 + 0.006166 + 0.005708 + 0.004917 + 0.005666 + 0.005542 + 0.005792 + 0.006917 + 0.00675 + 0.007791 + 0.006333 + 0.006125 + 0.006083 + 0.006125 + 0.0045 + 0.004917 + 0.006125 + 0.006125 + 0.006208 + 0.005583 + 0.006333 + 0.006125 + 0.005 + 0.00475 + 0.005042 + 0.003959 + 0.004459 + 0.004542 + 0.004083 + 0.00375 + 0.004125 + 0.004541 + 0.00375 + 0.004583 + 0.003958 + 0.004291 + 0.004125 + 0.004042 + 0.004167 + 0.003833 + 0.004 + 0.004041 + 0.00425 + 0.004708 + 0.004417 + 0.004292 + 0.004375 + 0.004 + 0.005 + 0.005333 + 0.004834 + 0.004958 + 0.004333 + 0.003792 + 0.00475 + 0.0055 + 0.005333 + 0.004875 + 0.005334 + 0.00525 + 0.004833 + 0.005125 + 0.005417 + 0.005334 + 0.004709 + 0.004084 + 0.004084 + 0.004167 + 0.003958 + 0.00425 + 0.004459 + 0.004541 + 0.004333 + 0.007667 + 0.006 + 0.00525 + 0.004375 + 0.004458 + 0.0045 + 0.004875 + 0.005708 + 0.005625 + 0.005416 + 0.004958 + 0.006333 + 0.007083 + 0.006875 + 0.006 + 0.005542 + 0.005584 + 0.005459 + 0.005 + 0.004875 + 0.004917 + 0.005 + 0.004458 + 0.004708 + 0.004416 + 0.004458 + 0.004292 + 0.005791 + 0.006042 + 0.006083 + 0.006041 + 0.005542 + 0.006167 + 0.006291 + 0.006583 + 0.005542 + 0.005625 + 0.007042 + 0.006958 + 0.006416 + 0.006667 + 0.005792 + 0.006708 + 0.006333 + 0.007292 + 0.006542 + 0.006875 + 0.006417 + 0.006667 + 0.005958 + 0.005208 + 0.005041 + 0.004959 + 0.004417 + 0.004333 + 0.00425 + 0.004167 + 0.004709 + 0.00375 + 0.004584 + 0.004459 + 0.004166 + 0.004209 + 0.004041 + 0.004125 + 0.004166 + 0.0045 + 0.004292 + 0.004375 + 0.005208 + 0.005667 + 0.005333 + 0.005125 + 0.005583 + 0.005666 + 0.005125 + 0.00525 + 0.005084 + 0.004458 + 0.004208 + 0.004208 + 0.004291 + 0.004709 + 0.004084 + 0.004334 + 0.004667 + 0.004458 + 0.004209 + 0.003791 + 0.003875 + 0.003917 + 0.004291 + 0.004417 + 0.004167 + 0.004291 + 0.004167 + 0.004375 + 0.004209 + 0.004459 + 0.006125 + 0.00575 + 0.005667 + 0.005708 + 0.005541 + 0.005542 + 0.005708 + 0.00575 + 0.006041 + 0.005042 + 0.005125 + 0.006708 + 0.006958 + 0.007208 + 0.006541 + 0.005417 + 0.00575 + 0.005833 + 0.005125 + 0.004833 + 0.004584 + 0.004292 + 0.004 + 0.004584 + 0.004375 + 0.004709 + 0.006541 + 0.005833 + 0.005916 + 0.005917 + 0.005458 + 0.005125 + 0.005792 + 0.005792 + 0.005125 + 0.00525 + 0.005417 + 0.004958 + 0.007083 + 0.006791 + 0.007 + 0.006625 + 0.005375 + 0.006667 + 0.006375 + 0.006125 + 0.006833 + 0.006375 + 0.006583 + 0.006458 + 0.006375 + 0.005625 + 0.005292 + 0.005041 + 0.004333 + 0.003958 + 0.00425 + 0.004083 + 0.004375 + 0.004042 + 0.004375 + 0.003833 + 0.004625 + 0.004166 + 0.003875 + 0.003875 + 0.00375 + 0.004208 + 0.004125 + 0.004291 + 0.003625 + 0.003917 + 0.004292 + 0.004084 + 0.004125 + 0.004584 + 0.00425 + 0.004 + 0.003958 + 0.004125 + 0.004 + 0.004084 + 0.004042 + 0.004167 + 0.004042 + 0.004041 + 0.004083 + 0.003833 + 0.004167 + 0.003833 + 0.004375 + 0.004042 + 0.00375 + 0.004083 + 0.004375 + 0.004166 + 0.004167 + 0.004042 + 0.00425 + 0.004291 + 0.004584 + 0.003875 + 0.004708 + 0.004625 + 0.005709 + 0.005958 + 0.005542 + 0.00525 + 0.00675 + 0.006375 + 0.006583 + 0.00625 + 0.006833 + 0.0065 + 0.007 + 0.006167 + 0.005542 + 0.0055 + 0.005416 + 0.005875 + 0.00475 + 0.004583 + 0.004125 + 0.004 + 0.00575 + 0.006 + 0.005791 + 0.005417 + 0.005791 + 0.006209 + 0.005459 + 0.005709 + 0.004875 + 0.004708 + 0.006208 + 0.00575 + 0.00525 + 0.005541 + 0.007458 + 0.006833 + 0.022375 + 0.025916 + 0.006958 + 0.005667 + 0.005375 + 0.005875 + 0.005375 + 0.007875 + 0.006583 + 0.005833 + 0.005875 + 0.005166 + 0.004875 + 0.004209 + 0.004667 + 0.004375 + 0.005042 + 0.004791 + 0.005792 + 0.005458 + 0.005208 + 0.005875 + 0.004667 + 0.005875 + 0.005167 + 0.005041 + 0.005167 + 0.004292 + 0.003875 + 0.004584 + 0.003875 + 0.004209 + 0.004084 + 0.00425 + 0.004334 + 0.00475 + 0.004667 + 0.004667 + 0.004541 + 0.004125 + 0.004167 + 0.004625 + 0.003916 + 0.004083 + 0.004208 + 0.004417 + 0.004208 + 0.004541 + 0.004459 + 0.003875 + 0.003792 + 0.00375 + 0.003917 + 0.003708 + 0.003667 + 0.003875 + 0.004458 + 0.00425 + 0.004333 + 0.004167 + 0.004625 + 0.004458 + 0.00475 + 0.004125 + 0.004666 + 0.004208 + 0.005625 + 0.005208 + 0.005416 + 0.006625 + 0.006542 + 0.0055 + 0.005 + 0.005042 + 0.008166 + 0.00625 + 0.005 + 0.004834 + 0.005291 + 0.0045 + 0.00425 + 0.004208 + 0.004208 + 0.004208 + 0.003708 + 0.004667 + 0.00425 + 0.004583 + 0.004208 + 0.004417 + 0.00425 + 0.004 + 0.00575 + 0.005375 + 0.005084 + 0.005959 + 0.006583 + 0.005917 + 0.0055 + 0.005416 + 0.006125 + 0.006208 + 0.006083 + 0.005 + 0.00625 + 0.005333 + 0.004833 + 0.006417 + 0.006042 + 0.005792 + 0.005709 + 0.004917 + 0.004625 + 0.004166 + 0.004334 + 0.003958 + 0.003791 + 0.004042 + 0.003667 + 0.004541 + 0.003708 + 0.003625 + 0.004167 + 0.00475 + 0.004834 + 0.004917 + 0.004125 + 0.00425 + 0.003667 + 0.003834 + 0.004 + 0.004334 + 0.004334 + 0.004292 + 0.003958 + 0.00425 + 0.00425 + 0.005083 + 0.005375 + 0.005792 + 0.005167 + 0.005833 + 0.005375 + 0.004667 + 0.004417 + 0.004375 + 0.004417 + 0.003916 + 0.0035 + 0.003958 + 0.004333 + 0.003667 + 0.003584 + 0.003834 + 0.003792 + 0.003958 + 0.003917 + 0.003625 + 0.004208 + 0.005 + 0.005125 + 0.004958 + 0.005208 + 0.004958 + 0.004833 + 0.004042 + 0.004333 + 0.005375 + 0.005416 + 0.006167 + 0.006 + 0.006083 + 0.005542 + 0.005458 + 0.006417 + 0.006209 + 0.005042 + 0.004833 + 0.005333 + 0.004958 + 0.004291 + 0.004417 + 0.004125 + 0.003917 + 0.004208 + 0.004458 + 0.004125 + 0.003834 + 0.004334 + 0.004583 + 0.004916 + 0.004667 + 0.005875 + 0.005417 + 0.005291 + 0.004625 + 0.006 + 0.005917 + 0.00475 + 0.005125 + 0.005084 + 0.006166 + 0.006292 + 0.004875 + 0.006 + 0.006125 + 0.005375 + 0.005166 + 0.006 + 0.006708 + 0.005458 + 0.005541 + 0.004875 + 0.0045 + 0.004375 + 0.003958 + 0.004125 + 0.004458 + 0.004708 + 0.005667 + 0.004625 + 0.005167 + 0.005875 + 0.005541 + 0.005417 + 0.005125 + 0.00475 + 0.005 + 0.004666 + 0.004958 + 0.004458 + 0.0045 + 0.004833 + 0.004292 + 0.004125 + 0.004084 + 0.003917 + 0.004125 + 0.004083 + 0.004584 + 0.00475 + 0.004208 + 0.004 + 0.004208 + 0.003958 + 0.004041 + 0.003875 + 0.003958 + 0.004042 + 0.004 + 0.003875 + 0.004 + 0.003958 + 0.00375 + 0.004209 + 0.003625 + 0.004084 + 0.003792 + 0.003708 + 0.004125 + 0.004 + 0.003791 + 0.004583 + 0.005042 + 0.005167 + 0.005333 + 0.005375 + 0.006792 + 0.00575 + 0.005459 + 0.006167 + 0.0055 + 0.005667 + 0.005041 + 0.006917 + 0.006458 + 0.006541 + 0.005458 + 0.005583 + 0.004625 + 0.004583 + 0.004666 + 0.004958 + 0.004459 + 0.00425 + 0.004292 + 0.005042 + 0.004583 + 0.004417 + 0.005875 + 0.005625 + 0.004875 + 0.004625 + 0.00475 + 0.00425 + 0.004375 + 0.005459 + 0.00525 + 0.005084 + 0.005333 + 0.006042 + 0.0065 + 0.00625 + 0.005583 + 0.006416 + 0.00625 + 0.006625 + 0.007291 + 0.006542 + 0.007084 + 0.006625 + 0.006208 + 0.005625 + 0.005083 + 0.005 + 0.004667 + 0.004167 + 0.004 + 0.004 + 0.003791 + 0.004125 + 0.003833 + 0.003875 + 0.003833 + 0.004125 + 0.004167 + 0.004 + 0.004083 + 0.004125 + 0.003792 + 0.003958 + 0.003792 + 0.003959 + 0.004792 + 0.005 + 0.005208 + 0.004916 + 0.004792 + 0.004875 + 0.005125 + 0.004833 + 0.005125 + 0.004917 + 0.004791 + 0.004083 + 0.004083 + 0.004042 + 0.004208 + 0.003917 + 0.00375 + 0.003625 + 0.004041 + 0.003958 + 0.004083 + 0.004 + 0.004125 + 0.003792 + 0.004125 + 0.004 + 0.003834 + 0.004333 + 0.004291 + 0.004 + 0.004209 + 0.004 + 0.005834 + 0.005834 + 0.005291 + 0.005292 + 0.006542 + 0.006541 + 0.006208 + 0.006458 + 0.006708 + 0.006875 + 0.006584 + 0.0065 + 0.005666 + 0.005208 + 0.005208 + 0.005625 + 0.005042 + 0.004541 + 0.004167 + 0.004625 + 0.004084 + 0.004167 + 0.00525 + 0.005375 + 0.005083 + 0.004958 + 0.00525 + 0.005125 + 0.0055 + 0.0055 + 0.005166 + 0.005792 + 0.00575 + 0.004917 + 0.006125 + 0.006833 + 0.006125 + 0.006458 + 0.005958 + 0.005541 + 0.006833 + 0.006875 + 0.006708 + 0.00675 + 0.006709 + 0.006084 + 0.006083 + 0.005208 + 0.00525 + 0.004542 + 0.004042 + 0.004416 + 0.003958 + 0.004125 + 0.003584 + 0.005 + 0.005333 + 0.004917 + 0.005375 + 0.005375 + 0.00525 + 0.005041 + 0.005084 + 0.004875 + 0.004292 + 0.00375 + 0.003708 + 0.004083 + 0.004166 + 0.003833 + 0.00375 + 0.003958 + 0.003959 + 0.004125 + 0.004292 + 0.004666 + 0.004375 + 0.004084 + 0.004125 + 0.004167 + 0.003834 + 0.003917 + 0.004166 + 0.004292 + 0.004291 + 0.003917 + 0.0045 + 0.004375 + 0.003917 + 0.003917 + 0.004583 + 0.004 + 0.003625 + 0.003834 + 0.004 + 0.003291 + 0.004083 + 0.006625 + 0.005166 + 0.005417 + 0.005375 + 0.005458 + 0.00525 + 0.005375 + 0.005583 + 0.006375 + 0.006625 + 0.005833 + 0.007 + 0.007209 + 0.007042 + 0.006584 + 0.005625 + 0.005875 + 0.005708 + 0.005417 + 0.00475 + 0.008833 + 0.004625 + 0.004708 + 0.00425 + 0.004416 + 0.006583 + 0.005708 + 0.005875 + 0.005375 + 0.0055 + 0.005 + 0.005292 + 0.005875 + 0.005542 + 0.0055 + 0.005792 + 0.006 + 0.006834 + 0.007625 + 0.022167 + 0.010375 + 0.015709 + 0.0055 + 0.006458 + 0.007792 + 0.006875 + 0.007208 + 0.006959 + 0.006166 + 0.005958 + 0.00575 + 0.006542 + 0.00475 + 0.004667 + 0.005583 + 0.004958 + 0.004458 + 0.00525 + 0.004875 + 0.005333 + 0.004083 + 0.005166 + 0.004959 + 0.004834 + 0.004167 + 0.004625 + 0.004791 + 0.00475 + 0.005291 + 0.005708 + 0.004875 + 0.004333 + 0.004583 + 0.005 + 0.004625 + 0.004458 + 0.00425 + 0.005375 + 0.004958 + 0.0045 + 0.004375 + 0.004291 + 0.004375 + 0.004125 + 0.0045 + 0.004625 + 0.003833 + 0.004792 + 0.004333 + 0.004375 + 0.004042 + 0.004666 + 0.004292 + 0.004292 + 0.003958 + 0.0045 + 0.004167 + 0.004333 + 0.0045 + 0.005125 + 0.004375 + 0.004333 + 0.006084 + 0.005375 + 0.005125 + 0.005333 + 0.005041 + 0.005833 + 0.005917 + 0.005 + 0.005292 + 0.005542 + 0.006583 + 0.00675 + 0.006459 + 0.005625 + 0.00525 + 0.005041 + 0.004625 + 0.00475 + 0.004625 + 0.00425 + 0.004375 + 0.004458 + 0.004416 + 0.003834 + 0.0045 + 0.004084 + 0.005833 + 0.005 + 0.005542 + 0.005333 + 0.004958 + 0.005292 + 0.004375 + 0.005375 + 0.005375 + 0.005083 + 0.005208 + 0.006292 + 0.006417 + 0.006208 + 0.005333 + 0.00675 + 0.00525 + 0.005208 + 0.00725 + 0.00625 + 0.006292 + 0.005916 + 0.00525 + 0.004333 + 0.004292 + 0.004209 + 0.004334 + 0.004 + 0.00475 + 0.004625 + 0.004292 + 0.004083 + 0.003916 + 0.003917 + 0.004375 + 0.004209 + 0.004 + 0.004208 + 0.004042 + 0.004125 + 0.004209 + 0.005083 + 0.005333 + 0.005292 + 0.005459 + 0.004834 + 0.005125 + 0.0055 + 0.00475 + 0.005125 + 0.004417 + 0.004208 + 0.004042 + 0.004291 + 0.004125 + 0.004375 + 0.004167 + 0.004209 + 0.004167 + 0.004 + 0.003666 + 0.004042 + 0.003875 + 0.004167 + 0.004 + 0.004667 + 0.004 + 0.004208 + 0.004083 + 0.003958 + 0.003292 + 0.004583 + 0.0045 + 0.0045 + 0.004167 + 0.003833 + 0.005291 + 0.005208 + 0.004792 + 0.004833 + 0.006125 + 0.006125 + 0.005125 + 0.004875 + 0.006834 + 0.007 + 0.005959 + 0.005959 + 0.005333 + 0.004708 + 0.0045 + 0.004125 + 0.004917 + 0.004667 + 0.004042 + 0.00425 + 0.004708 + 0.00425 + 0.006125 + 0.00525 + 0.005708 + 0.004666 + 0.004917 + 0.004875 + 0.004583 + 0.004958 + 0.004167 + 0.005375 + 0.005209 + 0.005375 + 0.006625 + 0.0065 + 0.006125 + 0.005958 + 0.004667 + 0.005458 + 0.004959 + 0.004667 + 0.007208 + 0.006417 + 0.00575 + 0.006 + 0.005833 + 0.0045 + 0.003708 + 0.003833 + 0.004 + 0.003958 + 0.003875 + 0.003875 + 0.004416 + 0.003542 + 0.00425 + 0.004 + 0.003958 + 0.00375 + 0.004333 + 0.004041 + 0.004083 + 0.003792 + 0.004083 + 0.00425 + 0.004208 + 0.00375 + 0.004208 + 0.003792 + 0.003667 + 0.003708 + 0.004167 + 0.003791 + 0.003833 + 0.003833 + 0.003792 + 0.004417 + 0.003834 + 0.004333 + 0.004167 + 0.004166 + 0.003625 + 0.003833 + 0.003958 + 0.004 + 0.004125 + 0.004084 + 0.004084 + 0.004292 + 0.004042 + 0.004041 + 0.003875 + 0.004042 + 0.003958 + 0.0045 + 0.004166 + 0.0045 + 0.004042 + 0.004542 + 0.005416 + 0.004542 + 0.004667 + 0.004959 + 0.005666 + 0.005709 + 0.005375 + 0.004875 + 0.005958 + 0.005958 + 0.005875 + 0.006208 + 0.005208 + 0.004458 + 0.004583 + 0.004208 + 0.004292 + 0.004208 + 0.00475 + 0.00425 + 0.00425 + 0.00425 + 0.005708 + 0.005 + 0.005625 + 0.00475 + 0.005167 + 0.004875 + 0.004167 + 0.003291 + 0.0045 + 0.004708 + 0.005042 + 0.0055 + 0.006 + 0.006625 + 0.007625 + 0.004958 + 0.004792 + 0.004959 + 0.005791 + 0.0065 + 0.005291 + 0.005292 + 0.005334 + 0.005292 + 0.005209 + 0.004333 + 0.004542 + 0.003792 + 0.003916 + 0.003833 + 0.003583 + 0.003709 + 0.003959 + 0.004583 + 0.004083 + 0.003833 + 0.004208 + 0.004417 + 0.004792 + 0.004 + 0.004625 + 0.004333 + 0.004125 + 0.00425 + 0.004625 + 0.004459 + 0.004166 + 0.004125 + 0.004417 + 0.004 + 0.004125 + 0.004 + 0.004334 + 0.003834 + 0.004125 + 0.004167 + 0.00425 + 0.003834 + 0.003959 + 0.004291 + 0.004459 + 0.004292 + 0.004625 + 0.004417 + 0.00425 + 0.004167 + 0.004 + 0.004583 + 0.004125 + 0.004042 + 0.003959 + 0.004042 + 0.003792 + 0.004125 + 0.004292 + 0.006041 + 0.005083 + 0.004958 + 0.005 + 0.005166 + 0.004583 + 0.004375 + 0.004667 + 0.005125 + 0.004459 + 0.005 + 0.008584 + 0.006084 + 0.006334 + 0.006708 + 0.005708 + 0.005417 + 0.005625 + 0.005458 + 0.005583 + 0.005042 + 0.004875 + 0.004333 + 0.004584 + 0.004541 + 0.004459 + 0.004208 + 0.005791 + 0.005125 + 0.007333 + 0.004958 + 0.005208 + 0.004583 + 0.004292 + 0.005042 + 0.005041 + 0.00475 + 0.004583 + 0.006333 + 0.006167 + 0.006 + 0.006167 + 0.00525 + 0.005625 + 0.004792 + 0.006791 + 0.006042 + 0.005709 + 0.005917 + 0.005541 + 0.004625 + 0.004041 + 0.003959 + 0.004125 + 0.003625 + 0.003709 + 0.003709 + 0.005041 + 0.004792 + 0.003458 + 0.003666 + 0.004458 + 0.004458 + 0.004375 + 0.003541 + 0.004167 + 0.003958 + 0.003917 + 0.003917 + 0.004125 + 0.004042 + 0.003833 + 0.003875 + 0.003792 + 0.003959 + 0.003917 + 0.003833 + 0.003875 + 0.004 + 0.00375 + 0.003833 + 0.003708 + 0.003458 + 0.003708 + 0.003792 + 0.003916 + 0.003917 + 0.00375 + 0.004 + 0.003625 + 0.004 + 0.003709 + 0.004209 + 0.004459 + 0.004125 + 0.004083 + 0.003667 + 0.003833 + 0.004041 + 0.004042 + 0.004041 + 0.00425 + 0.004291 + 0.00575 + 0.005125 + 0.004917 + 0.005334 + 0.004875 + 0.005875 + 0.004708 + 0.005 + 0.004417 + 0.005875 + 0.006334 + 0.00575 + 0.005 + 0.004875 + 0.004834 + 0.004042 + 0.004625 + 0.004333 + 0.004458 + 0.004 + 0.004208 + 0.004125 + 0.004166 + 0.004125 + 0.004208 + 0.004083 + 0.006292 + 0.005667 + 0.005291 + 0.004959 + 0.004875 + 0.005084 + 0.005667 + 0.004792 + 0.004416 + 0.005083 + 0.004667 + 0.006125 + 0.005792 + 0.005167 + 0.005792 + 0.006416 + 0.005875 + 0.006125 + 0.006667 + 0.006666 + 0.005625 + 0.004541 + 0.004 + 0.003917 + 0.004042 + 0.004083 + 0.003833 + 0.003458 + 0.00375 + 0.0045 + 0.003916 + 0.003667 + 0.003959 + 0.00425 + 0.004208 + 0.00425 + 0.003708 + 0.00375 + 0.003792 + 0.004 + 0.003791 + 0.00375 + 0.004042 + 0.004083 + 0.004125 + 0.003958 + 0.003916 + 0.003792 + 0.004166 + 0.003834 + 0.003625 + 0.003917 + 0.0035 + 0.003292 + 0.00375 + 0.00375 + 0.00375 + 0.00375 + 0.003875 + 0.004459 + 0.003833 + 0.004042 + 0.00375 + 0.004042 + 0.004166 + 0.004083 + 0.003916 + 0.00425 + 0.003834 + 0.004417 + 0.004125 + 0.0045 + 0.003833 + 0.00425 + 0.004083 + 0.00425 + 0.004 + 0.005958 + 0.005417 + 0.005709 + 0.005667 + 0.005625 + 0.004958 + 0.004541 + 0.006417 + 0.006209 + 0.006 + 0.005208 + 0.004875 + 0.004833 + 0.0045 + 0.003875 + 0.004167 + 0.004125 + 0.004625 + 0.004208 + 0.004542 + 0.004209 + 0.004584 + 0.004541 + 0.004208 + 0.004209 + 0.006042 + 0.004833 + 0.005375 + 0.004667 + 0.00625 + 0.005708 + 0.004792 + 0.004833 + 0.004792 + 0.00575 + 0.007958 + 0.005042 + 0.006125 + 0.007 + 0.006667 + 0.007042 + 0.006 + 0.007209 + 0.006083 + 0.005542 + 0.004292 + 0.003958 + 0.003666 + 0.003584 + 0.003458 + 0.003583 + 0.003458 + 0.003583 + 0.003375 + 0.003416 + 0.004 + 0.004416 + 0.005917 + 0.004542 + 0.004541 + 0.004458 + 0.004417 + 0.004208 + 0.004416 + 0.004333 + 0.004375 + 0.004167 + 0.003917 + 0.003583 + 0.003792 + 0.003709 + 0.003625 + 0.003458 + 0.003875 + 0.003417 + 0.00375 + 0.003542 + 0.003666 + 0.003708 + 0.003583 + 0.003958 + 0.004083 + 0.003792 + 0.003791 + 0.00325 + 0.003584 + 0.003375 + 0.003458 + 0.003583 + 0.003292 + 0.003292 + 0.003458 + 0.00325 + 0.003833 + 0.00375 + 0.003542 + 0.003542 + 0.004167 + 0.003708 + 0.003583 + 0.003542 + 0.003791 + 0.008625 + 0.005666 + 0.00525 + 0.005542 + 0.005625 + 0.00525 + 0.004416 + 0.005458 + 0.004583 + 0.004667 + 0.004916 + 0.005042 + 0.004292 + 0.004125 + 0.003584 + 0.004125 + 0.003875 + 0.003834 + 0.00375 + 0.003458 + 0.003625 + 0.003333 + 0.003708 + 0.009833 + 0.003958 + 0.003667 + 0.004041 + 0.006292 + 0.00675 + 0.006125 + 0.006334 + 0.006375 + 0.005333 + 0.00525 + 0.007333 + 0.005708 + 0.005959 + 0.006 + 0.006084 + 0.006041 + 0.005 + 0.007208 + 0.006333 + 0.005333 + 0.005083 + 0.004709 + 0.004584 + 0.004166 + 0.003958 + 0.004334 + 0.004125 + 0.004792 + 0.004 + 0.003709 + 0.00375 + 0.004125 + 0.004208 + 0.004333 + 0.004083 + 0.004083 + 0.004167 + 0.004291 + 0.004375 + 0.004625 + 0.004375 + 0.004458 + 0.00425 + 0.004209 + 0.004416 + 0.004459 + 0.004333 + 0.0045 + 0.005 + 0.004667 + 0.004791 + 0.004375 + 0.004708 + 0.004125 + 0.004208 + 0.004042 + 0.004541 + 0.00425 + 0.004 + 0.004875 + 0.004375 + 0.004166 + 0.004333 + 0.004542 + 0.004209 + 0.004125 + 0.003792 + 0.00475 + 0.00475 + 0.004792 + 0.004625 + 0.004458 + 0.004667 + 0.004708 + 0.004333 + 0.004542 + 0.00425 + 0.00525 + 0.005375 + 0.009166 + 0.006167 + 0.006084 + 0.006083 + 0.006 + 0.005042 + 0.004833 + 0.00425 + 0.004625 + 0.00425 + 0.004625 + 0.005084 + 0.004166 + 0.004459 + 0.004459 + 0.005334 + 0.005666 + 0.004917 + 0.005625 + 0.005625 + 0.005542 + 0.00475 + 0.004666 + 0.005167 + 0.004958 + 0.00575 + 0.005167 + 0.0065 + 0.006834 + 0.006458 + 0.006417 + 0.006084 + 0.004959 + 0.005166 + 0.006083 + 0.006708 + 0.006666 + 0.006625 + 0.006917 + 0.005625 + 0.004167 + 0.004458 + 0.004791 + 0.005 + 0.004166 + 0.005042 + 0.004709 + 0.006334 + 0.005291 + 0.004 + 0.003875 + 0.005042 + 0.00525 + 0.004584 + 0.00425 + 0.004417 + 0.004459 + 0.003834 + 0.004417 + 0.004333 + 0.004667 + 0.00425 + 0.004375 + 0.004416 + 0.004417 + 0.004084 + 0.004334 + 0.004875 + 0.004084 + 0.004 + 0.004542 + 0.004416 + 0.00425 + 0.004208 + 0.004167 + 0.004166 + 0.010042 + 0.004041 + 0.004333 + 0.004333 + 0.004083 + 0.00475 + 0.005 + 0.005083 + 0.005167 + 0.005583 + 0.005 + 0.005167 + 0.005125 + 0.006 + 0.006583 + 0.005541 + 0.005375 + 0.004875 + 0.004584 + 0.00425 + 0.0045 + 0.004708 + 0.00525 + 0.004875 + 0.00525 + 0.008958 + 0.006041 + 0.006 + 0.006333 + 0.00525 + 0.00475 + 0.004458 + 0.004333 + 0.004333 + 0.0045 + 0.004542 + 0.004666 + 0.004375 + 0.014333 + 0.0045 + 0.004417 + 0.0045 + 0.006167 + 0.004958 + 0.00525 + 0.0055 + 0.005042 + 0.005459 + 0.00425 + 0.005333 + 0.005125 + 0.005375 + 0.005666 + 0.008417 + 0.006209 + 0.005917 + 0.005291 + 0.006333 + 0.005792 + 0.005125 + 0.005833 + 0.005792 + 0.006292 + 0.005958 + 0.006042 + 0.004541 + 0.004167 + 0.004416 + 0.004084 + 0.003959 + 0.004084 + 0.00425 + 0.004208 + 0.004375 + 0.004083 + 0.00375 + 0.004333 + 0.003875 + 0.004125 + 0.003834 + 0.003959 + 0.004375 + 0.003917 + 0.003791 + 0.003959 + 0.003959 + 0.004292 + 0.003792 + 0.004167 + 0.003958 + 0.003917 + 0.003958 + 0.004375 + 0.004 + 0.004208 + 0.004167 + 0.004 + 0.004125 + 0.004041 + 0.003875 + 0.004 + 0.003542 + 0.003875 + 0.003791 + 0.003875 + 0.004333 + 0.004042 + 0.004166 + 0.004042 + 0.004 + 0.003958 + 0.004167 + 0.004209 + 0.004416 + 0.004458 + 0.004541 + 0.003959 + 0.008625 + 0.005083 + 0.00525 + 0.004583 + 0.00525 + 0.006 + 0.005334 + 0.00475 + 0.008125 + 0.006041 + 0.006542 + 0.006084 + 0.005917 + 0.005666 + 0.005 + 0.00375 + 0.003916 + 0.003833 + 0.003792 + 0.004375 + 0.004583 + 0.005542 + 0.005458 + 0.005083 + 0.005125 + 0.004791 + 0.00525 + 0.00425 + 0.004458 + 0.004625 + 0.00475 + 0.005416 + 0.006042 + 0.006542 + 0.005791 + 0.006375 + 0.006083 + 0.00625 + 0.006042 + 0.004791 + 0.003542 + 0.005 + 0.004958 + 0.004959 + 0.006084 + 0.006 + 0.005666 + 0.006208 + 0.005334 + 0.004167 + 0.004083 + 0.004167 + 0.004292 + 0.003708 + 0.003833 + 0.004334 + 0.004334 + 0.003959 + 0.003542 + 0.004 + 0.003791 + 0.003833 + 0.004125 + 0.004417 + 0.004041 + 0.004334 + 0.004375 + 0.003917 + 0.004458 + 0.004166 + 0.004 + 0.003958 + 0.004083 + 0.00425 + 0.00425 + 0.004459 + 0.004084 + 0.003959 + 0.004125 + 0.003959 + 0.004084 + 0.004042 + 0.00375 + 0.003666 + 0.00375 + 0.003542 + 0.003625 + 0.003542 + 0.003708 + 0.003958 + 0.004333 + 0.004375 + 0.004041 + 0.003708 + 0.004208 + 0.003917 + 0.004875 + 0.004292 + 0.004334 + 0.004 + 0.0045 + 0.003792 + 0.004084 + 0.008584 + 0.005334 + 0.005917 + 0.006166 + 0.005708 + 0.00575 + 0.005 + 0.006041 + 0.006333 + 0.006125 + 0.005 + 0.005125 + 0.00475 + 0.003708 + 0.003875 + 0.003875 + 0.003917 + 0.003917 + 0.004375 + 0.0045 + 0.004125 + 0.004375 + 0.004459 + 0.005167 + 0.005041 + 0.004958 + 0.004875 + 0.004916 + 0.005334 + 0.005459 + 0.005375 + 0.00525 + 0.005667 + 0.006125 + 0.006125 + 0.006167 + 0.006084 + 0.0045 + 0.006084 + 0.00475 + 0.005666 + 0.006083 + 0.005833 + 0.005667 + 0.005875 + 0.004834 + 0.004 + 0.004166 + 0.003584 + 0.003834 + 0.003833 + 0.004 + 0.003917 + 0.004 + 0.004333 + 0.003583 + 0.003917 + 0.004458 + 0.003708 + 0.003958 + 0.00375 + 0.003542 + 0.004041 + 0.003833 + 0.004084 + 0.003833 + 0.00425 + 0.004208 + 0.004083 + 0.00375 + 0.004083 + 0.003917 + 0.004417 + 0.004333 + 0.003875 + 0.013875 + 0.003791 + 0.004042 + 0.003917 + 0.003834 + 0.004 + 0.003833 + 0.003583 + 0.003958 + 0.003667 + 0.003666 + 0.004084 + 0.003875 + 0.003959 + 0.0045 + 0.003875 + 0.0045 + 0.003667 + 0.004292 + 0.004417 + 0.0045 + 0.00425 + 0.004625 + 0.004375 + 0.004375 + 0.005708 + 0.005125 + 0.005083 + 0.006458 + 0.0055 + 0.005917 + 0.004958 + 0.00475 + 0.00725 + 0.006417 + 0.005041 + 0.00425 + 0.004041 + 0.00425 + 0.004 + 0.003709 + 0.004334 + 0.003792 + 0.003833 + 0.004125 + 0.004458 + 0.006541 + 0.006292 + 0.005667 + 0.005625 + 0.006042 + 0.005583 + 0.005167 + 0.006083 + 0.062 + 0.059375 + 0.008458 + 0.007125 + 0.006375 + 0.006458 + 0.009292 + 0.0075 + 0.006459 + 0.007542 + 0.051333 + 0.009167 + 0.00575 + 0.005209 + 0.009541 + 0.006458 + 0.005791 + 0.005042 + 0.004625 + 0.004167 + 0.005209 + 0.004833 + 0.005083 + 0.006208 + 0.005166 + 0.005 + 0.004625 + 0.004917 + 0.005375 + 0.004042 + 0.004417 + 0.004125 + 0.005333 + 0.005458 + 0.005125 + 0.005166 + 0.004958 + 0.006209 + 0.005791 + 0.005375 + 0.005666 + 0.00575 + 0.004833 + 0.004541 + 0.004958 + 0.005333 + 0.005666 + 0.006208 + 0.0125 + 0.024625 + 0.00625 + 0.006208 + 0.005333 + 0.006125 + 0.005625 + 0.017042 + 0.008334 + 0.005125 + 0.004834 + 0.004666 + 0.00525 + 0.005375 + 0.005208 + 0.005792 + 0.006208 + 0.005541 + 0.005167 + 0.004625 + 0.005292 + 0.005583 + 0.00625 + 0.005958 + 0.00575 + 0.0055 + 0.0065 + 0.00925 + 0.010792 + 0.009709 + 0.008792 + 0.009041 + 0.007833 + 0.007 + 0.006917 + 0.008875 + 0.009667 + 0.008709 + 0.0075 + 0.007084 + 0.005584 + 0.007 + 0.00575 + 0.00725 + 0.007875 + 0.006959 + 0.006667 + 0.005958 + 0.007334 + 0.006584 + 0.007084 + 0.005959 + 0.004792 + 0.006625 + 0.008041 + 0.007458 + 0.007375 + 0.007708 + 0.00675 + 0.007458 + 0.007042 + 0.009417 + 0.008166 + 0.007208 + 0.006792 + 0.006667 + 0.006791 + 0.007042 + 0.007166 + 0.006292 + 0.006625 + 0.005875 + 0.006208 + 0.006333 + 0.00625 + 0.006916 + 0.006625 + 0.005875 + 0.006042 + 0.004917 + 0.00575 + 0.005125 + 0.004792 + 0.004958 + 0.004708 + 0.005583 + 0.005917 + 0.032958 + 0.007 + 0.0055 + 0.004667 + 0.005209 + 0.0045 + 0.004917 + 0.00475 + 0.004458 + 0.004167 + 0.004583 + 0.004333 + 0.004458 + 0.004542 + 0.004959 + 0.004542 + 0.005166 + 0.005042 + 0.00525 + 0.0045 + 0.004667 + 0.004375 + 0.004667 + 0.004875 + 0.005334 + 0.004667 + 0.004875 + 0.004625 + 0.004958 + 0.005417 + 0.005541 + 0.00425 + 0.004541 + 0.004542 + 0.00475 + 0.004625 + 0.004708 + 0.004875 + 0.004625 + 0.005583 + 0.007708 + 0.006667 + 0.006375 + 0.00525 + 0.005459 + 0.00575 + 0.008375 + 0.004833 + 0.00425 + 0.006083 + 0.005834 + 0.005334 + 0.005 + 0.005208 + 0.004916 + 0.005208 + 0.005917 + 0.005417 + 0.004375 + 0.005083 + 0.004875 + 0.005083 + 0.005167 + 0.004709 + 0.004791 + 0.004958 + 0.00525 + 0.0075 + 0.007125 + 0.006417 + 0.005166 + 0.005833 + 0.005583 + 0.00525 + 0.004666 + 0.007458 + 0.007333 + 0.006458 + 0.00725 + 0.007 + 0.006708 + 0.007417 + 0.0065 + 0.005542 + 0.005417 + 0.005541 + 0.005667 + 0.004875 + 0.004666 + 0.004958 + 0.005458 + 0.004792 + 0.005209 + 0.005708 + 0.006125 + 0.005833 + 0.005583 + 0.005583 + 0.005834 + 0.005625 + 0.005417 + 0.004875 + 0.004875 + 0.004916 + 0.004417 + 0.004209 + 0.0045 + 0.004375 + 0.005292 + 0.005709 + 0.00525 + 0.004625 + 0.004666 + 0.00425 + 0.004417 + 0.004166 + 0.004334 + 0.00425 + 0.004375 + 0.00425 + 0.003958 + 0.004417 + 0.00425 + 0.004208 + 0.004208 + 0.004208 + 0.0045 + 0.00425 + 0.004 + 0.004167 + 0.004375 + 0.004666 + 0.004375 + 0.004667 + 0.004458 + 0.004416 + 0.005125 + 0.005333 + 0.00525 + 0.00425 + 0.005541 + 0.005708 + 0.008333 + 0.007833 + 0.007958 + 0.0065 + 0.006833 + 0.005833 + 0.005458 + 0.005917 + 0.005791 + 0.005583 + 0.005792 + 0.00525 + 0.005667 + 0.005291 + 0.006042 + 0.007458 + 0.005917 + 0.005958 + 0.005916 + 0.005625 + 0.005583 + 0.005708 + 0.006166 + 0.006042 + 0.005375 + 0.00525 + 0.005459 + 0.009334 + 0.006875 + 0.007 + 0.006667 + 0.00675 + 0.005875 + 0.007 + 0.006708 + 0.007125 + 0.006625 + 0.006375 + 0.006875 + 0.00675 + 0.005791 + 0.005084 + 0.004167 + 0.00425 + 0.004666 + 0.004375 + 0.004333 + 0.003917 + 0.005667 + 0.004792 + 0.004667 + 0.005209 + 0.005167 + 0.004667 + 0.004416 + 0.004292 + 0.004666 + 0.004292 + 0.004334 + 0.004291 + 0.004 + 0.004458 + 0.004417 + 0.004208 + 0.0045 + 0.004292 + 0.004459 + 0.004167 + 0.004583 + 0.004417 + 0.004375 + 0.004083 + 0.004459 + 0.004292 + 0.004292 + 0.004166 + 0.004042 + 0.00425 + 0.004125 + 0.004084 + 0.004375 + 0.004125 + 0.004209 + 0.004541 + 0.004209 + 0.004042 + 0.00425 + 0.004375 + 0.004375 + 0.004167 + 0.005209 + 0.00525 + 0.005583 + 0.004917 + 0.004959 + 0.005292 + 0.004708 + 0.004542 + 0.005209 + 0.005292 + 0.00525 + 0.006333 + 0.006666 + 0.006625 + 0.006 + 0.005334 + 0.004709 + 0.004541 + 0.004542 + 0.0045 + 0.004625 + 0.0045 + 0.004666 + 0.004542 + 0.004917 + 0.004083 + 0.005291 + 0.005042 + 0.005792 + 0.005375 + 0.005125 + 0.005208 + 0.004834 + 0.0045 + 0.003791 + 0.00525 + 0.005416 + 0.005042 + 0.006333 + 0.006708 + 0.006125 + 0.005666 + 0.004958 + 0.005583 + 0.005625 + 0.006084 + 0.006542 + 0.006333 + 0.005625 + 0.005416 + 0.00575 + 0.0045 + 0.003916 + 0.004375 + 0.003958 + 0.004875 + 0.00425 + 0.004 + 0.00475 + 0.003875 + 0.004 + 0.00475 + 0.00425 + 0.00425 + 0.004334 + 0.004125 + 0.004166 + 0.003958 + 0.003917 + 0.004041 + 0.003833 + 0.003959 + 0.004041 + 0.003833 + 0.004084 + 0.003792 + 0.004292 + 0.003875 + 0.004541 + 0.003833 + 0.003958 + 0.003875 + 0.004375 + 0.004084 + 0.003958 + 0.003875 + 0.003834 + 0.00425 + 0.004417 + 0.004083 + 0.004 + 0.003959 + 0.004125 + 0.004125 + 0.004167 + 0.004375 + 0.00375 + 0.003875 + 0.004292 + 0.004208 + 0.005208 + 0.004958 + 0.004958 + 0.00525 + 0.005167 + 0.004791 + 0.004917 + 0.00425 + 0.004791 + 0.005167 + 0.005292 + 0.006791 + 0.005833 + 0.006625 + 0.006 + 0.005709 + 0.005 + 0.0045 + 0.004167 + 0.004417 + 0.004125 + 0.004416 + 0.005875 + 0.005542 + 0.004709 + 0.004958 + 0.005208 + 0.00475 + 0.005 + 0.004334 + 0.00425 + 0.004125 + 0.004042 + 0.004458 + 0.004375 + 0.004917 + 0.005667 + 0.007 + 0.006291 + 0.00625 + 0.006208 + 0.005708 + 0.005167 + 0.004792 + 0.007 + 0.006584 + 0.006042 + 0.00575 + 0.006583 + 0.005708 + 0.006166 + 0.005083 + 0.0045 + 0.004292 + 0.004625 + 0.005209 + 0.004167 + 0.003917 + 0.00425 + 0.00425 + 0.003834 + 0.003875 + 0.004625 + 0.004375 + 0.004042 + 0.00425 + 0.003667 + 0.004416 + 0.004167 + 0.003958 + 0.003667 + 0.003709 + 0.003834 + 0.003708 + 0.003458 + 0.017458 + 0.003791 + 0.00375 + 0.004125 + 0.004041 + 0.003917 + 0.003958 + 0.003917 + 0.004084 + 0.00425 + 0.003916 + 0.004125 + 0.004 + 0.004166 + 0.00475 + 0.004667 + 0.004625 + 0.004667 + 0.004834 + 0.004833 + 0.004208 + 0.004041 + 0.004167 + 0.0035 + 0.004375 + 0.004542 + 0.004166 + 0.004167 + 0.004 + 0.003917 + 0.003834 + 0.004084 + 0.003959 + 0.005042 + 0.005083 + 0.006708 + 0.006 + 0.006292 + 0.006042 + 0.005916 + 0.005958 + 0.004208 + 0.004542 + 0.004209 + 0.008084 + 0.005333 + 0.005375 + 0.005 + 0.005291 + 0.005042 + 0.004167 + 0.004208 + 0.004042 + 0.00425 + 0.00375 + 0.004 + 0.0045 + 0.004041 + 0.004208 + 0.004333 + 0.005042 + 0.006542 + 0.006125 + 0.005917 + 0.005916 + 0.006334 + 0.0055 + 0.006792 + 0.005458 + 0.006625 + 0.014708 + 0.006833 + 0.006667 + 0.0065 + 0.00825 + 0.006125 + 0.00475 + 0.004459 + 0.003625 + 0.00425 + 0.004 + 0.003541 + 0.003875 + 0.00375 + 0.005041 + 0.00525 + 0.005083 + 0.0055 + 0.005542 + 0.004959 + 0.005667 + 0.004958 + 0.00475 + 0.004667 + 0.004458 + 0.003833 + 0.004083 + 0.00425 + 0.003875 + 0.003542 + 0.003667 + 0.004083 + 0.003875 + 0.004042 + 0.003875 + 0.004167 + 0.003875 + 0.004125 + 0.003916 + 0.004 + 0.00375 + 0.004042 + 0.003625 + 0.004125 + 0.004083 + 0.004375 + 0.004625 + 0.004917 + 0.004792 + 0.005708 + 0.004792 + 0.005042 + 0.005 + 0.005208 + 0.005125 + 0.005417 + 0.005625 + 0.005042 + 0.004208 + 0.003833 + 0.00425 + 0.004041 + 0.004042 + 0.004458 + 0.005125 + 0.004875 + 0.006791 + 0.006708 + 0.006166 + 0.006333 + 0.006292 + 0.005709 + 0.005667 + 0.005416 + 0.004834 + 0.004041 + 0.004375 + 0.00425 + 0.005459 + 0.005625 + 0.005292 + 0.00575 + 0.005542 + 0.005375 + 0.00575 + 0.005208 + 0.004833 + 0.004417 + 0.003875 + 0.004708 + 0.004666 + 0.005 + 0.009083 + 0.006542 + 0.006875 + 0.006333 + 0.006208 + 0.006333 + 0.005458 + 0.006458 + 0.006458 + 0.00675 + 0.00625 + 0.006542 + 0.006625 + 0.006625 + 0.005209 + 0.005083 + 0.004416 + 0.003584 + 0.004042 + 0.004167 + 0.003625 + 0.005083 + 0.004583 + 0.004542 + 0.004 + 0.004333 + 0.004333 + 0.004125 + 0.003875 + 0.003917 + 0.004416 + 0.004 + 0.0045 + 0.004334 + 0.004333 + 0.004 + 0.004083 + 0.004 + 0.003875 + 0.003625 + 0.003833 + 0.003917 + 0.004375 + 0.00375 + 0.003959 + 0.004459 + 0.004125 + 0.004208 + 0.004125 + 0.004167 + 0.003792 + 0.004208 + 0.003958 + 0.003958 + 0.00425 + 0.005166 + 0.00525 + 0.005334 + 0.005084 + 0.004792 + 0.005583 + 0.005042 + 0.00525 + 0.005708 + 0.004542 + 0.004542 + 0.003916 + 0.00625 + 0.005709 + 0.005708 + 0.0055 + 0.006416 + 0.007125 + 0.00625 + 0.005959 + 0.005417 + 0.006041 + 0.006875 + 0.006292 + 0.005958 + 0.005292 + 0.005667 + 0.005583 + 0.005375 + 0.005292 + 0.004416 + 0.004333 + 0.004208 + 0.004292 + 0.003916 + 0.00425 + 0.004458 + 0.004625 + 0.005458 + 0.006 + 0.0055 + 0.005417 + 0.005625 + 0.00525 + 0.006 + 0.006458 + 0.005792 + 0.005041 + 0.00625 + 0.006958 + 0.006375 + 0.005708 + 0.006708 + 0.006125 + 0.006584 + 0.006416 + 0.007292 + 0.006125 + 0.006667 + 0.006458 + 0.005208 + 0.005 + 0.004709 + 0.004917 + 0.005041 + 0.004833 + 0.004625 + 0.004209 + 0.004417 + 0.004292 + 0.004084 + 0.004125 + 0.004084 + 0.004291 + 0.004167 + 0.004166 + 0.004083 + 0.004042 + 0.003958 + 0.004208 + 0.004375 + 0.004417 + 0.004 + 0.004333 + 0.004292 + 0.003833 + 0.003834 + 0.003917 + 0.003709 + 0.003958 + 0.003666 + 0.003708 + 0.004084 + 0.003959 + 0.004041 + 0.004208 + 0.003875 + 0.004 + 0.003875 + 0.004 + 0.003917 + 0.00425 + 0.005 + 0.004209 + 0.00425 + 0.004084 + 0.005209 + 0.004709 + 0.005542 + 0.0055 + 0.005667 + 0.005958 + 0.005292 + 0.005416 + 0.005375 + 0.004417 + 0.007 + 0.006458 + 0.006541 + 0.006416 + 0.0065 + 0.006708 + 0.007958 + 0.00675 + 0.006375 + 0.005625 + 0.006208 + 0.005166 + 0.0055 + 0.005458 + 0.005375 + 0.005042 + 0.004375 + 0.004875 + 0.004667 + 0.0045 + 0.004333 + 0.005375 + 0.005375 + 0.005375 + 0.00525 + 0.005875 + 0.005459 + 0.005042 + 0.006375 + 0.006042 + 0.004708 + 0.005041 + 0.006208 + 0.006792 + 0.006167 + 0.005542 + 0.006541 + 0.0065 + 0.00625 + 0.007125 + 0.007084 + 0.006625 + 0.006542 + 0.00625 + 0.005417 + 0.005166 + 0.004833 + 0.004917 + 0.004083 + 0.003916 + 0.00375 + 0.004417 + 0.004167 + 0.00425 + 0.003709 + 0.003917 + 0.004083 + 0.004167 + 0.004334 + 0.004084 + 0.003917 + 0.004208 + 0.004208 + 0.003875 + 0.003833 + 0.004292 + 0.004 + 0.004208 + 0.004042 + 0.004209 + 0.003917 + 0.003875 + 0.003667 + 0.004 + 0.003917 + 0.004333 + 0.004208 + 0.003916 + 0.003875 + 0.00425 + 0.003959 + 0.004125 + 0.004083 + 0.003917 + 0.004 + 0.003958 + 0.00425 + 0.003958 + 0.003834 + 0.004167 + 0.00425 + 0.003625 + 0.004709 + 0.004625 + 0.004375 + 0.004541 + 0.004333 + 0.004542 + 0.004334 + 0.005667 + 0.005458 + 0.00675 + 0.006334 + 0.006125 + 0.00625 + 0.006208 + 0.007 + 0.006791 + 0.00625 + 0.005542 + 0.005042 + 0.0055 + 0.005292 + 0.005416 + 0.005542 + 0.004541 + 0.004417 + 0.004166 + 0.004042 + 0.0045 + 0.004708 + 0.005625 + 0.005958 + 0.005625 + 0.005083 + 0.007541 + 0.005542 + 0.005375 + 0.006292 + 0.00575 + 0.005 + 0.005542 + 0.007292 + 0.006209 + 0.011208 + 0.006667 + 0.005708 + 0.0065 + 0.006417 + 0.005458 + 0.006959 + 0.007167 + 0.0065 + 0.006666 + 0.005458 + 0.004792 + 0.005125 + 0.004833 + 0.004667 + 0.004083 + 0.003875 + 0.004208 + 0.004583 + 0.004167 + 0.003958 + 0.004583 + 0.004292 + 0.004208 + 0.004208 + 0.004125 + 0.004625 + 0.003958 + 0.004083 + 0.004167 + 0.004084 + 0.00425 + 0.00425 + 0.004125 + 0.003917 + 0.004833 + 0.005334 + 0.005792 + 0.020458 + 0.012125 + 0.00525 + 0.005291 + 0.004834 + 0.005167 + 0.005083 + 0.004084 + 0.003958 + 0.004041 + 0.00425 + 0.004625 + 0.004083 + 0.004166 + 0.003875 + 0.004 + 0.004167 + 0.004125 + 0.004167 + 0.004042 + 0.003916 + 0.004792 + 0.004791 + 0.004292 + 0.004208 + 0.0045 + 0.0045 + 0.004166 + 0.011708 + 0.014333 + 0.007291 + 0.006042 + 0.005542 + 0.005958 + 0.005708 + 0.006917 + 0.007458 + 0.006 + 0.0055 + 0.006 + 0.005417 + 0.005625 + 0.005416 + 0.00475 + 0.004791 + 0.004667 + 0.004916 + 0.004584 + 0.004542 + 0.004666 + 0.004292 + 0.004666 + 0.0045 + 0.006833 + 0.00525 + 0.005625 + 0.006375 + 0.006292 + 0.005291 + 0.00475 + 0.00525 + 0.006875 + 0.006292 + 0.015875 + 0.006 + 0.005167 + 0.005125 + 0.005416 + 0.007 + 0.007542 + 0.006792 + 0.006333 + 0.005333 + 0.004084 + 0.004417 + 0.004625 + 0.004625 + 0.004709 + 0.004709 + 0.005083 + 0.004833 + 0.004417 + 0.012208 + 0.004625 + 0.004833 + 0.004708 + 0.004667 + 0.004917 + 0.004541 + 0.005292 + 0.004709 + 0.004958 + 0.005708 + 0.004833 + 0.004916 + 0.004167 + 0.0045 + 0.00475 + 0.004417 + 0.004708 + 0.005042 + 0.004542 + 0.012333 + 0.004125 + 0.004375 + 0.004834 + 0.004792 + 0.005 + 0.0045 + 0.004542 + 0.004083 + 0.003916 + 0.004583 + 0.0045 + 0.004583 + 0.004625 + 0.004042 + 0.004958 + 0.004958 + 0.004541 + 0.00475 + 0.00425 + 0.00525 + 0.00425 + 0.004833 + 0.00475 + 0.012208 + 0.004375 + 0.005166 + 0.005708 + 0.00875 + 0.007 + 0.007084 + 0.007084 + 0.005792 + 0.0065 + 0.007708 + 0.006125 + 0.006125 + 0.005333 + 0.005708 + 0.005125 + 0.004625 + 0.004916 + 0.004417 + 0.004667 + 0.004083 + 0.004208 + 0.00425 + 0.004875 + 0.004708 + 0.004458 + 0.004292 + 0.004334 + 0.004625 + 0.004334 + 0.004292 + 0.006375 + 0.0065 + 0.006 + 0.006208 + 0.005958 + 0.005333 + 0.006084 + 0.005292 + 0.006209 + 0.006084 + 0.006042 + 0.005583 + 0.005083 + 0.005917 + 0.006542 + 0.005666 + 0.005292 + 0.004834 + 0.004625 + 0.003958 + 0.004625 + 0.013833 + 0.004 + 0.004667 + 0.007416 + 0.004292 + 0.003875 + 0.0045 + 0.003833 + 0.003792 + 0.003917 + 0.003958 + 0.003916 + 0.012875 + 0.00425 + 0.004166 + 0.003958 + 0.00425 + 0.004417 + 0.004 + 0.003917 + 0.004 + 0.004291 + 0.004459 + 0.00425 + 0.004333 + 0.004458 + 0.003834 + 0.003834 + 0.003916 + 0.003958 + 0.003792 + 0.003916 + 0.004417 + 0.003792 + 0.003916 + 0.00425 + 0.004167 + 0.004542 + 0.004041 + 0.004333 + 0.004334 + 0.003792 + 0.004625 + 0.0045 + 0.004792 + 0.004291 + 0.004292 + 0.004125 + 0.004209 + 0.00425 + 0.004292 + 0.004333 + 0.007458 + 0.006292 + 0.006167 + 0.005833 + 0.00575 + 0.00575 + 0.008792 + 0.006208 + 0.005833 + 0.005875 + 0.006625 + 0.005709 + 0.004625 + 0.004458 + 0.004542 + 0.004625 + 0.004667 + 0.004917 + 0.004875 + 0.004792 + 0.005666 + 0.0055 + 0.0055 + 0.005917 + 0.005458 + 0.005417 + 0.005375 + 0.008542 + 0.006541 + 0.006875 + 0.006875 + 0.0065 + 0.006167 + 0.008125 + 0.005834 + 0.006459 + 0.006709 + 0.006209 + 0.005791 + 0.005792 + 0.007958 + 0.006875 + 0.0065 + 0.00575 + 0.005042 + 0.005 + 0.00525 + 0.004834 + 0.004542 + 0.004333 + 0.004792 + 0.006292 + 0.0055 + 0.005584 + 0.0055 + 0.005375 + 0.00475 + 0.00525 + 0.005167 + 0.005541 + 0.005167 + 0.005209 + 0.004959 + 0.005584 + 0.005125 + 0.004625 + 0.005125 + 0.004833 + 0.004833 + 0.004375 + 0.004125 + 0.004041 + 0.003875 + 0.004375 + 0.004375 + 0.003792 + 0.003958 + 0.00475 + 0.004833 + 0.004208 + 0.003958 + 0.004125 + 0.004333 + 0.003958 + 0.004625 + 0.004708 + 0.004917 + 0.004458 + 0.004791 + 0.004667 + 0.004584 + 0.004625 + 0.00425 + 0.004458 + 0.004291 + 0.004167 + 0.004583 + 0.004292 + 0.004 + 0.003958 + 0.006458 + 0.006292 + 0.006208 + 0.005959 + 0.00575 + 0.005209 + 0.004417 + 0.006333 + 0.005208 + 0.005042 + 0.005125 + 0.004959 + 0.005291 + 0.004791 + 0.004292 + 0.004416 + 0.004417 + 0.004583 + 0.00425 + 0.004834 + 0.004792 + 0.004333 + 0.004292 + 0.004541 + 0.004542 + 0.004459 + 0.004542 + 0.007 + 0.009 + 0.006375 + 0.007459 + 0.007209 + 0.007792 + 0.00775 + 0.005708 + 0.0055 + 0.006417 + 0.006458 + 0.006333 + 0.006292 + 0.00725 + 0.006709 + 0.006292 + 0.005459 + 0.004709 + 0.005584 + 0.005209 + 0.00475 + 0.004417 + 0.004416 + 0.004917 + 0.005208 + 0.004458 + 0.004375 + 0.005041 + 0.005209 + 0.005125 + 0.004875 + 0.004792 + 0.005625 + 0.00525 + 0.0055 + 0.005375 + 0.005375 + 0.005417 + 0.0055 + 0.006 + 0.006625 + 0.005416 + 0.004958 + 0.005458 + 0.005583 + 0.005 + 0.005291 + 0.004958 + 0.005209 + 0.0055 + 0.005417 + 0.004708 + 0.005167 + 0.003958 + 0.004375 + 0.004208 + 0.005 + 0.004916 + 0.004542 + 0.0045 + 0.004584 + 0.004792 + 0.004333 + 0.004708 + 0.005125 + 0.005333 + 0.005917 + 0.005125 + 0.004625 + 0.004709 + 0.004834 + 0.00525 + 0.004625 + 0.004708 + 0.00775 + 0.007292 + 0.006375 + 0.007 + 0.0055 + 0.005834 + 0.00775 + 0.00525 + 0.005666 + 0.005083 + 0.004917 + 0.005 + 0.005125 + 0.005333 + 0.006208 + 0.02275 + 0.008125 + 0.005542 + 0.00525 + 0.004958 + 0.016458 + 0.005167 + 0.005542 + 0.005583 + 0.006 + 0.006375 + 0.008375 + 0.006541 + 0.006083 + 0.006834 + 0.006542 + 0.006958 + 0.008625 + 0.007708 + 0.007 + 0.007416 + 0.005834 + 0.00675 + 0.007083 + 0.008458 + 0.006625 + 0.005 + 0.004625 + 0.004584 + 0.004292 + 0.00575 + 0.005084 + 0.005125 + 0.004167 + 0.005083 + 0.005416 + 0.003958 + 0.009667 + 0.009666 + 0.00525 + 0.004375 + 0.004208 + 0.00425 + 0.00525 + 0.004584 + 0.005125 + 0.006209 + 0.012333 + 0.008083 + 0.00425 + 0.004375 + 0.004334 + 0.006833 + 0.00475 + 0.004375 + 0.004959 + 0.004334 + 0.004958 + 0.004541 + 0.005167 + 0.004834 + 0.0045 + 0.004084 + 0.004625 + 0.004334 + 0.004542 + 0.004583 + 0.004958 + 0.011291 + 0.005375 + 0.005333 + 0.006 + 0.006417 + 0.015958 + 0.00925 + 0.006291 + 0.008125 + 0.006 + 0.0065 + 0.006458 + 0.006625 + 0.004834 + 0.005 + 0.005167 + 0.006916 + 0.008208 + 0.007167 + 0.007209 + 0.007542 + 0.006417 + 0.006209 + 0.006125 + 0.00475 + 0.00575 + 0.008375 + 0.006625 + 0.006334 + 0.006042 + 0.012041 + 0.005084 + 0.005584 + 0.006 + 0.005625 + 0.0055 + 0.005 + 0.005125 + 0.005167 + 0.005041 + 0.004833 + 0.005083 + 0.005042 + 0.004958 + 0.008375 + 0.00775 + 0.006875 + 0.007584 + 0.007209 + 0.007959 + 0.008917 + 0.006125 + 0.006625 + 0.006667 + 0.006959 + 0.005625 + 0.005666 + 0.007334 + 0.007209 + 0.006542 + 0.005041 + 0.005167 + 0.005541 + 0.004542 + 0.004333 + 0.004708 + 0.00475 + 0.00475 + 0.004875 + 0.004041 + 0.004083 + 0.004875 + 0.004583 + 0.004542 + 0.00475 + 0.004333 + 0.004542 + 0.004666 + 0.004542 + 0.004417 + 0.004208 + 0.004333 + 0.0045 + 0.004667 + 0.004625 + 0.004541 + 0.004708 + 0.004375 + 0.004333 + 0.004583 + 0.004209 + 0.0045 + 0.004541 + 0.004209 + 0.004209 + 0.004291 + 0.004 + 0.004625 + 0.004541 + 0.004292 + 0.004708 + 0.004375 + 0.00425 + 0.010417 + 0.004583 + 0.004833 + 0.005583 + 0.005583 + 0.004917 + 0.005041 + 0.005084 + 0.004875 + 0.0045 + 0.004875 + 0.004333 + 0.004875 + 0.005167 + 0.008458 + 0.007125 + 0.006875 + 0.006875 + 0.006833 + 0.007 + 0.007625 + 0.013208 + 0.005916 + 0.006042 + 0.005625 + 0.004917 + 0.004917 + 0.005541 + 0.005084 + 0.005 + 0.004709 + 0.005375 + 0.005 + 0.005167 + 0.005833 + 0.006333 + 0.005875 + 0.006666 + 0.005583 + 0.006334 + 0.00525 + 0.008292 + 0.007208 + 0.007208 + 0.007375 + 0.006875 + 0.006959 + 0.011083 + 0.006375 + 0.00625 + 0.007 + 0.007042 + 0.0075 + 0.007167 + 0.007834 + 0.006958 + 0.007083 + 0.005708 + 0.0055 + 0.005625 + 0.005125 + 0.00525 + 0.005583 + 0.005541 + 0.004542 + 0.005334 + 0.0045 + 0.004584 + 0.004916 + 0.004209 + 0.005375 + 0.00625 + 0.005625 + 0.005416 + 0.005708 + 0.004958 + 0.0045 + 0.004875 + 0.005041 + 0.004667 + 0.004875 + 0.014667 + 0.006458 + 0.005084 + 0.004709 + 0.00475 + 0.004625 + 0.005541 + 0.005375 + 0.005208 + 0.005042 + 0.004084 + 0.004 + 0.004375 + 0.004459 + 0.00475 + 0.0045 + 0.005 + 0.005167 + 0.004958 + 0.004458 + 0.004708 + 0.004458 + 0.004375 + 0.0045 + 0.005208 + 0.004958 + 0.004458 + 0.004958 + 0.004625 + 0.004792 + 0.004416 + 0.004792 + 0.004584 + 0.010084 + 0.007 + 0.00675 + 0.006583 + 0.006375 + 0.005459 + 0.005542 + 0.006833 + 0.006291 + 0.004917 + 0.005917 + 0.005458 + 0.005209 + 0.004458 + 0.004583 + 0.004875 + 0.00475 + 0.005084 + 0.004667 + 0.00475 + 0.00475 + 0.004458 + 0.004834 + 0.004667 + 0.004666 + 0.005167 + 0.005083 + 0.007333 + 0.00675 + 0.006458 + 0.007 + 0.006375 + 0.005208 + 0.007041 + 0.007208 + 0.005583 + 0.006583 + 0.006292 + 0.005625 + 0.005542 + 0.005042 + 0.00675 + 0.006667 + 0.005125 + 0.005375 + 0.005334 + 0.0045 + 0.004375 + 0.004708 + 0.004167 + 0.004125 + 0.004458 + 0.005083 + 0.004084 + 0.00475 + 0.004625 + 0.004667 + 0.004834 + 0.004209 + 0.004333 + 0.00375 + 0.003834 + 0.004375 + 0.00425 + 0.004916 + 0.004709 + 0.00425 + 0.004375 + 0.004458 + 0.013666 + 0.00425 + 0.004542 + 0.004791 + 0.004917 + 0.004166 + 0.0045 + 0.005042 + 0.004208 + 0.004875 + 0.004542 + 0.004583 + 0.00475 + 0.005084 + 0.004792 + 0.004833 + 0.005083 + 0.005291 + 0.005167 + 0.005 + 0.005209 + 0.004875 + 0.005458 + 0.00575 + 0.005708 + 0.005541 + 0.005167 + 0.006375 + 0.00575 + 0.005541 + 0.005583 + 0.004959 + 0.006375 + 0.007042 + 0.007042 + 0.007 + 0.006584 + 0.006667 + 0.006417 + 0.006084 + 0.005625 + 0.006208 + 0.0065 + 0.007166 + 0.005417 + 0.005458 + 0.005708 + 0.006042 + 0.005208 + 0.005625 + 0.005375 + 0.005167 + 0.00575 + 0.005375 + 0.01225 + 0.004958 + 0.004167 + 0.004625 + 0.004792 + 0.006834 + 0.006459 + 0.006125 + 0.006458 + 0.006208 + 0.005292 + 0.005334 + 0.004542 + 0.006166 + 0.00625 + 0.006334 + 0.006375 + 0.006041 + 0.005333 + 0.006375 + 0.005125 + 0.004917 + 0.004792 + 0.005 + 0.005083 + 0.004 + 0.003875 + 0.005 + 0.0045 + 0.003792 + 0.003958 + 0.004333 + 0.004708 + 0.004042 + 0.004875 + 0.005 + 0.004416 + 0.004125 + 0.004166 + 0.003792 + 0.003667 + 0.003917 + 0.004583 + 0.004166 + 0.004084 + 0.004209 + 0.004333 + 0.004 + 0.004791 + 0.004667 + 0.004708 + 0.004708 + 0.00475 + 0.004292 + 0.003833 + 0.004333 + 0.004167 + 0.004041 + 0.003959 + 0.004166 + 0.004208 + 0.004167 + 0.004209 + 0.003917 + 0.004 + 0.00375 + 0.003708 + 0.003667 + 0.004208 + 0.005 + 0.004916 + 0.00475 + 0.005416 + 0.004792 + 0.005 + 0.004375 + 0.004333 + 0.004375 + 0.00475 + 0.005125 + 0.007208 + 0.005958 + 0.006042 + 0.006125 + 0.005917 + 0.0045 + 0.003834 + 0.004584 + 0.004292 + 0.004208 + 0.004833 + 0.004958 + 0.004792 + 0.004042 + 0.005625 + 0.005709 + 0.005 + 0.004708 + 0.005042 + 0.004792 + 0.00475 + 0.00425 + 0.004125 + 0.004459 + 0.004084 + 0.005334 + 0.00525 + 0.005167 + 0.006334 + 0.006125 + 0.006041 + 0.005958 + 0.005083 + 0.006125 + 0.004917 + 0.006458 + 0.006334 + 0.006209 + 0.006 + 0.006167 + 0.005084 + 0.004375 + 0.004334 + 0.004125 + 0.003833 + 0.003958 + 0.004458 + 0.003709 + 0.009875 + 0.003875 + 0.004292 + 0.003875 + 0.004208 + 0.004583 + 0.004 + 0.004 + 0.004041 + 0.003834 + 0.003834 + 0.004167 + 0.003833 + 0.003833 + 0.012 + 0.004125 + 0.003625 + 0.004209 + 0.004209 + 0.00425 + 0.003875 + 0.004167 + 0.004166 + 0.004417 + 0.003916 + 0.003959 + 0.003959 + 0.004208 + 0.004 + 0.004417 + 0.004708 + 0.00475 + 0.00475 + 0.005083 + 0.005417 + 0.005375 + 0.005 + 0.004833 + 0.010167 + 0.004917 + 0.004916 + 0.005292 + 0.006667 + 0.0075 + 0.005292 + 0.005334 + 0.005542 + 0.005458 + 0.005541 + 0.005542 + 0.006666 + 0.006625 + 0.0065 + 0.009083 + 0.006917 + 0.006375 + 0.006542 + 0.006708 + 0.005667 + 0.005584 + 0.005459 + 0.005167 + 0.004791 + 0.006292 + 0.006 + 0.005292 + 0.005584 + 0.006209 + 0.005291 + 0.0055 + 0.005292 + 0.005416 + 0.005208 + 0.005041 + 0.004458 + 0.0045 + 0.004083 + 0.004875 + 0.005375 + 0.010542 + 0.006667 + 0.005833 + 0.005834 + 0.006375 + 0.0045 + 0.004167 + 0.005291 + 0.006458 + 0.005625 + 0.005833 + 0.006083 + 0.006042 + 0.005292 + 0.003916 + 0.003875 + 0.004084 + 0.004 + 0.003792 + 0.004 + 0.003958 + 0.003792 + 0.004208 + 0.003458 + 0.003875 + 0.003667 + 0.004083 + 0.004375 + 0.004458 + 0.004916 + 0.004875 + 0.00425 + 0.00375 + 0.003833 + 0.004 + 0.004167 + 0.003958 + 0.003666 + 0.004125 + 0.007625 + 0.005042 + 0.00525 + 0.005666 + 0.005583 + 0.005208 + 0.004833 + 0.005 + 0.004708 + 0.005208 + 0.004958 + 0.004625 + 0.004958 + 0.0045 + 0.004625 + 0.0045 + 0.004375 + 0.005417 + 0.005291 + 0.004625 + 0.005083 + 0.004958 + 0.005125 + 0.00625 + 0.007291 + 0.005417 + 0.00525 + 0.004792 + 0.004334 + 0.004542 + 0.005375 + 0.004959 + 0.004875 + 0.006666 + 0.006958 + 0.007292 + 0.008792 + 0.00725 + 0.007167 + 0.006833 + 0.005583 + 0.004792 + 0.005084 + 0.004375 + 0.004625 + 0.0045 + 0.003875 + 0.004708 + 0.004625 + 0.004292 + 0.005625 + 0.005167 + 0.00525 + 0.005458 + 0.00525 + 0.005458 + 0.005291 + 0.005125 + 0.005458 + 0.005958 + 0.005209 + 0.004958 + 0.007375 + 0.007084 + 0.0065 + 0.006792 + 0.006333 + 0.005833 + 0.006709 + 0.007125 + 0.006958 + 0.006583 + 0.006417 + 0.006125 + 0.00625 + 0.005333 + 0.005083 + 0.004208 + 0.00375 + 0.004041 + 0.004 + 0.004 + 0.00425 + 0.009542 + 0.003792 + 0.003959 + 0.004167 + 0.004291 + 0.00425 + 0.004291 + 0.004958 + 0.005291 + 0.005833 + 0.005458 + 0.005875 + 0.004583 + 0.004583 + 0.004541 + 0.004667 + 0.005666 + 0.005542 + 0.005042 + 0.016625 + 0.061625 + 0.00875 + 0.005542 + 0.051333 + 0.0245 + 0.005959 + 0.005041 + 0.004583 + 0.005 + 0.00575 + 0.004834 + 0.004667 + 0.005041 + 0.00475 + 0.004625 + 0.004167 + 0.004667 + 0.004583 + 0.004916 + 0.005 + 0.005542 + 0.006375 + 0.005792 + 0.005542 + 0.006416 + 0.009625 + 0.005416 + 0.006 + 0.00575 + 0.033416 + 0.010625 + 0.007084 + 0.006042 + 0.007083 + 0.007208 + 0.010166 + 0.007708 + 0.006333 + 0.00575 + 0.005333 + 0.005125 + 0.004875 + 0.005042 + 0.005208 + 0.005083 + 0.005083 + 0.005083 + 0.005417 + 0.004792 + 0.004458 + 0.004875 + 0.004958 + 0.005291 + 0.006958 + 0.006916 + 0.00625 + 0.004958 + 0.006208 + 0.006125 + 0.00575 + 0.006375 + 0.005666 + 0.007042 + 0.017667 + 0.006084 + 0.006083 + 0.006333 + 0.006125 + 0.005834 + 0.010167 + 0.007208 + 0.006791 + 0.006292 + 0.005167 + 0.004959 + 0.005334 + 0.004959 + 0.004917 + 0.004041 + 0.004125 + 0.005458 + 0.00425 + 0.003917 + 0.004583 + 0.004834 + 0.004542 + 0.004416 + 0.0045 + 0.004667 + 0.005125 + 0.005083 + 0.005375 + 0.005291 + 0.004958 + 0.004833 + 0.004667 + 0.004458 + 0.004875 + 0.004875 + 0.004542 + 0.004791 + 0.005 + 0.004666 + 0.004583 + 0.00475 + 0.00425 + 0.004292 + 0.004333 + 0.00425 + 0.004625 + 0.005708 + 0.005583 + 0.006042 + 0.005875 + 0.0055 + 0.005458 + 0.005166 + 0.005375 + 0.00425 + 0.004125 + 0.004958 + 0.004709 + 0.004709 + 0.004667 + 0.004417 + 0.004583 + 0.004792 + 0.004625 + 0.006 + 0.005459 + 0.006792 + 0.006708 + 0.006916 + 0.007083 + 0.007042 + 0.01 + 0.007333 + 0.006458 + 0.006333 + 0.006166 + 0.007042 + 0.006167 + 0.006458 + 0.006583 + 0.00625 + 0.0065 + 0.004917 + 0.005417 + 0.005042 + 0.005708 + 0.006666 + 0.006708 + 0.006542 + 0.006459 + 0.006042 + 0.004916 + 0.005583 + 0.006333 + 0.006 + 0.007375 + 0.052167 + 0.021125 + 0.008791 + 0.00725 + 0.006125 + 0.006708 + 0.006291 + 0.006292 + 0.007625 + 0.006458 + 0.006167 + 0.006917 + 0.006125 + 0.004833 + 0.0075 + 0.004709 + 0.004834 + 0.004709 + 0.004416 + 0.008 + 0.011459 + 0.00425 + 0.003834 + 0.004166 + 0.005333 + 0.005417 + 0.0055 + 0.01225 + 0.00525 + 0.005333 + 0.005333 + 0.00525 + 0.005667 + 0.005417 + 0.005875 + 0.006625 + 0.006167 + 0.006833 + 0.006 + 0.006125 + 0.00625 + 0.005958 + 0.00525 + 0.007042 + 0.006125 + 0.006458 + 0.005875 + 0.00525 + 0.006125 + 0.005375 + 0.005 + 0.006625 + 0.00575 + 0.005959 + 0.004917 + 0.005833 + 0.004917 + 0.005417 + 0.005708 + 0.0055 + 0.006166 + 0.006209 + 0.005542 + 0.005875 + 0.005542 + 0.007958 + 0.007125 + 0.005875 + 0.005041 + 0.005042 + 0.013542 + 0.014541 + 0.00725 + 0.007041 + 0.008708 + 0.007708 + 0.007458 + 0.0065 + 0.005333 + 0.004333 + 0.004791 + 0.00475 + 0.004792 + 0.005084 + 0.005 + 0.004875 + 0.004542 + 0.005125 + 0.00475 + 0.00475 + 0.006792 + 0.005833 + 0.005792 + 0.005625 + 0.00575 + 0.005792 + 0.005208 + 0.005083 + 0.005917 + 0.005667 + 0.005083 + 0.007083 + 0.006333 + 0.006375 + 0.006209 + 0.005209 + 0.006084 + 0.005791 + 0.006417 + 0.007083 + 0.006333 + 0.005917 + 0.006125 + 0.005083 + 0.004625 + 0.00425 + 0.004542 + 0.004167 + 0.004625 + 0.004083 + 0.004041 + 0.013792 + 0.004666 + 0.004125 + 0.005209 + 0.005292 + 0.004875 + 0.004541 + 0.004209 + 0.0045 + 0.00475 + 0.004541 + 0.005042 + 0.004792 + 0.004625 + 0.004208 + 0.004708 + 0.005 + 0.005291 + 0.005083 + 0.00475 + 0.005708 + 0.006125 + 0.005709 + 0.005167 + 0.005041 + 0.004333 + 0.00475 + 0.004667 + 0.004792 + 0.004042 + 0.004667 + 0.005542 + 0.005625 + 0.005083 + 0.005542 + 0.005375 + 0.005917 + 0.005625 + 0.005333 + 0.005333 + 0.004333 + 0.004458 + 0.005625 + 0.006209 + 0.007416 + 0.006458 + 0.005667 + 0.005167 + 0.005583 + 0.006041 + 0.007083 + 0.00725 + 0.006959 + 0.006625 + 0.008208 + 0.007458 + 0.006875 + 0.004917 + 0.004542 + 0.00525 + 0.005042 + 0.004792 + 0.005041 + 0.005208 + 0.005125 + 0.006041 + 0.005042 + 0.004542 + 0.004542 + 0.004667 + 0.00625 + 0.006625 + 0.005625 + 0.005917 + 0.006084 + 0.006 + 0.006459 + 0.006959 + 0.006958 + 0.0065 + 0.0085 + 0.007291 + 0.007125 + 0.006541 + 0.006125 + 0.006916 + 0.00625 + 0.006583 + 0.006917 + 0.0065 + 0.006958 + 0.005875 + 0.004625 + 0.004459 + 0.0045 + 0.00425 + 0.0045 + 0.004333 + 0.004208 + 0.004917 + 0.005292 + 0.004208 + 0.003791 + 0.00425 + 0.004666 + 0.004208 + 0.003917 + 0.004167 + 0.004458 + 0.004458 + 0.004292 + 0.004416 + 0.004542 + 0.004083 + 0.004166 + 0.004167 + 0.004167 + 0.004541 + 0.004208 + 0.0045 + 0.006792 + 0.004084 + 0.004584 + 0.008667 + 0.004333 + 0.004041 + 0.00425 + 0.004333 + 0.004375 + 0.004292 + 0.00475 + 0.004458 + 0.004458 + 0.004167 + 0.004416 + 0.004375 + 0.004167 + 0.00425 + 0.004417 + 0.004166 + 0.004208 + 0.004333 + 0.004667 + 0.004375 + 0.004625 + 0.004625 + 0.004416 + 0.004708 + 0.005542 + 0.005459 + 0.006167 + 0.005875 + 0.005958 + 0.005708 + 0.005208 + 0.008 + 0.006292 + 0.005417 + 0.005458 + 0.005625 + 0.004625 + 0.004625 + 0.00475 + 0.00475 + 0.004209 + 0.004416 + 0.004333 + 0.003834 + 0.004458 + 0.004791 + 0.004792 + 0.00475 + 0.004542 + 0.00475 + 0.00475 + 0.004041 + 0.004292 + 0.006917 + 0.006208 + 0.00625 + 0.006542 + 0.006042 + 0.005083 + 0.008792 + 0.005417 + 0.006042 + 0.006417 + 0.006166 + 0.010583 + 0.005417 + 0.006583 + 0.006041 + 0.006333 + 0.005084 + 0.004709 + 0.004459 + 0.004292 + 0.004667 + 0.003916 + 0.004417 + 0.005584 + 0.005291 + 0.005667 + 0.005292 + 0.005708 + 0.005292 + 0.005375 + 0.005042 + 0.005458 + 0.00525 + 0.004667 + 0.005125 + 0.004958 + 0.00475 + 0.004291 + 0.004084 + 0.004583 + 0.004333 + 0.00525 + 0.005459 + 0.005209 + 0.005209 + 0.00475 + 0.004709 + 0.004333 + 0.004667 + 0.004917 + 0.005 + 0.005042 + 0.00425 + 0.004167 + 0.003834 + 0.00425 + 0.004292 + 0.005125 + 0.005416 + 0.005167 + 0.005042 + 0.005 + 0.004583 + 0.004583 + 0.004834 + 0.004584 + 0.004959 + 0.005542 + 0.005416 + 0.005375 + 0.005708 + 0.005708 + 0.005375 + 0.008459 + 0.00725 + 0.006666 + 0.006333 + 0.005459 + 0.00575 + 0.007792 + 0.006917 + 0.005167 + 0.004583 + 0.004834 + 0.004833 + 0.00425 + 0.004667 + 0.004375 + 0.004292 + 0.004459 + 0.004375 + 0.004625 + 0.004375 + 0.00475 + 0.004416 + 0.004083 + 0.008792 + 0.006084 + 0.005417 + 0.004833 + 0.006583 + 0.00725 + 0.006041 + 0.00625 + 0.005625 + 0.005875 + 0.00625 + 0.005625 + 0.006208 + 0.006292 + 0.006625 + 0.0065 + 0.0075 + 0.006584 + 0.006375 + 0.00675 + 0.005458 + 0.005042 + 0.005125 + 0.004875 + 0.004958 + 0.003959 + 0.004417 + 0.004416 + 0.004666 + 0.004542 + 0.003834 + 0.004291 + 0.003917 + 0.004166 + 0.004375 + 0.004 + 0.003959 + 0.004292 + 0.004375 + 0.004167 + 0.004209 + 0.004208 + 0.003875 + 0.004334 + 0.004041 + 0.004417 + 0.004208 + 0.004459 + 0.00425 + 0.004416 + 0.004125 + 0.004084 + 0.003959 + 0.004167 + 0.003666 + 0.003792 + 0.003792 + 0.004167 + 0.004416 + 0.004125 + 0.00425 + 0.004542 + 0.004166 + 0.004167 + 0.003959 + 0.004 + 0.004041 + 0.003959 + 0.004375 + 0.004375 + 0.004375 + 0.004084 + 0.004208 + 0.004541 + 0.004416 + 0.004917 + 0.005583 + 0.006416 + 0.00625 + 0.006458 + 0.0065 + 0.006208 + 0.008416 + 0.007 + 0.006583 + 0.005708 + 0.005167 + 0.006 + 0.00575 + 0.005542 + 0.004834 + 0.004542 + 0.004333 + 0.00475 + 0.004292 + 0.004416 + 0.004084 + 0.00475 + 0.005334 + 0.006417 + 0.006375 + 0.005959 + 0.006375 + 0.00525 + 0.006958 + 0.006292 + 0.0055 + 0.005083 + 0.006042 + 0.015667 + 0.006709 + 0.006459 + 0.005875 + 0.006625 + 0.005791 + 0.007083 + 0.007042 + 0.00625 + 0.006542 + 0.006333 + 0.005458 + 0.005291 + 0.005042 + 0.005542 + 0.006041 + 0.005625 + 0.00525 + 0.00575 + 0.005042 + 0.00525 + 0.004875 + 0.004625 + 0.004 + 0.004459 + 0.004166 + 0.0045 + 0.004 + 0.003875 + 0.004125 + 0.0045 + 0.003833 + 0.004 + 0.004041 + 0.00425 + 0.004209 + 0.004625 + 0.003959 + 0.0045 + 0.004125 + 0.004208 + 0.004375 + 0.004917 + 0.003708 + 0.003916 + 0.004042 + 0.003791 + 0.00375 + 0.003667 + 0.004166 + 0.004 + 0.004041 + 0.004125 + 0.004292 + 0.004625 + 0.00425 + 0.003959 + 0.004333 + 0.00425 + 0.00375 + 0.004542 + 0.004583 + 0.004625 + 0.004542 + 0.004375 + 0.006792 + 0.005917 + 0.005375 + 0.005667 + 0.006875 + 0.00675 + 0.006084 + 0.00625 + 0.006417 + 0.008583 + 0.006417 + 0.005584 + 0.004834 + 0.005333 + 0.00475 + 0.004958 + 0.005083 + 0.005583 + 0.0055 + 0.005584 + 0.005334 + 0.004667 + 0.004583 + 0.0045 + 0.004208 + 0.004583 + 0.004208 + 0.004375 + 0.004083 + 0.004333 + 0.006792 + 0.006959 + 0.006917 + 0.00625 + 0.006625 + 0.006625 + 0.007333 + 0.006417 + 0.005875 + 0.006333 + 0.006583 + 0.006292 + 0.006625 + 0.007333 + 0.006792 + 0.006459 + 0.005625 + 0.005167 + 0.005125 + 0.005167 + 0.005458 + 0.004875 + 0.004916 + 0.005875 + 0.004958 + 0.004083 + 0.003875 + 0.00425 + 0.003833 + 0.003875 + 0.004291 + 0.003959 + 0.004208 + 0.004333 + 0.004375 + 0.004083 + 0.003666 + 0.004417 + 0.004041 + 0.004167 + 0.004291 + 0.004042 + 0.004125 + 0.004417 + 0.004334 + 0.004625 + 0.004084 + 0.004291 + 0.004417 + 0.004125 + 0.003833 + 0.003833 + 0.003834 + 0.003834 + 0.003875 + 0.004042 + 0.003958 + 0.003833 + 0.004167 + 0.004166 + 0.004458 + 0.004292 + 0.004208 + 0.003833 + 0.004625 + 0.004792 + 0.004667 + 0.004083 + 0.004792 + 0.004417 + 0.004334 + 0.004417 + 0.0045 + 0.004208 + 0.00975 + 0.006792 + 0.006958 + 0.006583 + 0.006083 + 0.006541 + 0.00625 + 0.005666 + 0.004458 + 0.005458 + 0.00575 + 0.006 + 0.005334 + 0.005459 + 0.005209 + 0.005834 + 0.005459 + 0.005042 + 0.004333 + 0.004292 + 0.004542 + 0.004083 + 0.004208 + 0.00425 + 0.004583 + 0.004583 + 0.004333 + 0.005292 + 0.011583 + 0.007083 + 0.006416 + 0.007542 + 0.0065 + 0.006541 + 0.005917 + 0.007541 + 0.006792 + 0.006375 + 0.006417 + 0.006666 + 0.006375 + 0.007709 + 0.006333 + 0.005416 + 0.005375 + 0.005333 + 0.005417 + 0.005125 + 0.004791 + 0.005458 + 0.006084 + 0.00525 + 0.005291 + 0.004458 + 0.00625 + 0.005291 + 0.005208 + 0.00475 + 0.004042 + 0.004625 + 0.004125 + 0.004625 + 0.004291 + 0.004084 + 0.004041 + 0.004417 + 0.004 + 0.003917 + 0.004667 + 0.004708 + 0.004208 + 0.005125 + 0.004791 + 0.004625 + 0.004667 + 0.00425 + 0.004375 + 0.004 + 0.004125 + 0.004042 + 0.004375 + 0.004042 + 0.00425 + 0.004292 + 0.004375 + 0.005 + 0.004459 + 0.004792 + 0.004166 + 0.004667 + 0.00525 + 0.004417 + 0.004834 + 0.004959 + 0.005458 + 0.005625 + 0.005625 + 0.004625 + 0.011583 + 0.004583 + 0.006542 + 0.005875 + 0.007458 + 0.007208 + 0.007417 + 0.007834 + 0.006917 + 0.007375 + 0.01275 + 0.005 + 0.006667 + 0.006458 + 0.008709 + 0.019083 + 0.009833 + 0.005666 + 0.005125 + 0.005916 + 0.005458 + 0.004917 + 0.004875 + 0.005083 + 0.004958 + 0.00525 + 0.005458 + 0.004708 + 0.004708 + 0.00975 + 0.007792 + 0.00625 + 0.006416 + 0.006542 + 0.006625 + 0.005375 + 0.004333 + 0.006625 + 0.006292 + 0.007 + 0.013958 + 0.01575 + 0.006958 + 0.007875 + 0.007167 + 0.005083 + 0.00425 + 0.004167 + 0.005 + 0.005375 + 0.004625 + 0.004375 + 0.0055 + 0.004916 + 0.004083 + 0.005083 + 0.00425 + 0.003875 + 0.004 + 0.004666 + 0.004083 + 0.004334 + 0.00425 + 0.004125 + 0.004458 + 0.004333 + 0.004125 + 0.00475 + 0.004167 + 0.004166 + 0.004167 + 0.0045 + 0.004375 + 0.004 + 0.004375 + 0.004625 + 0.004042 + 0.004458 + 0.004042 + 0.004417 + 0.004084 + 0.003709 + 0.003792 + 0.004375 + 0.00425 + 0.004167 + 0.004041 + 0.004416 + 0.004167 + 0.004042 + 0.004041 + 0.004208 + 0.00425 + 0.004625 + 0.005 + 0.004583 + 0.004583 + 0.004208 + 0.004375 + 0.004542 + 0.004333 + 0.00425 + 0.004875 + 0.009458 + 0.006292 + 0.006166 + 0.006333 + 0.005875 + 0.0055 + 0.004916 + 0.005042 + 0.0035 + 0.004333 + 0.009667 + 0.005666 + 0.005 + 0.004916 + 0.005334 + 0.012125 + 0.005208 + 0.004667 + 0.005084 + 0.005541 + 0.005334 + 0.004458 + 0.005041 + 0.004417 + 0.004083 + 0.004708 + 0.005959 + 0.007875 + 0.00775 + 0.007041 + 0.007458 + 0.00725 + 0.006625 + 0.007583 + 0.006166 + 0.006833 + 0.006125 + 0.004958 + 0.005125 + 0.004958 + 0.006125 + 0.0075 + 0.005458 + 0.005208 + 0.005375 + 0.0055 + 0.005542 + 0.004916 + 0.005292 + 0.005084 + 0.005959 + 0.004959 + 0.005625 + 0.005375 + 0.005458 + 0.004875 + 0.00525 + 0.00425 + 0.004292 + 0.004584 + 0.004417 + 0.005 + 0.0045 + 0.00425 + 0.005042 + 0.00525 + 0.005166 + 0.005 + 0.0055 + 0.004875 + 0.005083 + 0.005375 + 0.005208 + 0.004542 + 0.004084 + 0.004167 + 0.004167 + 0.003917 + 0.004542 + 0.004083 + 0.003917 + 0.004167 + 0.004292 + 0.004292 + 0.004208 + 0.004084 + 0.003959 + 0.004417 + 0.004708 + 0.00475 + 0.00575 + 0.006333 + 0.005791 + 0.005208 + 0.004625 + 0.004833 + 0.005333 + 0.004916 + 0.004708 + 0.005417 + 0.006875 + 0.007417 + 0.008666 + 0.006916 + 0.005625 + 0.005833 + 0.005875 + 0.00475 + 0.004583 + 0.004375 + 0.004708 + 0.00475 + 0.004208 + 0.006041 + 0.005083 + 0.004667 + 0.004959 + 0.005209 + 0.004542 + 0.00475 + 0.004292 + 0.00425 + 0.005541 + 0.003875 + 0.0055 + 0.004792 + 0.006125 + 0.0055 + 0.005333 + 0.005333 + 0.0045 + 0.006 + 0.005792 + 0.005875 + 0.004417 + 0.005292 + 0.005041 + 0.004709 + 0.003959 + 0.005542 + 0.0055 + 0.005209 + 0.004542 + 0.004291 + 0.004125 + 0.003792 + 0.003458 + 0.003417 + 0.003041 + 0.00375 + 0.003625 + 0.0035 + 0.003375 + 0.003791 + 0.0035 + 0.003375 + 0.003333 + 0.003584 + 0.003333 + 0.003208 + 0.003167 + 0.003833 + 0.004 + 0.004167 + 0.003958 + 0.003083 + 0.00325 + 0.003208 + 0.003334 + 0.003667 + 0.003791 + 0.003458 + 0.003208 + 0.003333 + 0.003292 + 0.003584 + 0.004 + 0.0035 + 0.003083 + 0.003083 + 0.003209 + 0.003416 + 0.003625 + 0.003333 + 0.0035 + 0.0035 + 0.003292 + 0.003084 + 0.003417 + 0.003875 + 0.004083 + 0.004375 + 0.003958 + 0.005 + 0.004625 + 0.00425 + 0.004041 + 0.003625 + 0.003917 + 0.004416 + 0.00475 + 0.005584 + 0.005375 + 0.005125 + 0.005709 + 0.005791 + 0.0055 + 0.003792 + 0.003708 + 0.003542 + 0.00375 + 0.004 + 0.003667 + 0.004875 + 0.0045 + 0.004333 + 0.004292 + 0.005333 + 0.005833 + 0.005792 + 0.0045 + 0.004417 + 0.004666 + 0.004708 + 0.004583 + 0.004833 + 0.00425 + 0.005792 + 0.0055 + 0.006292 + 0.006625 + 0.006375 + 0.006084 + 0.006667 + 0.005041 + 0.005459 + 0.006667 + 0.007041 + 0.006584 + 0.006541 + 0.00575 + 0.005666 + 0.006458 + 0.005375 + 0.005084 + 0.005042 + 0.004916 + 0.004625 + 0.004375 + 0.004459 + 0.005167 + 0.005834 + 0.004792 + 0.005375 + 0.005584 + 0.00525 + 0.005583 + 0.005042 + 0.0045 + 0.004042 + 0.004666 + 0.004708 + 0.004708 + 0.005 + 0.005375 + 0.00525 + 0.005166 + 0.005209 + 0.00575 + 0.005792 + 0.00525 + 0.005584 + 0.004792 + 0.004833 + 0.004542 + 0.005542 + 0.005417 + 0.00525 + 0.00475 + 0.00575 + 0.005916 + 0.006417 + 0.006208 + 0.006583 + 0.004833 + 0.004708 + 0.004625 + 0.004583 + 0.004917 + 0.004584 + 0.005125 + 0.005375 + 0.005708 + 0.004875 + 0.006 + 0.005917 + 0.00575 + 0.005125 + 0.005417 + 0.004916 + 0.006083 + 0.00575 + 0.007333 + 0.006834 + 0.005542 + 0.004917 + 0.005583 + 0.005333 + 0.004834 + 0.004333 + 0.006708 + 0.006 + 0.005959 + 0.005709 + 0.006 + 0.005958 + 0.005916 + 0.006125 + 0.006 + 0.005666 + 0.0065 + 0.006666 + 0.005292 + 0.005333 + 0.005333 + 0.005459 + 0.005166 + 0.005709 + 0.0075 + 0.007292 + 0.0075 + 0.007834 + 0.006875 + 0.007 + 0.005708 + 0.006166 + 0.006875 + 0.006958 + 0.007125 + 0.006667 + 0.006917 + 0.007875 + 0.00675 + 0.005417 + 0.005542 + 0.005333 + 0.005791 + 0.005042 + 0.004667 + 0.00475 + 0.004458 + 0.005958 + 0.004875 + 0.005708 + 0.00725 + 0.006708 + 0.006208 + 0.005375 + 0.005625 + 0.005416 + 0.0055 + 0.005125 + 0.004791 + 0.005042 + 0.00525 + 0.004791 + 0.004709 + 0.005 + 0.005334 + 0.005334 + 0.005291 + 0.005292 + 0.005625 + 0.005125 + 0.005125 + 0.005625 + 0.006667 + 0.00625 + 0.006042 + 0.005917 + 0.005833 + 0.005916 + 0.006625 + 0.006417 + 0.006667 + 0.006167 + 0.005333 + 0.005583 + 0.005208 + 0.004917 + 0.004875 + 0.005292 + 0.005583 + 0.005666 + 0.005917 + 0.004917 + 0.00575 + 0.005625 + 0.00525 + 0.006 + 0.00725 + 0.072791 + 0.013167 + 0.007584 + 0.0065 + 0.00675 + 0.006125 + 0.0075 + 0.006166 + 0.005208 + 0.006208 + 0.005084 + 0.004667 + 0.00475 + 0.004333 + 0.007208 + 0.004709 + 0.005708 + 0.006 + 0.005834 + 0.005333 + 0.0055 + 0.005875 + 0.005583 + 0.005875 + 0.005166 + 0.004833 + 0.007375 + 0.007042 + 0.007542 + 0.007333 + 0.00725 + 0.006667 + 0.008 + 0.007083 + 0.005542 + 0.006584 + 0.0065 + 0.006833 + 0.006583 + 0.007 + 0.007375 + 0.006834 + 0.0055 + 0.005291 + 0.005583 + 0.005167 + 0.005125 + 0.004792 + 0.005 + 0.004042 + 0.005666 + 0.006542 + 0.005542 + 0.005666 + 0.005834 + 0.005625 + 0.005458 + 0.005042 + 0.0055 + 0.005917 + 0.006084 + 0.005125 + 0.006 + 0.005167 + 0.005083 + 0.005041 + 0.004959 + 0.00425 + 0.004084 + 0.004541 + 0.004708 + 0.004292 + 0.004792 + 0.00525 + 0.00525 + 0.004875 + 0.005458 + 0.005292 + 0.005625 + 0.006042 + 0.004959 + 0.006167 + 0.006541 + 0.006375 + 0.006625 + 0.005333 + 0.00575 + 0.006333 + 0.005333 + 0.00525 + 0.006166 + 0.005875 + 0.005875 + 0.005209 + 0.004375 + 0.005375 + 0.005333 + 0.007 + 0.005791 + 0.006875 + 0.008583 + 0.006916 + 0.007 + 0.009375 + 0.007709 + 0.00725 + 0.008917 + 0.006708 + 0.007 + 0.006875 + 0.006458 + 0.006125 + 0.005542 + 0.005708 + 0.005917 + 0.00575 + 0.00575 + 0.00575 + 0.005958 + 0.005917 + 0.006041 + 0.005709 + 0.005875 + 0.005417 + 0.005583 + 0.006709 + 0.009167 + 0.007208 + 0.006625 + 0.006625 + 0.007833 + 0.006833 + 0.007625 + 0.007209 + 0.005667 + 0.006333 + 0.005542 + 0.007709 + 0.006625 + 0.00625 + 0.010125 + 0.007042 + 0.006042 + 0.005667 + 0.004709 + 0.005084 + 0.004792 + 0.004542 + 0.00475 + 0.005459 + 0.0055 + 0.006625 + 0.006333 + 0.013208 + 0.006542 + 0.0085 + 0.005958 + 0.009292 + 0.006166 + 0.006167 + 0.00725 + 0.00625 + 0.006125 + 0.005459 + 0.005541 + 0.005125 + 0.004958 + 0.005042 + 0.006333 + 0.00625 + 0.006125 + 0.007209 + 0.008834 + 0.006542 + 0.007292 + 0.006916 + 0.006333 + 0.005458 + 0.004917 + 0.004917 + 0.004583 + 0.0045 + 0.004333 + 0.004666 + 0.004292 + 0.004708 + 0.004833 + 0.005042 + 0.005125 + 0.005583 + 0.00675 + 0.007209 + 0.006 + 0.005667 + 0.006084 + 0.00675 + 0.005125 + 0.004333 + 0.00475 + 0.00475 + 0.006042 + 0.01125 + 0.007917 + 0.006791 + 0.006792 + 0.006667 + 0.007083 + 0.006041 + 0.00525 + 0.004875 + 0.006875 + 0.006875 + 0.00575 + 0.005667 + 0.005667 + 0.005666 + 0.00575 + 0.005583 + 0.005583 + 0.005458 + 0.004875 + 0.004792 + 0.004708 + 0.004625 + 0.005125 + 0.005042 + 0.004375 + 0.005667 + 0.007666 + 0.007417 + 0.007 + 0.006792 + 0.007333 + 0.007 + 0.005708 + 0.007208 + 0.007208 + 0.007 + 0.0095 + 0.009083 + 0.00775 + 0.006834 + 0.00775 + 0.006042 + 0.005542 + 0.0055 + 0.005334 + 0.005833 + 0.005667 + 0.005875 + 0.005167 + 0.004416 + 0.004917 + 0.004166 + 0.004834 + 0.005 + 0.004792 + 0.004708 + 0.004625 + 0.004709 + 0.004792 + 0.004791 + 0.004333 + 0.004583 + 0.004917 + 0.004875 + 0.004375 + 0.004583 + 0.004459 + 0.00425 + 0.004416 + 0.004209 + 0.004542 + 0.004417 + 0.00475 + 0.005291 + 0.004334 + 0.00425 + 0.004459 + 0.004583 + 0.004958 + 0.004667 + 0.004917 + 0.00475 + 0.005 + 0.004458 + 0.004667 + 0.004791 + 0.00425 + 0.0045 + 0.005 + 0.005 + 0.005041 + 0.005125 + 0.004625 + 0.00475 + 0.00475 + 0.004917 + 0.005042 + 0.00475 + 0.005583 + 0.005459 + 0.00725 + 0.00675 + 0.00675 + 0.006833 + 0.007 + 0.007125 + 0.005792 + 0.005083 + 0.00425 + 0.004709 + 0.004292 + 0.005959 + 0.006167 + 0.00575 + 0.005709 + 0.005958 + 0.006125 + 0.005959 + 0.00575 + 0.005042 + 0.004917 + 0.005417 + 0.004791 + 0.004708 + 0.004792 + 0.00625 + 0.006 + 0.007167 + 0.00775 + 0.007459 + 0.007292 + 0.007 + 0.006792 + 0.006208 + 0.005958 + 0.007166 + 0.007459 + 0.007 + 0.021125 + 0.011084 + 0.006917 + 0.005917 + 0.00525 + 0.005167 + 0.004833 + 0.0045 + 0.00425 + 0.004166 + 0.004625 + 0.004959 + 0.004042 + 0.004291 + 0.004458 + 0.004542 + 0.0045 + 0.004458 + 0.004333 + 0.004417 + 0.004375 + 0.005167 + 0.00525 + 0.005208 + 0.005583 + 0.0055 + 0.005459 + 0.005208 + 0.005416 + 0.005125 + 0.005 + 0.005333 + 0.004916 + 0.004208 + 0.004292 + 0.004625 + 0.0045 + 0.004125 + 0.004375 + 0.004459 + 0.004292 + 0.004167 + 0.00425 + 0.005125 + 0.005167 + 0.005125 + 0.004084 + 0.004625 + 0.0045 + 0.004292 + 0.004333 + 0.00475 + 0.006584 + 0.005917 + 0.006375 + 0.006 + 0.005583 + 0.005458 + 0.005791 + 0.005792 + 0.004959 + 0.0065 + 0.006208 + 0.005625 + 0.006625 + 0.007208 + 0.006542 + 0.007042 + 0.005833 + 0.005542 + 0.006166 + 0.006042 + 0.004917 + 0.004333 + 0.0045 + 0.00475 + 0.0045 + 0.004542 + 0.006209 + 0.005792 + 0.005875 + 0.0055 + 0.005333 + 0.004917 + 0.004084 + 0.004125 + 0.004625 + 0.005125 + 0.005 + 0.006084 + 0.006708 + 0.006083 + 0.005958 + 0.006208 + 0.00575 + 0.004917 + 0.005666 + 0.005292 + 0.007 + 0.006125 + 0.005958 + 0.005958 + 0.005834 + 0.005042 + 0.005 + 0.004584 + 0.004209 + 0.004209 + 0.004125 + 0.003959 + 0.003541 + 0.004333 + 0.004667 + 0.003833 + 0.003791 + 0.004125 + 0.004291 + 0.004709 + 0.004084 + 0.003708 + 0.004833 + 0.004875 + 0.005167 + 0.004791 + 0.004375 + 0.00425 + 0.004166 + 0.004042 + 0.004583 + 0.004875 + 0.0055 + 0.004875 + 0.004625 + 0.003792 + 0.003916 + 0.004542 + 0.004792 + 0.005416 + 0.0055 + 0.005625 + 0.004834 + 0.004792 + 0.005041 + 0.005334 + 0.005292 + 0.012708 + 0.007875 + 0.005125 + 0.004542 + 0.004542 + 0.004208 + 0.005542 + 0.004875 + 0.005 + 0.004208 + 0.004709 + 0.004459 + 0.006375 + 0.00525 + 0.005541 + 0.005708 + 0.006916 + 0.006875 + 0.006917 + 0.006583 + 0.006542 + 0.009 + 0.007125 + 0.005916 + 0.005667 + 0.005625 + 0.005833 + 0.004583 + 0.004959 + 0.004167 + 0.004208 + 0.004583 + 0.004375 + 0.004333 + 0.004416 + 0.004333 + 0.004667 + 0.004708 + 0.004375 + 0.005167 + 0.005833 + 0.005833 + 0.004458 + 0.006042 + 0.006709 + 0.0065 + 0.00675 + 0.005542 + 0.006417 + 0.007 + 0.0055 + 0.006458 + 0.006625 + 0.006583 + 0.006417 + 0.007125 + 0.006959 + 0.006709 + 0.006208 + 0.005291 + 0.006083 + 0.00525 + 0.005041 + 0.004792 + 0.003792 + 0.004083 + 0.004625 + 0.003834 + 0.004042 + 0.004333 + 0.004333 + 0.004625 + 0.004208 + 0.00425 + 0.004166 + 0.004083 + 0.004333 + 0.003916 + 0.004209 + 0.004292 + 0.004208 + 0.004167 + 0.004166 + 0.004333 + 0.00375 + 0.004 + 0.003625 + 0.003916 + 0.004 + 0.004083 + 0.003917 + 0.004375 + 0.004042 + 0.004333 + 0.004834 + 0.005167 + 0.005333 + 0.004875 + 0.004916 + 0.005 + 0.005083 + 0.005083 + 0.004958 + 0.0045 + 0.004208 + 0.00425 + 0.00475 + 0.004166 + 0.004458 + 0.004042 + 0.004542 + 0.004458 + 0.004584 + 0.004125 + 0.006041 + 0.005167 + 0.006334 + 0.0065 + 0.006209 + 0.007083 + 0.006417 + 0.008542 + 0.00675 + 0.007542 + 0.005875 + 0.005709 + 0.005459 + 0.005584 + 0.005208 + 0.004375 + 0.00425 + 0.004334 + 0.004417 + 0.004333 + 0.004667 + 0.00475 + 0.00625 + 0.005541 + 0.006083 + 0.005667 + 0.00575 + 0.005708 + 0.005666 + 0.006292 + 0.005375 + 0.005042 + 0.005417 + 0.006333 + 0.006583 + 0.006166 + 0.005833 + 0.006375 + 0.006458 + 0.00675 + 0.007917 + 0.006584 + 0.006459 + 0.006709 + 0.006625 + 0.005542 + 0.005166 + 0.005083 + 0.004667 + 0.003833 + 0.00375 + 0.0045 + 0.004542 + 0.004042 + 0.003875 + 0.004125 + 0.004625 + 0.004291 + 0.004042 + 0.00425 + 0.003833 + 0.004375 + 0.0045 + 0.0045 + 0.003792 + 0.004333 + 0.004167 + 0.004417 + 0.005083 + 0.005458 + 0.004833 + 0.005375 + 0.005084 + 0.005167 + 0.005 + 0.004458 + 0.004625 + 0.004083 + 0.004458 + 0.003958 + 0.004083 + 0.004042 + 0.004334 + 0.005125 + 0.005375 + 0.005 + 0.004917 + 0.004791 + 0.004792 + 0.0045 + 0.004792 + 0.005083 + 0.005625 + 0.005834 + 0.00625 + 0.005167 + 0.004667 + 0.004833 + 0.006208 + 0.005792 + 0.005833 + 0.004875 + 0.005209 + 0.004667 + 0.005333 + 0.00475 + 0.006084 + 0.007084 + 0.00675 + 0.006125 + 0.005666 + 0.005417 + 0.005583 + 0.005083 + 0.005083 + 0.005459 + 0.00425 + 0.004625 + 0.005625 + 0.005958 + 0.005708 + 0.006167 + 0.005834 + 0.005792 + 0.005791 + 0.005625 + 0.004875 + 0.0045 + 0.004167 + 0.004875 + 0.006417 + 0.006417 + 0.007166 + 0.005917 + 0.006834 + 0.005917 + 0.009041 + 0.00575 + 0.008459 + 0.006916 + 0.006458 + 0.005833 + 0.005542 + 0.005084 + 0.006666 + 0.006584 + 0.006 + 0.005208 + 0.005042 + 0.005417 + 0.008666 + 0.005 + 0.004291 + 0.006083 + 0.00475 + 0.005167 + 0.005583 + 0.005125 + 0.005083 + 0.005333 + 0.005042 + 0.004708 + 0.004458 + 0.005334 + 0.004709 + 0.005084 + 0.0045 + 0.004667 + 0.004417 + 0.005 + 0.004667 + 0.00425 + 0.005625 + 0.004959 + 0.004875 + 0.004625 + 0.004417 + 0.004667 + 0.004666 + 0.00425 + 0.003917 + 0.00475 + 0.005084 + 0.004917 + 0.004791 + 0.004708 + 0.004875 + 0.004375 + 0.004541 + 0.004917 + 0.005292 + 0.005541 + 0.00475 + 0.004792 + 0.004834 + 0.005125 + 0.005375 + 0.005125 + 0.004708 + 0.004375 + 0.004167 + 0.004375 + 0.004958 + 0.006125 + 0.00525 + 0.007042 + 0.006625 + 0.006292 + 0.006791 + 0.006708 + 0.004958 + 0.005 + 0.004708 + 0.005208 + 0.004792 + 0.004709 + 0.004834 + 0.004917 + 0.003958 + 0.004958 + 0.006958 + 0.006125 + 0.005625 + 0.005584 + 0.005459 + 0.004834 + 0.004625 + 0.004458 + 0.004625 + 0.005375 + 0.005125 + 0.005209 + 0.006834 + 0.006625 + 0.006042 + 0.006084 + 0.006209 + 0.004625 + 0.005167 + 0.007 + 0.006333 + 0.006792 + 0.006375 + 0.006084 + 0.005334 + 0.004417 + 0.004416 + 0.004167 + 0.004333 + 0.003958 + 0.0045 + 0.003708 + 0.005292 + 0.00575 + 0.004959 + 0.0045 + 0.004625 + 0.004916 + 0.005459 + 0.004958 + 0.005542 + 0.005541 + 0.005208 + 0.005708 + 0.005375 + 0.00525 + 0.004666 + 0.004208 + 0.004417 + 0.005042 + 0.004041 + 0.004334 + 0.004459 + 0.004417 + 0.004125 + 0.004583 + 0.004917 + 0.004709 + 0.004 + 0.004125 + 0.004333 + 0.003875 + 0.003875 + 0.004583 + 0.004625 + 0.005375 + 0.005166 + 0.00525 + 0.005667 + 0.005542 + 0.004583 + 0.005208 + 0.005458 + 0.005041 + 0.004625 + 0.004416 + 0.006125 + 0.005916 + 0.0055 + 0.005625 + 0.010541 + 0.004709 + 0.005208 + 0.006 + 0.005459 + 0.005833 + 0.007083 + 0.006959 + 0.006458 + 0.005125 + 0.005084 + 0.004916 + 0.004459 + 0.004167 + 0.004875 + 0.005041 + 0.005333 + 0.005833 + 0.006042 + 0.005709 + 0.005834 + 0.005959 + 0.00575 + 0.006042 + 0.005958 + 0.004917 + 0.004834 + 0.004834 + 0.00575 + 0.005333 + 0.007583 + 0.006958 + 0.005833 + 0.0075 + 0.006875 + 0.006584 + 0.005125 + 0.004583 + 0.005584 + 0.004792 + 0.006917 + 0.006625 + 0.006333 + 0.006083 + 0.006 + 0.005916 + 0.005417 + 0.005042 + 0.004791 + 0.004708 + 0.005083 + 0.004708 + 0.004125 + 0.003833 + 0.003833 + 0.0035 + 0.004083 + 0.004292 + 0.004291 + 0.004292 + 0.003708 + 0.004041 + 0.003959 + 0.003667 + 0.004 + 0.003958 + 0.004083 + 0.004375 + 0.004167 + 0.004292 + 0.004291 + 0.004333 + 0.004125 + 0.0045 + 0.004 + 0.003959 + 0.004125 + 0.004375 + 0.003875 + 0.003917 + 0.003833 + 0.00375 + 0.004083 + 0.003916 + 0.004042 + 0.004083 + 0.003959 + 0.004209 + 0.00425 + 0.003917 + 0.004083 + 0.004125 + 0.004041 + 0.004084 + 0.004666 + 0.00575 + 0.005167 + 0.005375 + 0.005 + 0.004958 + 0.005125 + 0.00475 + 0.004083 + 0.004666 + 0.005417 + 0.005125 + 0.006125 + 0.005833 + 0.006458 + 0.005916 + 0.005875 + 0.005041 + 0.00425 + 0.004416 + 0.004083 + 0.004292 + 0.004209 + 0.005167 + 0.005042 + 0.005417 + 0.005458 + 0.005208 + 0.00525 + 0.004542 + 0.004125 + 0.004292 + 0.004416 + 0.004875 + 0.004875 + 0.00425 + 0.004917 + 0.0055 + 0.006208 + 0.006333 + 0.006583 + 0.006166 + 0.006 + 0.004417 + 0.005 + 0.006292 + 0.0065 + 0.006084 + 0.005792 + 0.005708 + 0.005458 + 0.00625 + 0.005541 + 0.004875 + 0.004958 + 0.004583 + 0.004708 + 0.004 + 0.004042 + 0.004167 + 0.003834 + 0.004084 + 0.00425 + 0.004542 + 0.004083 + 0.003792 + 0.004625 + 0.004292 + 0.004542 + 0.0045 + 0.004416 + 0.004584 + 0.004417 + 0.004583 + 0.004208 + 0.004875 + 0.00425 + 0.004459 + 0.004334 + 0.004542 + 0.004083 + 0.004792 + 0.004417 + 0.00425 + 0.005 + 0.004041 + 0.004792 + 0.003958 + 0.004 + 0.003583 + 0.004166 + 0.004459 + 0.004542 + 0.004208 + 0.003959 + 0.00425 + 0.004125 + 0.003958 + 0.004167 + 0.004166 + 0.004292 + 0.004625 + 0.004459 + 0.004375 + 0.004125 + 0.004375 + 0.004292 + 0.00425 + 0.004625 + 0.006125 + 0.006083 + 0.007041 + 0.006708 + 0.006292 + 0.006333 + 0.006416 + 0.00625 + 0.005541 + 0.005 + 0.004916 + 0.004292 + 0.004334 + 0.005459 + 0.005125 + 0.005167 + 0.00525 + 0.00525 + 0.005 + 0.004542 + 0.004166 + 0.004208 + 0.004333 + 0.004 + 0.004167 + 0.004375 + 0.004334 + 0.005209 + 0.005125 + 0.006042 + 0.006292 + 0.005666 + 0.005666 + 0.006 + 0.00525 + 0.004666 + 0.004959 + 0.0065 + 0.005959 + 0.005959 + 0.005667 + 0.005666 + 0.005417 + 0.004375 + 0.003958 + 0.004041 + 0.004167 + 0.004167 + 0.003958 + 0.004083 + 0.003875 + 0.003625 + 0.003958 + 0.003792 + 0.004167 + 0.004333 + 0.003708 + 0.003833 + 0.004125 + 0.003667 + 0.00375 + 0.004084 + 0.004208 + 0.004125 + 0.004375 + 0.003917 + 0.003958 + 0.004125 + 0.004083 + 0.004041 + 0.004458 + 0.004292 + 0.004083 + 0.003667 + 0.00425 + 0.004375 + 0.003833 + 0.0035 + 0.003541 + 0.003875 + 0.003708 + 0.003375 + 0.003792 + 0.003542 + 0.003667 + 0.004458 + 0.004 + 0.004375 + 0.003708 + 0.003958 + 0.004292 + 0.00375 + 0.004375 + 0.004375 + 0.00425 + 0.005375 + 0.004917 + 0.005292 + 0.005166 + 0.004708 + 0.004958 + 0.005417 + 0.004875 + 0.005125 + 0.0045 + 0.007042 + 0.005958 + 0.005708 + 0.005041 + 0.005 + 0.004875 + 0.004041 + 0.004333 + 0.00425 + 0.003916 + 0.004 + 0.005166 + 0.005041 + 0.005375 + 0.004208 + 0.004792 + 0.004417 + 0.003958 + 0.004166 + 0.006291 + 0.005542 + 0.005958 + 0.006375 + 0.006417 + 0.006417 + 0.006708 + 0.006583 + 0.0075 + 0.006083 + 0.00575 + 0.00525 + 0.006292 + 0.005084 + 0.004792 + 0.006 + 0.005666 + 0.005333 + 0.005833 + 0.005583 + 0.004417 + 0.003875 + 0.004042 + 0.004 + 0.003792 + 0.004375 + 0.0045 + 0.004291 + 0.0035 + 0.003625 + 0.004167 + 0.004166 + 0.003875 + 0.00375 + 0.003833 + 0.004292 + 0.004209 + 0.003834 + 0.003875 + 0.004125 + 0.003708 + 0.004042 + 0.003875 + 0.003667 + 0.004167 + 0.003709 + 0.004208 + 0.004167 + 0.004083 + 0.003875 + 0.003875 + 0.003791 + 0.004167 + 0.003584 + 0.004167 + 0.004 + 0.003666 + 0.003833 + 0.003542 + 0.003834 + 0.003792 + 0.003583 + 0.003666 + 0.003958 + 0.003833 + 0.003916 + 0.003667 + 0.00425 + 0.004375 + 0.004417 + 0.004125 + 0.004209 + 0.004209 + 0.004125 + 0.003959 + 0.004833 + 0.004166 + 0.006708 + 0.006083 + 0.006667 + 0.006 + 0.005875 + 0.005083 + 0.006041 + 0.005208 + 0.004875 + 0.004959 + 0.004917 + 0.005166 + 0.004375 + 0.003917 + 0.003916 + 0.0045 + 0.004167 + 0.003916 + 0.004458 + 0.004083 + 0.004083 + 0.004792 + 0.004084 + 0.004084 + 0.004084 + 0.004291 + 0.004084 + 0.006209 + 0.006417 + 0.006083 + 0.005792 + 0.005917 + 0.005416 + 0.00575 + 0.004833 + 0.005833 + 0.005958 + 0.006125 + 0.005292 + 0.004667 + 0.005917 + 0.005959 + 0.005625 + 0.005083 + 0.004875 + 0.00475 + 0.004166 + 0.00375 + 0.004125 + 0.004 + 0.004 + 0.0035 + 0.003959 + 0.003833 + 0.004458 + 0.003583 + 0.0035 + 0.003541 + 0.00375 + 0.003417 + 0.00375 + 0.004208 + 0.003917 + 0.003958 + 0.004083 + 0.004 + 0.00375 + 0.003792 + 0.003833 + 0.004 + 0.00375 + 0.004041 + 0.003792 + 0.004041 + 0.00375 + 0.004 + 0.003917 + 0.003959 + 0.003791 + 0.004167 + 0.003625 + 0.003833 + 0.00375 + 0.003542 + 0.003625 + 0.003834 + 0.003625 + 0.004125 + 0.00425 + 0.004 + 0.004542 + 0.004125 + 0.004292 + 0.003875 + 0.004333 + 0.003875 + 0.0045 + 0.004042 + 0.004042 + 0.004417 + 0.0065 + 0.006084 + 0.006584 + 0.005709 + 0.00575 + 0.006 + 0.005958 + 0.005791 + 0.004875 + 0.004791 + 0.004583 + 0.004833 + 0.004292 + 0.004416 + 0.004333 + 0.004625 + 0.004625 + 0.005834 + 0.005625 + 0.005542 + 0.005417 + 0.005041 + 0.004667 + 0.004583 + 0.00575 + 0.005459 + 0.007 + 0.007625 + 0.007125 + 0.006167 + 0.006166 + 0.0065 + 0.008125 + 0.007125 + 0.005917 + 0.006791 + 0.005458 + 0.005333 + 0.006042 + 0.006708 + 0.007041 + 0.006583 + 0.00725 + 0.005083 + 0.004958 + 0.005541 + 0.006083 + 0.005583 + 0.005791 + 0.005459 + 0.005 + 0.004292 + 0.004125 + 0.004333 + 0.004542 + 0.004875 + 0.004542 + 0.005292 + 0.005459 + 0.005416 + 0.005084 + 0.005209 + 0.00525 + 0.005 + 0.004792 + 0.004416 + 0.004417 + 0.004291 + 0.0045 + 0.004125 + 0.00425 + 0.004209 + 0.004 + 0.004125 + 0.004 + 0.004417 + 0.004041 + 0.003917 + 0.003791 + 0.003833 + 0.003709 + 0.00425 + 0.003792 + 0.003792 + 0.003708 + 0.00375 + 0.003791 + 0.004084 + 0.004708 + 0.005416 + 0.005833 + 0.005208 + 0.005542 + 0.004958 + 0.004917 + 0.004584 + 0.004542 + 0.004833 + 0.005917 + 0.005459 + 0.006459 + 0.005042 + 0.0055 + 0.00525 + 0.004875 + 0.006083 + 0.006917 + 0.006708 + 0.005667 + 0.004709 + 0.004958 + 0.004792 + 0.004333 + 0.004542 + 0.004584 + 0.004417 + 0.004417 + 0.005125 + 0.00475 + 0.004792 + 0.00625 + 0.005958 + 0.0055 + 0.005709 + 0.00525 + 0.006 + 0.006333 + 0.007125 + 0.006709 + 0.006958 + 0.006083 + 0.008208 + 0.007417 + 0.007292 + 0.006791 + 0.005083 + 0.005708 + 0.005917 + 0.007625 + 0.007375 + 0.007458 + 0.007083 + 0.006458 + 0.005625 + 0.005209 + 0.00475 + 0.005209 + 0.005042 + 0.005041 + 0.005042 + 0.005875 + 0.005292 + 0.005958 + 0.005917 + 0.0055 + 0.006125 + 0.005208 + 0.005083 + 0.005167 + 0.005625 + 0.00575 + 0.005833 + 0.005292 + 0.00525 + 0.00525 + 0.005292 + 0.005167 + 0.004541 + 0.005042 + 0.005625 + 0.005042 + 0.004833 + 0.004208 + 0.005209 + 0.00475 + 0.0055 + 0.004416 + 0.005667 + 0.005875 + 0.005917 + 0.033708 + 0.010042 + 0.005542 + 0.005292 + 0.005625 + 0.004375 + 0.004417 + 0.004833 + 0.004791 + 0.004292 + 0.004166 + 0.005084 + 0.006417 + 0.00475 + 0.004959 + 0.004875 + 0.0095 + 0.005583 + 0.007541 + 0.005792 + 0.00525 + 0.007792 + 0.006208 + 0.005875 + 0.005625 + 0.00775 + 0.006958 + 0.007458 + 0.005917 + 0.005541 + 0.006167 + 0.005958 + 0.005541 + 0.005167 + 0.004625 + 0.004667 + 0.004125 + 0.004833 + 0.004542 + 0.004708 + 0.004375 + 0.004958 + 0.006541 + 0.006208 + 0.005917 + 0.006042 + 0.005958 + 0.007125 + 0.006791 + 0.006167 + 0.005083 + 0.007542 + 0.007292 + 0.006666 + 0.006375 + 0.006125 + 0.006792 + 0.007458 + 0.006458 + 0.00725 + 0.006833 + 0.006583 + 0.00625 + 0.005792 + 0.005083 + 0.005208 + 0.0055 + 0.005625 + 0.0045 + 0.005083 + 0.005583 + 0.006291 + 0.005208 + 0.004167 + 0.00475 + 0.005375 + 0.004333 + 0.004125 + 0.004583 + 0.005 + 0.004 + 0.004459 + 0.004417 + 0.005 + 0.005375 + 0.00575 + 0.006167 + 0.005375 + 0.004708 + 0.005542 + 0.005334 + 0.0055 + 0.005167 + 0.004917 + 0.00475 + 0.005084 + 0.004667 + 0.004458 + 0.004875 + 0.004458 + 0.004333 + 0.004166 + 0.004 + 0.004375 + 0.00525 + 0.005417 + 0.005333 + 0.005584 + 0.005667 + 0.005 + 0.005375 + 0.005417 + 0.005416 + 0.006 + 0.006041 + 0.006292 + 0.005916 + 0.00475 + 0.005209 + 0.006667 + 0.006291 + 0.006625 + 0.006708 + 0.006541 + 0.007292 + 0.007375 + 0.008583 + 0.007583 + 0.01325 + 0.007041 + 0.005375 + 0.005166 + 0.005708 + 0.006833 + 0.006208 + 0.006083 + 0.005208 + 0.004], + :filter-tx-sparse-bounds + [0.005 + 0.003541 + 0.003125 + 0.003125 + 0.003875 + 0.003959 + 0.003791 + 0.004 + 0.003416 + 0.00325 + 0.003334 + 0.004167 + 0.00325 + 0.003417 + 0.00325 + 0.00325 + 0.0035 + 0.003208 + 0.002958 + 0.003583 + 0.003583 + 0.002959 + 0.003083 + 0.003333 + 0.003333 + 0.003292 + 0.003541 + 0.003417 + 0.003208 + 0.003083 + 0.003041 + 0.003792 + 0.003917 + 0.0035 + 0.004 + 0.003208 + 0.003083 + 0.004 + 0.005042 + 0.004542 + 0.004583 + 0.004208 + 0.004292 + 0.004084 + 0.004167 + 0.003541 + 0.007542 + 0.004708 + 0.004208 + 0.005875 + 0.00575 + 0.005042 + 0.00475 + 0.004458 + 0.003875 + 0.003375 + 0.003916 + 0.004292 + 0.004834 + 0.0045 + 0.004667 + 0.005375 + 0.005041 + 0.004875 + 0.007958 + 0.004917 + 0.004583 + 0.004375 + 0.004459 + 0.004292 + 0.004625 + 0.004125 + 0.004625 + 0.004584 + 0.004375 + 0.004042 + 0.0065 + 0.005875 + 0.005875 + 0.005083 + 0.005458 + 0.004416 + 0.0045 + 0.004625 + 0.005542 + 0.0055 + 0.005167 + 0.004875 + 0.004875 + 0.004208 + 0.003458 + 0.003792 + 0.004084 + 0.004375 + 0.003584 + 0.004375 + 0.004042 + 0.004916 + 0.004209 + 0.003959 + 0.004 + 0.004459 + 0.005042 + 0.005375 + 0.004875 + 0.004667 + 0.004709 + 0.004417 + 0.004333 + 0.004292 + 0.00425 + 0.004417 + 0.00425 + 0.004959 + 0.005042 + 0.005667 + 0.004625 + 0.004625 + 0.004917 + 0.004708 + 0.0045 + 0.0045 + 0.004416 + 0.003958 + 0.003875 + 0.004333 + 0.004208 + 0.003916 + 0.003959 + 0.004334 + 0.00375 + 0.003917 + 0.004291 + 0.003959 + 0.004083 + 0.003708 + 0.003875 + 0.00425 + 0.004291 + 0.004375 + 0.004542 + 0.004166 + 0.004458 + 0.003833 + 0.004084 + 0.005959 + 0.006084 + 0.006292 + 0.006708 + 0.006041 + 0.006833 + 0.006208 + 0.007417 + 0.006667 + 0.00525 + 0.005209 + 0.005625 + 0.00575 + 0.005583 + 0.005208 + 0.005292 + 0.004 + 0.00425 + 0.004166 + 0.004167 + 0.004375 + 0.004333 + 0.004208 + 0.004 + 0.004292 + 0.00525 + 0.005708 + 0.005541 + 0.006583 + 0.006208 + 0.006583 + 0.006292 + 0.006541 + 0.006292 + 0.006834 + 0.006542 + 0.005791 + 0.007 + 0.006667 + 0.006709 + 0.007375 + 0.006417 + 0.006667 + 0.006166 + 0.005625 + 0.004833 + 0.005125 + 0.005334 + 0.004542 + 0.00375 + 0.004 + 0.005 + 0.0055 + 0.003958 + 0.004041 + 0.004458 + 0.004125 + 0.004083 + 0.003958 + 0.004 + 0.00375 + 0.00375 + 0.00375 + 0.003791 + 0.003833 + 0.004334 + 0.004208 + 0.003667 + 0.004167 + 0.00375 + 0.003625 + 0.004208 + 0.003708 + 0.004084 + 0.00425 + 0.004333 + 0.004 + 0.003667 + 0.004417 + 0.004125 + 0.004334 + 0.00375 + 0.00425 + 0.004167 + 0.004042 + 0.004 + 0.004459 + 0.004 + 0.003792 + 0.00375 + 0.004375 + 0.004041 + 0.004125 + 0.004375 + 0.004458 + 0.004334 + 0.004125 + 0.00425 + 0.004334 + 0.00425 + 0.006166 + 0.005959 + 0.006666 + 0.006625 + 0.00625 + 0.00675 + 0.006125 + 0.007625 + 0.006541 + 0.006 + 0.00525 + 0.005666 + 0.0055 + 0.005584 + 0.005584 + 0.005875 + 0.00525 + 0.0045 + 0.003542 + 0.004209 + 0.004416 + 0.004375 + 0.004042 + 0.004125 + 0.004375 + 0.004416 + 0.005667 + 0.005667 + 0.005458 + 0.006625 + 0.006375 + 0.006167 + 0.006709 + 0.006541 + 0.006792 + 0.006625 + 0.005417 + 0.006167 + 0.006708 + 0.006375 + 0.006417 + 0.007375 + 0.006417 + 0.006292 + 0.006292 + 0.005209 + 0.004958 + 0.00525 + 0.005125 + 0.004791 + 0.004125 + 0.003791 + 0.004333 + 0.0045 + 0.003583 + 0.004166 + 0.004084 + 0.003959 + 0.004292 + 0.003958 + 0.003541 + 0.004 + 0.003625 + 0.003542 + 0.003708 + 0.003875 + 0.004208 + 0.003834 + 0.004166 + 0.004042 + 0.004041 + 0.004084 + 0.004 + 0.004125 + 0.003958 + 0.004167 + 0.003917 + 0.003917 + 0.004125 + 0.003916 + 0.004042 + 0.004208 + 0.004083 + 0.004042 + 0.003958 + 0.00425 + 0.004125 + 0.004166 + 0.003917 + 0.003959 + 0.003958 + 0.003833 + 0.004291 + 0.004083 + 0.004208 + 0.004375 + 0.004375 + 0.004459 + 0.004166 + 0.004167 + 0.00575 + 0.005542 + 0.006292 + 0.006958 + 0.007291 + 0.00675 + 0.006417 + 0.007667 + 0.007292 + 0.006459 + 0.005709 + 0.0055 + 0.00575 + 0.005333 + 0.005459 + 0.00525 + 0.005167 + 0.005334 + 0.005333 + 0.005291 + 0.004625 + 0.004833 + 0.005875 + 0.005875 + 0.006041 + 0.00575 + 0.0055 + 0.005584 + 0.00575 + 0.007084 + 0.006209 + 0.005958 + 0.005291 + 0.006958 + 0.00775 + 0.007167 + 0.00575 + 0.006375 + 0.006875 + 0.006583 + 0.007875 + 0.008166 + 0.007 + 0.007125 + 0.007834 + 0.005917 + 0.005458 + 0.004875 + 0.004917 + 0.005125 + 0.005042 + 0.0045 + 0.005375 + 0.006375 + 0.005583 + 0.022958 + 0.0165 + 0.008209 + 0.006584 + 0.005292 + 0.004583 + 0.004167 + 0.004709 + 0.004167 + 0.004583 + 0.005083 + 0.004625 + 0.004166 + 0.004375 + 0.0045 + 0.004583 + 0.005042 + 0.004042 + 0.004583 + 0.00475 + 0.005208 + 0.004084 + 0.004292 + 0.004417 + 0.00475 + 0.004459 + 0.004041 + 0.005208 + 0.004417 + 0.003958 + 0.004541 + 0.005125 + 0.003958 + 0.004625 + 0.004375 + 0.005166 + 0.005542 + 0.004709 + 0.006416 + 0.005292 + 0.004333 + 0.004792 + 0.007833 + 0.006666 + 0.005666 + 0.00575 + 0.005667 + 0.008917 + 0.00675 + 0.006333 + 0.005708 + 0.006667 + 0.007375 + 0.006792 + 0.0065 + 0.005417 + 0.004041 + 0.004875 + 0.004583 + 0.00475 + 0.0045 + 0.004125 + 0.004459 + 0.004166 + 0.005 + 0.006208 + 0.0055 + 0.004959 + 0.005334 + 0.005125 + 0.00475 + 0.004542 + 0.004625 + 0.004833 + 0.005416 + 0.00575 + 0.005708 + 0.006542 + 0.006542 + 0.006166 + 0.006917 + 0.006459 + 0.00475 + 0.005416 + 0.004834 + 0.006791 + 0.006416 + 0.005792 + 0.006375 + 0.006375 + 0.004375 + 0.004 + 0.0045 + 0.004042 + 0.003959 + 0.003667 + 0.004291 + 0.003792 + 0.0045 + 0.0045 + 0.004375 + 0.004084 + 0.004375 + 0.00425 + 0.004167 + 0.003875 + 0.003917 + 0.004125 + 0.003833 + 0.004042 + 0.003916 + 0.00425 + 0.004125 + 0.00425 + 0.004667 + 0.004625 + 0.004875 + 0.004583 + 0.004583 + 0.004208 + 0.004167 + 0.004459 + 0.004 + 0.004417 + 0.004459 + 0.004291 + 0.004667 + 0.004208 + 0.004416 + 0.00475 + 0.005 + 0.005334 + 0.005041 + 0.005375 + 0.005166 + 0.005208 + 0.004792 + 0.004667 + 0.005166 + 0.004209 + 0.004458 + 0.004083 + 0.0045 + 0.004625 + 0.005917 + 0.005917 + 0.005584 + 0.006042 + 0.006833 + 0.006084 + 0.005834 + 0.00575 + 0.005875 + 0.006916 + 0.006792 + 0.005875 + 0.005084 + 0.005041 + 0.004542 + 0.004209 + 0.004666 + 0.004584 + 0.0045 + 0.004333 + 0.004292 + 0.004209 + 0.004333 + 0.004 + 0.004333 + 0.00425 + 0.004167 + 0.006458 + 0.005042 + 0.005625 + 0.005792 + 0.005875 + 0.0055 + 0.005708 + 0.005708 + 0.0065 + 0.006791 + 0.005834 + 0.005 + 0.006375 + 0.005458 + 0.005333 + 0.006708 + 0.006458 + 0.006042 + 0.006 + 0.004834 + 0.0045 + 0.00375 + 0.004542 + 0.004041 + 0.003917 + 0.00425 + 0.004959 + 0.004208 + 0.003834 + 0.004333 + 0.004041 + 0.004542 + 0.004084 + 0.004125 + 0.003584 + 0.003958 + 0.004083 + 0.004542 + 0.003791 + 0.003542 + 0.003917 + 0.00375 + 0.003708 + 0.00375 + 0.003791 + 0.003917 + 0.004291 + 0.004208 + 0.004292 + 0.004333 + 0.004375 + 0.004 + 0.004583 + 0.003917 + 0.003958 + 0.003917 + 0.004208 + 0.003875 + 0.004208 + 0.004042 + 0.004166 + 0.003917 + 0.004208 + 0.004041 + 0.004334 + 0.003625 + 0.004 + 0.004584 + 0.004125 + 0.003958 + 0.004167 + 0.003917 + 0.004083 + 0.003833 + 0.004083 + 0.004292 + 0.004042 + 0.006834 + 0.006084 + 0.00625 + 0.005667 + 0.006083 + 0.005333 + 0.006333 + 0.00525 + 0.005 + 0.005459 + 0.005625 + 0.005125 + 0.004458 + 0.004375 + 0.004625 + 0.004125 + 0.004708 + 0.004333 + 0.004 + 0.003958 + 0.004666 + 0.004125 + 0.004375 + 0.004084 + 0.004375 + 0.004667 + 0.004667 + 0.006041 + 0.00625 + 0.006375 + 0.00625 + 0.006125 + 0.005625 + 0.006084 + 0.005084 + 0.006083 + 0.005834 + 0.006125 + 0.005792 + 0.005208 + 0.00725 + 0.007209 + 0.005916 + 0.005083 + 0.005209 + 0.00425 + 0.00375 + 0.004583 + 0.004125 + 0.004083 + 0.004334 + 0.004542 + 0.004875 + 0.003541 + 0.004667 + 0.004125 + 0.004791 + 0.004833 + 0.004292 + 0.004084 + 0.004708 + 0.004417 + 0.004542 + 0.004291 + 0.004334 + 0.0045 + 0.00375 + 0.003792 + 0.003708 + 0.00375 + 0.003917 + 0.004208 + 0.004042 + 0.004458 + 0.004208 + 0.004042 + 0.003833 + 0.004 + 0.00375 + 0.004375 + 0.004291 + 0.004334 + 0.004417 + 0.00475 + 0.004792 + 0.005375 + 0.005208 + 0.005125 + 0.005125 + 0.005291 + 0.005458 + 0.006292 + 0.005625 + 0.004666 + 0.004875 + 0.004625 + 0.00475 + 0.003875 + 0.004042 + 0.004042 + 0.007084 + 0.006125 + 0.00625 + 0.005708 + 0.005791 + 0.00525 + 0.005125 + 0.00625 + 0.005334 + 0.004875 + 0.005167 + 0.005291 + 0.005042 + 0.00425 + 0.004375 + 0.00425 + 0.004292 + 0.004167 + 0.004458 + 0.004667 + 0.00425 + 0.004084 + 0.004334 + 0.003875 + 0.004208 + 0.0045 + 0.0045 + 0.007875 + 0.005958 + 0.006375 + 0.006125 + 0.006125 + 0.005042 + 0.006625 + 0.005375 + 0.004833 + 0.006125 + 0.00625 + 0.005625 + 0.005292 + 0.00475 + 0.006959 + 0.006375 + 0.004459 + 0.004375 + 0.004542 + 0.00475 + 0.003959 + 0.003583 + 0.004 + 0.004125 + 0.003666 + 0.003917 + 0.004375 + 0.003875 + 0.004209 + 0.003917 + 0.004042 + 0.004167 + 0.004125 + 0.004417 + 0.004666 + 0.004958 + 0.0045 + 0.00425 + 0.003875 + 0.003584 + 0.003917 + 0.003833 + 0.004333 + 0.004 + 0.003875 + 0.004042 + 0.004458 + 0.003958 + 0.004125 + 0.004042 + 0.004042 + 0.004333 + 0.004542 + 0.0045 + 0.004708 + 0.004417 + 0.00475 + 0.00475 + 0.00475 + 0.0045 + 0.004333 + 0.003792 + 0.004083 + 0.004041 + 0.004167 + 0.004209 + 0.004416 + 0.004458 + 0.004167 + 0.003666 + 0.003833 + 0.003875 + 0.003833 + 0.003583 + 0.004917 + 0.0065 + 0.006042 + 0.005959 + 0.00625 + 0.006167 + 0.005083 + 0.004125 + 0.004083 + 0.004625 + 0.004417 + 0.004459 + 0.005458 + 0.005208 + 0.005458 + 0.009375 + 0.004959 + 0.004708 + 0.0045 + 0.004542 + 0.004375 + 0.004209 + 0.003625 + 0.004083 + 0.004083 + 0.004125 + 0.004792 + 0.00475 + 0.006667 + 0.00625 + 0.006042 + 0.006125 + 0.005875 + 0.004959 + 0.004292 + 0.006792 + 0.006 + 0.005917 + 0.00575 + 0.005625 + 0.005167 + 0.005459 + 0.005458 + 0.004333 + 0.004208 + 0.00425 + 0.0045 + 0.00425 + 0.003875 + 0.00425 + 0.004667 + 0.00425 + 0.003667 + 0.003541 + 0.003708 + 0.004042 + 0.004292 + 0.003959 + 0.00425 + 0.004208 + 0.003959 + 0.003834 + 0.003959 + 0.004416 + 0.004167 + 0.003875 + 0.003625 + 0.003833 + 0.00375 + 0.00425 + 0.003667 + 0.003791 + 0.003459 + 0.003875 + 0.004041 + 0.003875 + 0.003792 + 0.004 + 0.003666 + 0.003833 + 0.003958 + 0.003959 + 0.003791 + 0.003875 + 0.003917 + 0.004125 + 0.004 + 0.003834 + 0.004 + 0.003708 + 0.004958 + 0.00425 + 0.004291 + 0.004375 + 0.004375 + 0.004083 + 0.004042 + 0.003666 + 0.004292 + 0.004125 + 0.004792 + 0.005416 + 0.0065 + 0.006041 + 0.005792 + 0.00625 + 0.005708 + 0.00475 + 0.00425 + 0.004542 + 0.004209 + 0.00425 + 0.004208 + 0.004333 + 0.004083 + 0.004375 + 0.003917 + 0.005416 + 0.005208 + 0.00525 + 0.005208 + 0.005458 + 0.005125 + 0.003833 + 0.004333 + 0.004125 + 0.00325 + 0.004875 + 0.005 + 0.00475 + 0.006083 + 0.006042 + 0.006291 + 0.005792 + 0.004916 + 0.005542 + 0.005 + 0.006125 + 0.006125 + 0.005708 + 0.005833 + 0.005583 + 0.005083 + 0.003875 + 0.00425 + 0.003791 + 0.003625 + 0.003792 + 0.003458 + 0.003625 + 0.004167 + 0.003833 + 0.00425 + 0.003958 + 0.004584 + 0.004166 + 0.003833 + 0.004083 + 0.004167 + 0.003958 + 0.003917 + 0.004375 + 0.004125 + 0.00475 + 0.004708 + 0.004417 + 0.004625 + 0.004208 + 0.003667 + 0.003708 + 0.003708 + 0.003833 + 0.003959 + 0.004 + 0.003875 + 0.00375 + 0.004167 + 0.004416 + 0.005208 + 0.005042 + 0.005375 + 0.005333 + 0.004875 + 0.00425 + 0.004416 + 0.004458 + 0.004459 + 0.004375 + 0.004792 + 0.004125 + 0.004625 + 0.004792 + 0.005041 + 0.0055 + 0.005125 + 0.005334 + 0.005209 + 0.00525 + 0.004959 + 0.004584 + 0.006541 + 0.006959 + 0.006834 + 0.008167 + 0.006083 + 0.006708 + 0.005916 + 0.006292 + 0.00475 + 0.005 + 0.004583 + 0.005416 + 0.004958 + 0.004833 + 0.004584 + 0.00475 + 0.005834 + 0.006459 + 0.005666 + 0.006042 + 0.005584 + 0.005209 + 0.005584 + 0.005458 + 0.005375 + 0.005041 + 0.0045 + 0.004875 + 0.005333 + 0.006458 + 0.008833 + 0.007333 + 0.007708 + 0.006917 + 0.006875 + 0.006583 + 0.006 + 0.005208 + 0.007375 + 0.006333 + 0.006917 + 0.006333 + 0.006333 + 0.005416 + 0.005125 + 0.004958 + 0.00525 + 0.00525 + 0.005584 + 0.006042 + 0.005458 + 0.005083 + 0.004833 + 0.00425 + 0.004791 + 0.005292 + 0.004792 + 0.005334 + 0.004375 + 0.005 + 0.005125 + 0.005167 + 0.005375 + 0.004792 + 0.004166 + 0.004584 + 0.004875 + 0.005833 + 0.005875 + 0.006 + 0.005875 + 0.005375 + 0.004625 + 0.005042 + 0.004375 + 0.00425 + 0.0045 + 0.004833 + 0.0045 + 0.004458 + 0.004541 + 0.004542 + 0.00475 + 0.004291 + 0.004625 + 0.004583 + 0.004958 + 0.006125 + 0.005209 + 0.005042 + 0.005333 + 0.006166 + 0.005625 + 0.005583 + 0.005875 + 0.005083 + 0.007 + 0.005833 + 0.005917 + 0.005334 + 0.006334 + 0.006542 + 0.006125 + 0.006 + 0.007083 + 0.005917 + 0.0065 + 0.0065 + 0.00575 + 0.005667 + 0.005417 + 0.005792 + 0.00525 + 0.005375 + 0.00475 + 0.0055 + 0.007791 + 0.0085 + 0.007375 + 0.00725 + 0.009375 + 0.007625 + 0.007291 + 0.007083 + 0.008708 + 0.006792 + 0.005875 + 0.006666 + 0.005959 + 0.006416 + 0.007 + 0.008 + 0.008959 + 0.006917 + 0.007916 + 0.005 + 0.006583 + 0.006208 + 0.007417 + 0.007542 + 0.007042 + 0.00625 + 0.005417 + 0.004625 + 0.004292 + 0.003792 + 0.004 + 0.00425 + 0.00475 + 0.003917 + 0.003708 + 0.00425 + 0.003834 + 0.004125 + 0.004542 + 0.004625 + 0.004708 + 0.005125 + 0.004417 + 0.004084 + 0.004083 + 0.00475 + 0.003708 + 0.004292 + 0.004167 + 0.00425 + 0.003834 + 0.004292 + 0.004125 + 0.004167 + 0.004083 + 0.00475 + 0.003916 + 0.004083 + 0.003667 + 0.004166 + 0.00375 + 0.003917 + 0.004625 + 0.004208 + 0.004041 + 0.003875 + 0.003958 + 0.003833 + 0.003917 + 0.003791 + 0.004 + 0.004375 + 0.004084 + 0.004167 + 0.00425 + 0.004416 + 0.004458 + 0.004125 + 0.004541 + 0.005584 + 0.005417 + 0.005 + 0.005292 + 0.005125 + 0.005375 + 0.005208 + 0.004583 + 0.005125 + 0.004958 + 0.0065 + 0.0065 + 0.006083 + 0.0055 + 0.00525 + 0.005042 + 0.005084 + 0.004291 + 0.004583 + 0.003959 + 0.004667 + 0.004 + 0.00475 + 0.004584 + 0.004584 + 0.004167 + 0.004333 + 0.004333 + 0.005417 + 0.005083 + 0.005042 + 0.005125 + 0.007834 + 0.006041 + 0.004875 + 0.005125 + 0.00525 + 0.005916 + 0.006292 + 0.005834 + 0.005292 + 0.006333 + 0.005416 + 0.005292 + 0.006 + 0.006625 + 0.006083 + 0.006125 + 0.005167 + 0.0045 + 0.004333 + 0.00425 + 0.004167 + 0.004083 + 0.004167 + 0.004916 + 0.004833 + 0.004 + 0.004125 + 0.004 + 0.004459 + 0.004209 + 0.003792 + 0.004 + 0.004291 + 0.00425 + 0.004167 + 0.004084 + 0.004458 + 0.004042 + 0.004125 + 0.00425 + 0.004375 + 0.003917 + 0.004083 + 0.00425 + 0.004458 + 0.004041 + 0.00425 + 0.003917 + 0.004 + 0.00375 + 0.003833 + 0.004208 + 0.003875 + 0.003959 + 0.004125 + 0.004334 + 0.004167 + 0.004 + 0.003875 + 0.004042 + 0.00375 + 0.003709 + 0.003875 + 0.004083 + 0.0045 + 0.004291 + 0.00425 + 0.004541 + 0.004042 + 0.004084 + 0.004334 + 0.003875 + 0.003875 + 0.004333 + 0.006584 + 0.006042 + 0.005666 + 0.005542 + 0.005834 + 0.005584 + 0.005417 + 0.00525 + 0.005292 + 0.005125 + 0.005042 + 0.004916 + 0.004834 + 0.004334 + 0.004666 + 0.004625 + 0.004375 + 0.004209 + 0.004625 + 0.004084 + 0.004459 + 0.004291 + 0.004334 + 0.004458 + 0.004458 + 0.004208 + 0.004 + 0.006417 + 0.006584 + 0.005792 + 0.006375 + 0.005958 + 0.004833 + 0.005375 + 0.005291 + 0.005833 + 0.005958 + 0.005959 + 0.005875 + 0.005083 + 0.005958 + 0.006084 + 0.006083 + 0.004833 + 0.004875 + 0.004625 + 0.004083 + 0.004541 + 0.003833 + 0.003958 + 0.004292 + 0.004 + 0.003625 + 0.003875 + 0.004083 + 0.003958 + 0.003708 + 0.004 + 0.003917 + 0.003959 + 0.003708 + 0.003958 + 0.004084 + 0.004209 + 0.004125 + 0.004125 + 0.004042 + 0.004083 + 0.004417 + 0.004 + 0.004542 + 0.003709 + 0.004084 + 0.003708 + 0.004125 + 0.003917 + 0.0035 + 0.003625 + 0.003875 + 0.004 + 0.003875 + 0.003667 + 0.003917 + 0.003958 + 0.004 + 0.004042 + 0.004459 + 0.004083 + 0.003792 + 0.004291 + 0.004334 + 0.004333 + 0.004542 + 0.0045 + 0.004542 + 0.004041 + 0.004334 + 0.00425 + 0.004292 + 0.004167 + 0.006291 + 0.006542 + 0.006166 + 0.005333 + 0.005875 + 0.006291 + 0.004709 + 0.005792 + 0.005792 + 0.004792 + 0.005125 + 0.00475 + 0.004875 + 0.004334 + 0.003959 + 0.004 + 0.004375 + 0.004459 + 0.004209 + 0.004042 + 0.004416 + 0.004125 + 0.004125 + 0.004167 + 0.004792 + 0.004541 + 0.004459 + 0.006333 + 0.006833 + 0.005791 + 0.005875 + 0.006167 + 0.004791 + 0.005875 + 0.005042 + 0.005625 + 0.005625 + 0.005875 + 0.005209 + 0.005 + 0.00625 + 0.006084 + 0.005875 + 0.005208 + 0.004625 + 0.004708 + 0.003791 + 0.004125 + 0.003875 + 0.004083 + 0.004125 + 0.003833 + 0.004333 + 0.003708 + 0.004208 + 0.003667 + 0.003541 + 0.004084 + 0.003834 + 0.003917 + 0.004166 + 0.004042 + 0.003958 + 0.003708 + 0.00425 + 0.003833 + 0.004083 + 0.003834 + 0.004042 + 0.003916 + 0.004167 + 0.003791 + 0.004417 + 0.004167 + 0.004208 + 0.004083 + 0.00375 + 0.00375 + 0.003958 + 0.003875 + 0.004416 + 0.004542 + 0.003833 + 0.00375 + 0.004125 + 0.004125 + 0.003792 + 0.003709 + 0.003625 + 0.004333 + 0.004167 + 0.004667 + 0.004708 + 0.005417 + 0.005375 + 0.005167 + 0.005208 + 0.00525 + 0.005 + 0.005708 + 0.00725 + 0.006667 + 0.005833 + 0.005625 + 0.005625 + 0.005292 + 0.006125 + 0.006125 + 0.005417 + 0.004583 + 0.005 + 0.004833 + 0.004458 + 0.003958 + 0.004084 + 0.004375 + 0.004459 + 0.004291 + 0.004209 + 0.004125 + 0.004458 + 0.004208 + 0.004292 + 0.004291 + 0.005167 + 0.005125 + 0.00525 + 0.006083 + 0.006083 + 0.005458 + 0.005042 + 0.004833 + 0.006 + 0.006416 + 0.005792 + 0.004917 + 0.0055 + 0.005459 + 0.005 + 0.005958 + 0.005875 + 0.00625 + 0.005917 + 0.005 + 0.004375 + 0.004125 + 0.004208 + 0.004083 + 0.004042 + 0.003791 + 0.004542 + 0.003542 + 0.004208 + 0.00375 + 0.004125 + 0.004125 + 0.003708 + 0.003625 + 0.003792 + 0.003584 + 0.003875 + 0.004291 + 0.004 + 0.004084 + 0.004125 + 0.003875 + 0.003917 + 0.003916 + 0.004334 + 0.003833 + 0.004708 + 0.004083 + 0.004125 + 0.003875 + 0.003958 + 0.004 + 0.003875 + 0.0035 + 0.003917 + 0.003875 + 0.003875 + 0.003542 + 0.003833 + 0.00375 + 0.003792 + 0.00425 + 0.00425 + 0.004084 + 0.003625 + 0.003958 + 0.004417 + 0.004125 + 0.004333 + 0.004417 + 0.004 + 0.004208 + 0.004084 + 0.00425 + 0.00425 + 0.004166 + 0.004084 + 0.006833 + 0.00575 + 0.006 + 0.005584 + 0.006 + 0.00525 + 0.005875 + 0.005875 + 0.004833 + 0.004958 + 0.004583 + 0.005125 + 0.004458 + 0.004167 + 0.004125 + 0.004041 + 0.004292 + 0.004541 + 0.004125 + 0.004334 + 0.004125 + 0.004083 + 0.004042 + 0.004417 + 0.00425 + 0.004583 + 0.006208 + 0.006625 + 0.006292 + 0.007 + 0.005959 + 0.008584 + 0.006708 + 0.006083 + 0.005083 + 0.005792 + 0.004792 + 0.005 + 0.005 + 0.005791 + 0.006042 + 0.005584 + 0.005542 + 0.004917 + 0.0045 + 0.004292 + 0.004333 + 0.003958 + 0.003875 + 0.005041 + 0.004667 + 0.004584 + 0.004125 + 0.00475 + 0.004625 + 0.004 + 0.0035 + 0.00375 + 0.004083 + 0.004125 + 0.003958 + 0.003875 + 0.004167 + 0.004167 + 0.004125 + 0.004041 + 0.004167 + 0.003958 + 0.003958 + 0.004542 + 0.00425 + 0.004292 + 0.00425 + 0.004209 + 0.004125 + 0.00425 + 0.003917 + 0.004083 + 0.00375 + 0.003833 + 0.003875 + 0.003417 + 0.004042 + 0.003709 + 0.004 + 0.004 + 0.004375 + 0.004333 + 0.003833 + 0.003958 + 0.004292 + 0.004292 + 0.004333 + 0.011333 + 0.004042 + 0.004209 + 0.004125 + 0.004041 + 0.004542 + 0.004708 + 0.006625 + 0.007625 + 0.006708 + 0.006125 + 0.0065 + 0.006709 + 0.005875 + 0.004458 + 0.004542 + 0.004625 + 0.006083 + 0.004958 + 0.003958 + 0.004042 + 0.00425 + 0.004166 + 0.004 + 0.003583 + 0.004417 + 0.004209 + 0.004334 + 0.004334 + 0.00425 + 0.004167 + 0.004041 + 0.004708 + 0.004 + 0.005458 + 0.00675 + 0.006583 + 0.006125 + 0.005917 + 0.005959 + 0.005375 + 0.00375 + 0.004833 + 0.006542 + 0.006417 + 0.00575 + 0.00625 + 0.0065 + 0.006292 + 0.007583 + 0.005542 + 0.005791 + 0.005417 + 0.004417 + 0.003833 + 0.004458 + 0.004542 + 0.004833 + 0.003583 + 0.004458 + 0.003458 + 0.0045 + 0.00375 + 0.003875 + 0.00425 + 0.003709 + 0.003625 + 0.003875 + 0.003917 + 0.003917 + 0.004125 + 0.004208 + 0.003833 + 0.004125 + 0.003958 + 0.003958 + 0.004 + 0.004292 + 0.004041 + 0.004292 + 0.004583 + 0.003708 + 0.004042 + 0.004083 + 0.004416 + 0.003958 + 0.003875 + 0.003958 + 0.003791 + 0.004 + 0.003625 + 0.003792 + 0.004083 + 0.003791 + 0.004084 + 0.003959 + 0.004375 + 0.00775 + 0.00475 + 0.004667 + 0.004125 + 0.004625 + 0.004375 + 0.004292 + 0.004375 + 0.004208 + 0.004208 + 0.005959 + 0.004709 + 0.007292 + 0.006791 + 0.006167 + 0.006834 + 0.006084 + 0.006542 + 0.00575 + 0.005041 + 0.005042 + 0.005542 + 0.005375 + 0.005459 + 0.00475 + 0.005167 + 0.005042 + 0.005333 + 0.005833 + 0.005583 + 0.004292 + 0.003958 + 0.004541 + 0.004542 + 0.004 + 0.004834 + 0.004333 + 0.005333 + 0.006459 + 0.00675 + 0.0065 + 0.006791 + 0.00625 + 0.006333 + 0.005541 + 0.006042 + 0.006834 + 0.00675 + 0.006833 + 0.006125 + 0.005875 + 0.005792 + 0.006834 + 0.005 + 0.004375 + 0.004292 + 0.004084 + 0.004334 + 0.004208 + 0.004166 + 0.004417 + 0.00375 + 0.003875 + 0.004167 + 0.00425 + 0.004875 + 0.004708 + 0.003875 + 0.003875 + 0.003708 + 0.004333 + 0.003792 + 0.004291 + 0.004375 + 0.004167 + 0.003834 + 0.004583 + 0.004041 + 0.00425 + 0.004 + 0.004208 + 0.004125 + 0.00425 + 0.004292 + 0.004334 + 0.004125 + 0.004167 + 0.004083 + 0.003917 + 0.00425 + 0.003875 + 0.004 + 0.003709 + 0.00425 + 0.003834 + 0.00375 + 0.003708 + 0.003542 + 0.003792 + 0.004 + 0.004042 + 0.005666 + 0.005667 + 0.005416 + 0.005208 + 0.005292 + 0.005542 + 0.005416 + 0.005417 + 0.005292 + 0.005375 + 0.005417 + 0.00525 + 0.0065 + 0.0065 + 0.006333 + 0.006666 + 0.006333 + 0.005333 + 0.005125 + 0.005625 + 0.00475 + 0.004166 + 0.00525 + 0.005667 + 0.005417 + 0.00525 + 0.005792 + 0.005375 + 0.0055 + 0.005958 + 0.005541 + 0.00525 + 0.005375 + 0.004541 + 0.004292 + 0.004542 + 0.005 + 0.005458 + 0.006583 + 0.007083 + 0.006208 + 0.006375 + 0.006375 + 0.0065 + 0.005417 + 0.007417 + 0.006625 + 0.006375 + 0.006708 + 0.006167 + 0.006334 + 0.006959 + 0.0055 + 0.005333 + 0.005208 + 0.005292 + 0.005083 + 0.004875 + 0.006042 + 0.004709 + 0.003959 + 0.004125 + 0.00475 + 0.004667 + 0.005041 + 0.00375 + 0.00375 + 0.004167 + 0.003958 + 0.003916 + 0.003875 + 0.004125 + 0.00425 + 0.005459 + 0.004708 + 0.004666 + 0.004083 + 0.003708 + 0.003667 + 0.003916 + 0.003458 + 0.00375 + 0.008792 + 0.003834 + 0.003541 + 0.003375 + 0.0115 + 0.004708 + 0.004583 + 0.004041 + 0.00425 + 0.0045 + 0.0045 + 0.004167 + 0.004334 + 0.004417 + 0.004375 + 0.004458 + 0.004583 + 0.004125 + 0.004625 + 0.003958 + 0.004167 + 0.005084 + 0.004792 + 0.005167 + 0.004834 + 0.004625 + 0.006292 + 0.00525 + 0.006125 + 0.005667 + 0.005042 + 0.003958 + 0.004291 + 0.005917 + 0.006291 + 0.004917 + 0.005 + 0.005 + 0.004875 + 0.004833 + 0.004708 + 0.004791 + 0.004541 + 0.004667 + 0.005334 + 0.004958 + 0.00475 + 0.00475 + 0.005375 + 0.005208 + 0.00525 + 0.00475 + 0.00375 + 0.003708 + 0.006 + 0.005958 + 0.00575 + 0.005459 + 0.005834 + 0.00575 + 0.005541 + 0.006292 + 0.004709 + 0.006459 + 0.005458 + 0.005667 + 0.005625 + 0.00525 + 0.006625 + 0.005666 + 0.00475 + 0.004666 + 0.00475 + 0.005083 + 0.004583 + 0.004458 + 0.004166 + 0.009 + 0.00475 + 0.004291 + 0.003709 + 0.004 + 0.004375 + 0.003792 + 0.003709 + 0.003792 + 0.003708 + 0.003583 + 0.003667 + 0.003541 + 0.003625 + 0.003541 + 0.003625 + 0.003833 + 0.003541 + 0.003375 + 0.00375 + 0.004125 + 0.003792 + 0.003958 + 0.003917 + 0.004125 + 0.004542 + 0.004416 + 0.004083 + 0.004917 + 0.011708 + 0.004417 + 0.014458 + 0.004 + 0.004292 + 0.004541 + 0.004709 + 0.005792 + 0.005917 + 0.006791 + 0.006167 + 0.005 + 0.006 + 0.006791 + 0.006 + 0.006125 + 0.006292 + 0.006125 + 0.005958 + 0.005541 + 0.005958 + 0.006166 + 0.0075 + 0.0065 + 0.009375 + 0.007292 + 0.006208 + 0.005958 + 0.005833 + 0.012417 + 0.005458 + 0.004542 + 0.005541 + 0.005584 + 0.006542 + 0.006833 + 0.006166 + 0.006208 + 0.005417 + 0.005792 + 0.007416 + 0.006416 + 0.006 + 0.005333 + 0.004792 + 0.005167 + 0.005041 + 0.005625 + 0.004666 + 0.006583 + 0.005625 + 0.006041 + 0.006667 + 0.007 + 0.006292 + 0.006625 + 0.005708 + 0.006208 + 0.007666 + 0.006875 + 0.0065 + 0.006417 + 0.006875 + 0.006958 + 0.006583 + 0.004875 + 0.006959 + 0.004209 + 0.004167 + 0.003833 + 0.004166 + 0.0045 + 0.004833 + 0.004042 + 0.003625 + 0.004 + 0.004292 + 0.003916 + 0.004292 + 0.003959 + 0.004166 + 0.003708 + 0.004209 + 0.004084 + 0.004333 + 0.004 + 0.004292 + 0.003875 + 0.00425 + 0.00425 + 0.004458 + 0.004791 + 0.004167 + 0.003833 + 0.004208 + 0.003917 + 0.003667 + 0.004 + 0.004125 + 0.004208 + 0.004 + 0.003666 + 0.004167 + 0.003834 + 0.004 + 0.003834 + 0.004125 + 0.004292 + 0.003917 + 0.0045 + 0.004208 + 0.004417 + 0.006583 + 0.0065 + 0.005833 + 0.007292 + 0.005584 + 0.005666 + 0.006292 + 0.006125 + 0.006042 + 0.005458 + 0.005666 + 0.006667 + 0.005791 + 0.009917 + 0.006834 + 0.006583 + 0.0065 + 0.005708 + 0.005125 + 0.004625 + 0.004458 + 0.004584 + 0.004625 + 0.005333 + 0.005583 + 0.005666 + 0.0055 + 0.005625 + 0.005459 + 0.005417 + 0.005333 + 0.005291 + 0.004917 + 0.005 + 0.004792 + 0.004958 + 0.004166 + 0.005083 + 0.005083 + 0.009292 + 0.007084 + 0.00675 + 0.006542 + 0.006334 + 0.005625 + 0.005833 + 0.00625 + 0.010542 + 0.006958 + 0.006542 + 0.006459 + 0.006625 + 0.006167 + 0.005166 + 0.005167 + 0.004666 + 0.004375 + 0.003875 + 0.00425 + 0.003791 + 0.003833 + 0.004042 + 0.003917 + 0.004125 + 0.004083 + 0.003917 + 0.004542 + 0.004458 + 0.003667 + 0.0035 + 0.004292 + 0.004625 + 0.003666 + 0.004208 + 0.004209 + 0.004459 + 0.004209 + 0.004167 + 0.00425 + 0.004042 + 0.003625 + 0.004084 + 0.004 + 0.00375 + 0.003916 + 0.004 + 0.003875 + 0.004166 + 0.003667 + 0.003792 + 0.004292 + 0.004291 + 0.004833 + 0.003792 + 0.003916 + 0.004292 + 0.003583 + 0.00375 + 0.003959 + 0.004084 + 0.003792 + 0.010916 + 0.00425 + 0.003875 + 0.004666 + 0.006792 + 0.005708 + 0.005292 + 0.005167 + 0.005291 + 0.006042 + 0.006625 + 0.0065 + 0.005834 + 0.006417 + 0.00675 + 0.00625 + 0.006208 + 0.005667 + 0.004916 + 0.005709 + 0.005334 + 0.004625 + 0.004167 + 0.004041 + 0.004375 + 0.00425 + 0.004416 + 0.004375 + 0.004125 + 0.004458 + 0.005459 + 0.00525 + 0.005417 + 0.004959 + 0.005084 + 0.006042 + 0.006458 + 0.006375 + 0.006125 + 0.005167 + 0.006166 + 0.006459 + 0.006083 + 0.006333 + 0.005333 + 0.006375 + 0.006208 + 0.007 + 0.006708 + 0.005958 + 0.006583 + 0.006458 + 0.005416 + 0.004958 + 0.004833 + 0.004708 + 0.0045 + 0.003958 + 0.003958 + 0.003417 + 0.004417 + 0.003875 + 0.003584 + 0.0035 + 0.004083 + 0.003833 + 0.004209 + 0.003584 + 0.003833 + 0.003958 + 0.003834 + 0.003875 + 0.003875 + 0.00375 + 0.004334 + 0.003875 + 0.003625 + 0.003875 + 0.003959 + 0.004042 + 0.003958 + 0.003792 + 0.004042 + 0.003541 + 0.003875 + 0.003833 + 0.003542 + 0.003833 + 0.003583 + 0.003792 + 0.003625 + 0.0035 + 0.003792 + 0.003792 + 0.003666 + 0.003959 + 0.004 + 0.004875 + 0.003583 + 0.003667 + 0.004208 + 0.004125 + 0.004375 + 0.0045 + 0.005 + 0.005417 + 0.005292 + 0.005375 + 0.005083 + 0.005541 + 0.005917 + 0.017042 + 0.013875 + 0.005458 + 0.007542 + 0.006459 + 0.005209 + 0.004625 + 0.004208 + 0.004042 + 0.004334 + 0.004416 + 0.004125 + 0.003917 + 0.004084 + 0.010833 + 0.004291 + 0.004292 + 0.00475 + 0.004834 + 0.004916 + 0.0065 + 0.00525 + 0.00525 + 0.005291 + 0.005542 + 0.00625 + 0.0065 + 0.005875 + 0.005417 + 0.009125 + 0.006708 + 0.006208 + 0.006125 + 0.005458 + 0.005917 + 0.006083 + 0.006541 + 0.007542 + 0.005917 + 0.0055 + 0.00525 + 0.003708 + 0.003542 + 0.004 + 0.003667 + 0.003417 + 0.003334 + 0.0035 + 0.00425 + 0.00425 + 0.004 + 0.003417 + 0.0035 + 0.004208 + 0.003583 + 0.003917 + 0.003625 + 0.003625 + 0.003458 + 0.00425 + 0.003791 + 0.003458 + 0.003708 + 0.003917 + 0.003292 + 0.003459 + 0.003666 + 0.0035 + 0.0035 + 0.003917 + 0.003625 + 0.003667 + 0.003667 + 0.003375 + 0.003583 + 0.0035 + 0.003375 + 0.003459 + 0.003667 + 0.00375 + 0.004 + 0.003709 + 0.00375 + 0.00375 + 0.003458 + 0.003792 + 0.003709 + 0.003459 + 0.003584 + 0.003959 + 0.003875 + 0.004125 + 0.003792 + 0.003708 + 0.003708 + 0.004042 + 0.004208 + 0.003583 + 0.004208 + 0.008917 + 0.005666 + 0.005375 + 0.005542 + 0.005625 + 0.0055 + 0.005209 + 0.004041 + 0.003709 + 0.003917 + 0.005 + 0.004959 + 0.005 + 0.004625 + 0.004625 + 0.004333 + 0.004375 + 0.004 + 0.004042 + 0.003917 + 0.003958 + 0.00375 + 0.003959 + 0.003916 + 0.003792 + 0.004166 + 0.003625 + 0.0045 + 0.005958 + 0.006208 + 0.005541 + 0.0055 + 0.00575 + 0.005292 + 0.015291 + 0.0115 + 0.006 + 0.00875 + 0.004917 + 0.005166 + 0.006167 + 0.006375 + 0.007542 + 0.005084 + 0.004 + 0.00425 + 0.003917 + 0.004084 + 0.003917 + 0.003209 + 0.005125 + 0.004667 + 0.011 + 0.004541 + 0.004875 + 0.004583 + 0.004542 + 0.004292 + 0.003958 + 0.004042 + 0.004292 + 0.003583 + 0.003875 + 0.003791 + 0.003959 + 0.003625 + 0.003541 + 0.003458 + 0.00375 + 0.00375 + 0.0035 + 0.003792 + 0.003666 + 0.003333 + 0.003459 + 0.003708 + 0.003625 + 0.003875 + 0.003667 + 0.003708 + 0.00375 + 0.004 + 0.003625 + 0.003834 + 0.00375 + 0.00375 + 0.003541 + 0.003875 + 0.003833 + 0.003917 + 0.003667 + 0.004125 + 0.004166 + 0.004709 + 0.003917 + 0.004208 + 0.004084 + 0.003542 + 0.013208 + 0.004333 + 0.004833 + 0.004584 + 0.007084 + 0.006042 + 0.006125 + 0.00575 + 0.005917 + 0.006375 + 0.004958 + 0.005 + 0.005333 + 0.005833 + 0.004958 + 0.004291 + 0.004542 + 0.00475 + 0.004791 + 0.00475 + 0.004375 + 0.003917 + 0.003875 + 0.003833 + 0.003916 + 0.003917 + 0.003666 + 0.004042 + 0.003916 + 0.004667 + 0.005708 + 0.005458 + 0.00575 + 0.006 + 0.005333 + 0.005083 + 0.003875 + 0.0035 + 0.00475 + 0.006042 + 0.005667 + 0.005291 + 0.005625 + 0.005625 + 0.005292 + 0.004083 + 0.003583 + 0.003875 + 0.00425 + 0.004167 + 0.004125 + 0.004125 + 0.003917 + 0.003375 + 0.003584 + 0.003625 + 0.003666 + 0.004334 + 0.00425 + 0.003542 + 0.004042 + 0.003291 + 0.003708 + 0.003625 + 0.003541 + 0.00375 + 0.003958 + 0.0035 + 0.003833 + 0.011583 + 0.004 + 0.0035 + 0.003458 + 0.003833 + 0.003541 + 0.003625 + 0.003708 + 0.004083 + 0.0035 + 0.003459 + 0.003542 + 0.00375 + 0.00375 + 0.0035 + 0.003625 + 0.00375 + 0.003667 + 0.003833 + 0.003625 + 0.00375 + 0.004042 + 0.003917 + 0.0035 + 0.003833 + 0.004125 + 0.003916 + 0.003917 + 0.005375 + 0.005042 + 0.004959 + 0.00475 + 0.004583 + 0.004583 + 0.006041 + 0.006292 + 0.006084 + 0.00575 + 0.006917 + 0.006625 + 0.006167 + 0.005042 + 0.005041 + 0.005667 + 0.004916 + 0.005083 + 0.005333 + 0.004458 + 0.003958 + 0.003833 + 0.004208 + 0.003959 + 0.003667 + 0.004 + 0.004041 + 0.003958 + 0.003833 + 0.005625 + 0.005292 + 0.005584 + 0.006042 + 0.005833 + 0.006 + 0.005792 + 0.00625 + 0.006834 + 0.006375 + 0.006083 + 0.005417 + 0.006083 + 0.006083 + 0.005833 + 0.005833 + 0.007292 + 0.0065 + 0.005917 + 0.005041 + 0.004958 + 0.004833 + 0.004542 + 0.004667 + 0.004625 + 0.004375 + 0.004042 + 0.004334 + 0.003834 + 0.003458 + 0.003333 + 0.003708 + 0.003917 + 0.003875 + 0.003875 + 0.003625 + 0.003542 + 0.003833 + 0.003875 + 0.003625 + 0.003792 + 0.00375 + 0.003625 + 0.003667 + 0.003833 + 0.003875 + 0.003875 + 0.00375 + 0.00375 + 0.003375 + 0.0035 + 0.003667 + 0.004084 + 0.003792 + 0.003541 + 0.00375 + 0.003583 + 0.003792 + 0.0035 + 0.003583 + 0.003542 + 0.004 + 0.003375 + 0.0035 + 0.003917 + 0.003583 + 0.003333 + 0.00425 + 0.004083 + 0.00425 + 0.003875 + 0.004167 + 0.003917 + 0.003958 + 0.003792 + 0.00425 + 0.004583 + 0.007084 + 0.006 + 0.00625 + 0.006125 + 0.005792 + 0.006416 + 0.006125 + 0.005125 + 0.004334 + 0.004083 + 0.005458 + 0.0055 + 0.004917 + 0.005167 + 0.004834 + 0.004959 + 0.005291 + 0.005167 + 0.004875 + 0.0045 + 0.004292 + 0.004334 + 0.003875 + 0.004083 + 0.003958 + 0.004125 + 0.004459 + 0.008833 + 0.00625 + 0.006042 + 0.006041 + 0.005583 + 0.005791 + 0.005833 + 0.005167 + 0.006541 + 0.006458 + 0.005875 + 0.005833 + 0.005875 + 0.006166 + 0.00675 + 0.005833 + 0.00475 + 0.005083 + 0.004625 + 0.004709 + 0.004459 + 0.0045 + 0.00675 + 0.00475 + 0.003792 + 0.004375 + 0.003709 + 0.003875 + 0.003875 + 0.003791 + 0.003625 + 0.003833 + 0.003583 + 0.003708 + 0.003625 + 0.003667 + 0.003959 + 0.003959 + 0.003583 + 0.003916 + 0.003583 + 0.003792 + 0.004041 + 0.003667 + 0.003666 + 0.003625 + 0.0035 + 0.003416 + 0.003416 + 0.003333 + 0.003708 + 0.003708 + 0.003666 + 0.00375 + 0.003708 + 0.003708 + 0.003625 + 0.004042 + 0.003667 + 0.003875 + 0.003792 + 0.003417 + 0.003583 + 0.003833 + 0.004125 + 0.003959 + 0.004 + 0.004125 + 0.003916 + 0.004167 + 0.003959 + 0.004125 + 0.004125 + 0.005125 + 0.004625 + 0.006708 + 0.0065 + 0.006334 + 0.005875 + 0.005958 + 0.006083 + 0.005125 + 0.005333 + 0.004667 + 0.00425 + 0.004 + 0.005625 + 0.014417 + 0.005375 + 0.004917 + 0.00525 + 0.005125 + 0.011084 + 0.004 + 0.003833 + 0.004125 + 0.014584 + 0.00375 + 0.004042 + 0.004125 + 0.005375 + 0.004916 + 0.006542 + 0.0095 + 0.006834 + 0.006167 + 0.0065 + 0.013542 + 0.005833 + 0.007292 + 0.006917 + 0.006875 + 0.006042 + 0.013458 + 0.017125 + 0.011833 + 0.005875 + 0.006833 + 0.005625 + 0.006292 + 0.00575 + 0.00525 + 0.004833 + 0.006 + 0.004417 + 0.00425 + 0.0045 + 0.004625 + 0.004792 + 0.004625 + 0.004125 + 0.004375 + 0.004417 + 0.005042 + 0.004666 + 0.004542 + 0.004834 + 0.004583 + 0.004458 + 0.004125 + 0.004208 + 0.00425 + 0.004583 + 0.004458 + 0.012542 + 0.004375 + 0.004959 + 0.005291 + 0.004833 + 0.004667 + 0.004583 + 0.00375 + 0.004833 + 0.004 + 0.004583 + 0.004167 + 0.003917 + 0.004042 + 0.004792 + 0.004125 + 0.003875 + 0.004542 + 0.004166 + 0.006417 + 0.004875 + 0.005084 + 0.00425 + 0.00475 + 0.004583 + 0.004667 + 0.004667 + 0.004791 + 0.004542 + 0.008375 + 0.005333 + 0.009625 + 0.006542 + 0.005666 + 0.006 + 0.006458 + 0.008333 + 0.004375 + 0.004167 + 0.005708 + 0.004917 + 0.004917 + 0.005166 + 0.005084 + 0.004875 + 0.004667 + 0.004375 + 0.004209 + 0.004292 + 0.004667 + 0.004417 + 0.004375 + 0.004625 + 0.00425 + 0.004125 + 0.004083 + 0.004958 + 0.006625 + 0.006375 + 0.006 + 0.005333 + 0.006458 + 0.005291 + 0.004667 + 0.004167 + 0.006583 + 0.006416 + 0.006333 + 0.006125 + 0.006375 + 0.015917 + 0.005375 + 0.004334 + 0.004542 + 0.004542 + 0.004875 + 0.004625 + 0.004166 + 0.004209 + 0.004375 + 0.0045 + 0.00375 + 0.004041 + 0.004042 + 0.004833 + 0.004667 + 0.004666 + 0.004458 + 0.004041 + 0.004959 + 0.005042 + 0.004833 + 0.005208 + 0.005292 + 0.005208 + 0.004958 + 0.00525 + 0.005125 + 0.004208 + 0.004083 + 0.004458 + 0.003917 + 0.004166 + 0.00375 + 0.003958 + 0.003959 + 0.004042 + 0.004458 + 0.004 + 0.004708 + 0.005208 + 0.0055 + 0.005083 + 0.005291 + 0.004917 + 0.005334 + 0.005083 + 0.005166 + 0.00475 + 0.004958 + 0.006042 + 0.005041 + 0.00475 + 0.005125 + 0.005083 + 0.005083 + 0.004833 + 0.004125 + 0.004709 + 0.003917 + 0.004958 + 0.004958 + 0.005541 + 0.006667 + 0.005667 + 0.005625 + 0.005584 + 0.005083 + 0.004792 + 0.004833 + 0.00425 + 0.004375 + 0.004375 + 0.004666 + 0.004375 + 0.004166 + 0.004292 + 0.004209 + 0.011791 + 0.005375 + 0.005167 + 0.005291 + 0.005292 + 0.004958 + 0.004208 + 0.004333 + 0.005041 + 0.005125 + 0.005042 + 0.007167 + 0.006375 + 0.005625 + 0.005417 + 0.005625 + 0.004584 + 0.004625 + 0.005583 + 0.005708 + 0.005833 + 0.006042 + 0.005875 + 0.005292 + 0.004 + 0.003958 + 0.004 + 0.00425 + 0.003875 + 0.003834 + 0.003875 + 0.003625 + 0.004667 + 0.003916 + 0.00375 + 0.004 + 0.003917 + 0.003792 + 0.004208 + 0.003917 + 0.003917 + 0.00375 + 0.004208 + 0.004 + 0.004125 + 0.004167 + 0.003958 + 0.004375 + 0.004042 + 0.004 + 0.003959 + 0.004125 + 0.003959 + 0.003709 + 0.004083 + 0.004083 + 0.00425 + 0.003542 + 0.003666 + 0.00375 + 0.003584 + 0.003208 + 0.003541 + 0.003834 + 0.004041 + 0.004 + 0.003958 + 0.004417 + 0.003834 + 0.003375 + 0.003959 + 0.004042 + 0.003875 + 0.004417 + 0.004375 + 0.005041 + 0.004916 + 0.004958 + 0.005 + 0.005125 + 0.004709 + 0.00425 + 0.00525 + 0.005042 + 0.00475 + 0.005125 + 0.0065 + 0.005625 + 0.005792 + 0.005 + 0.005125 + 0.004542 + 0.003917 + 0.003916 + 0.004833 + 0.004292 + 0.004125 + 0.003958 + 0.0045 + 0.004083 + 0.004333 + 0.004667 + 0.004958 + 0.008208 + 0.005 + 0.005292 + 0.005083 + 0.005166 + 0.004416 + 0.005417 + 0.005333 + 0.004792 + 0.005084 + 0.006042 + 0.006208 + 0.006208 + 0.005125 + 0.0065 + 0.005167 + 0.004667 + 0.005959 + 0.005792 + 0.005916 + 0.00625 + 0.006166 + 0.004583 + 0.003709 + 0.004375 + 0.004167 + 0.004084 + 0.003792 + 0.004208 + 0.004958 + 0.004 + 0.004166 + 0.004625 + 0.004333 + 0.004333 + 0.003917 + 0.003791 + 0.004083 + 0.004 + 0.003834 + 0.004292 + 0.004417 + 0.004417 + 0.0045 + 0.004209 + 0.0045 + 0.004125 + 0.004458 + 0.003791 + 0.004334 + 0.003791 + 0.004375 + 0.003916 + 0.004208 + 0.004 + 0.004125 + 0.003917 + 0.003791 + 0.004584 + 0.00425 + 0.003458 + 0.003666 + 0.003625 + 0.003541 + 0.003917 + 0.003791 + 0.010833 + 0.004125 + 0.008125 + 0.004125 + 0.005291 + 0.004958 + 0.004917 + 0.005041 + 0.006875 + 0.00525 + 0.005334 + 0.005125 + 0.005542 + 0.006583 + 0.006417 + 0.005875 + 0.00475 + 0.006541 + 0.00675 + 0.005792 + 0.005292 + 0.005 + 0.004875 + 0.004 + 0.003958 + 0.003625 + 0.004209 + 0.003709 + 0.004334 + 0.004125 + 0.004292 + 0.008666 + 0.005 + 0.013458 + 0.004958 + 0.004958 + 0.004042 + 0.004458 + 0.003833 + 0.004083 + 0.005041 + 0.005042 + 0.00475 + 0.009333 + 0.00575 + 0.005959 + 0.005917 + 0.004708 + 0.005875 + 0.005542 + 0.005792 + 0.005791 + 0.006125 + 0.005584 + 0.00575 + 0.005667 + 0.004375 + 0.003917 + 0.003791 + 0.003833 + 0.004042 + 0.003875 + 0.00375 + 0.003542 + 0.00425 + 0.004167 + 0.003875 + 0.003959 + 0.003709 + 0.003334 + 0.003709 + 0.003459 + 0.003625 + 0.003916 + 0.00375 + 0.003708 + 0.003708 + 0.004166 + 0.003917 + 0.003917 + 0.003958 + 0.003791 + 0.006542 + 0.004042 + 0.003875 + 0.003959 + 0.004084 + 0.004042 + 0.003625 + 0.004084 + 0.003959 + 0.004166 + 0.003417 + 0.003625 + 0.003584 + 0.003333 + 0.003833 + 0.003917 + 0.003917 + 0.003625 + 0.003959 + 0.004 + 0.003958 + 0.003875 + 0.004416 + 0.004167 + 0.004208 + 0.004375 + 0.004 + 0.005458 + 0.005083 + 0.004542 + 0.004875 + 0.006042 + 0.005834 + 0.0055 + 0.005875 + 0.006875 + 0.005708 + 0.004875 + 0.004834 + 0.004209 + 0.004 + 0.003917 + 0.003875 + 0.004125 + 0.004125 + 0.003792 + 0.004125 + 0.005416 + 0.005208 + 0.004875 + 0.004584 + 0.004459 + 0.00425 + 0.004083 + 0.003958 + 0.004125 + 0.004 + 0.00425 + 0.004833 + 0.004958 + 0.004875 + 0.006542 + 0.00575 + 0.005959 + 0.005709 + 0.005834 + 0.004209 + 0.005375 + 0.005459 + 0.006 + 0.00575 + 0.005542 + 0.005166 + 0.0055 + 0.004125 + 0.003708 + 0.003917 + 0.00375 + 0.004 + 0.003875 + 0.004 + 0.003792 + 0.003625 + 0.004334 + 0.003625 + 0.004167 + 0.004333 + 0.003708 + 0.003875 + 0.003708 + 0.003458 + 0.004375 + 0.003666 + 0.003667 + 0.003959 + 0.004292 + 0.004375 + 0.00375 + 0.00375 + 0.003625 + 0.003833 + 0.004125 + 0.004125 + 0.003916 + 0.003708 + 0.004 + 0.003667 + 0.004209 + 0.0035 + 0.004 + 0.003916 + 0.004125 + 0.003334 + 0.003667 + 0.003666 + 0.003667 + 0.003541 + 0.003792 + 0.003459 + 0.003875 + 0.003834 + 0.003834 + 0.003708 + 0.003625 + 0.00425 + 0.004875 + 0.005208 + 0.005 + 0.005083 + 0.005083 + 0.005 + 0.004125 + 0.005166 + 0.00525 + 0.004875 + 0.004667 + 0.006083 + 0.005917 + 0.005834 + 0.004625 + 0.005042 + 0.005041 + 0.004125 + 0.004125 + 0.004083 + 0.003916 + 0.003667 + 0.004 + 0.004167 + 0.004416 + 0.004167 + 0.00425 + 0.005292 + 0.004917 + 0.005125 + 0.004834 + 0.004834 + 0.005084 + 0.005334 + 0.005125 + 0.004709 + 0.004708 + 0.005958 + 0.006041 + 0.005875 + 0.005958 + 0.00475 + 0.005792 + 0.004917 + 0.006875 + 0.006042 + 0.005542 + 0.005292 + 0.005459 + 0.004917 + 0.00425 + 0.003875 + 0.00375 + 0.004375 + 0.00375 + 0.00375 + 0.004709 + 0.004167 + 0.003708 + 0.003667 + 0.003583 + 0.004542 + 0.003834 + 0.003458 + 0.003583 + 0.003542 + 0.003666 + 0.003584 + 0.004041 + 0.003709 + 0.003959 + 0.004 + 0.003708 + 0.003959 + 0.004 + 0.003917 + 0.004083 + 0.003625 + 0.003667 + 0.003666 + 0.003875 + 0.003708 + 0.003833 + 0.003833 + 0.003709 + 0.00375 + 0.004 + 0.003792 + 0.003916 + 0.003583 + 0.003667 + 0.003708 + 0.003625 + 0.003292 + 0.003625 + 0.00375 + 0.003458 + 0.004208 + 0.004083 + 0.004167 + 0.004208 + 0.003959 + 0.005542 + 0.005166 + 0.004625 + 0.004875 + 0.004334 + 0.005209 + 0.00475 + 0.0055 + 0.005083 + 0.006209 + 0.006375 + 0.006708 + 0.004834 + 0.004834 + 0.004875 + 0.005334 + 0.005375 + 0.005542 + 0.005084 + 0.005125 + 0.005292 + 0.0055 + 0.0045 + 0.005166 + 0.00525 + 0.004458 + 0.004667 + 0.004875 + 0.005 + 0.006958 + 0.006417 + 0.00575 + 0.007042 + 0.006209 + 0.005792 + 0.005916 + 0.006834 + 0.008042 + 0.006208 + 0.004458 + 0.005666 + 0.005792 + 0.006334 + 0.00525 + 0.007209 + 0.007458 + 0.006041 + 0.006 + 0.005167 + 0.005292 + 0.00475 + 0.003792 + 0.003833 + 0.004167 + 0.003875 + 0.004167 + 0.003791 + 0.004334 + 0.004084 + 0.004 + 0.003667 + 0.004166 + 0.004833 + 0.004792 + 0.004791 + 0.005459 + 0.005209 + 0.004667 + 0.005458 + 0.005958 + 0.005625 + 0.005792 + 0.00475 + 0.005042 + 0.005 + 0.005084 + 0.004875 + 0.004041 + 0.004042 + 0.004084 + 0.004125 + 0.003666 + 0.003834 + 0.004 + 0.004 + 0.00475 + 0.005292 + 0.005542 + 0.005625 + 0.005291 + 0.005042 + 0.004792 + 0.004625 + 0.0045 + 0.004458 + 0.005708 + 0.005583 + 0.005958 + 0.005792 + 0.005333 + 0.004375 + 0.005125 + 0.005458 + 0.006125 + 0.005041 + 0.005708 + 0.006292 + 0.007084 + 0.006875 + 0.007209 + 0.008792 + 0.007291 + 0.006083 + 0.00525 + 0.004583 + 0.004792 + 0.004541 + 0.0055 + 0.004917 + 0.004542 + 0.004417 + 0.00525 + 0.005834 + 0.005167 + 0.005 + 0.007583 + 0.008458 + 0.005291 + 0.009667 + 0.005541 + 0.005625 + 0.004791 + 0.014958 + 0.005875 + 0.006167 + 0.006625 + 0.007541 + 0.006792 + 0.008833 + 0.008833 + 0.017833 + 0.008792 + 0.006417 + 0.009125 + 0.009584 + 0.008542 + 0.007875 + 0.00675 + 0.005 + 0.004833 + 0.005 + 0.004833 + 0.005208 + 0.004833 + 0.004458 + 0.005875 + 0.004291 + 0.004375 + 0.005709 + 0.004916 + 0.004583 + 0.005375 + 0.005375 + 0.006 + 0.00525 + 0.004834 + 0.004375 + 0.003542 + 0.004 + 0.003959 + 0.003875 + 0.003958 + 0.003958 + 0.003916 + 0.010667 + 0.004041 + 0.004083 + 0.004 + 0.004458 + 0.004542 + 0.004584 + 0.004666 + 0.005625 + 0.004208 + 0.003917 + 0.003916 + 0.004 + 0.003875 + 0.004084 + 0.004417 + 0.004041 + 0.003667 + 0.003959 + 0.00375 + 0.003458 + 0.003834 + 0.004416 + 0.004375 + 0.003875 + 0.003958 + 0.008417 + 0.005334 + 0.004875 + 0.004834 + 0.004875 + 0.005833 + 0.005208 + 0.005292 + 0.005208 + 0.006125 + 0.006625 + 0.006625 + 0.006292 + 0.005459 + 0.003875 + 0.004625 + 0.004125 + 0.004375 + 0.004209 + 0.004541 + 0.004208 + 0.005125 + 0.005458 + 0.004958 + 0.004917 + 0.005292 + 0.004916 + 0.003833 + 0.004708 + 0.004292 + 0.004333 + 0.004208 + 0.005083 + 0.005375 + 0.006084 + 0.006334 + 0.005959 + 0.005375 + 0.00575 + 0.0055 + 0.004209 + 0.006291 + 0.006458 + 0.005667 + 0.005959 + 0.006375 + 0.005375 + 0.006 + 0.004833 + 0.004792 + 0.004458 + 0.004583 + 0.004583 + 0.004208 + 0.00375 + 0.004042 + 0.0045 + 0.004167 + 0.003666 + 0.003667 + 0.004416 + 0.004 + 0.003916 + 0.004375 + 0.003875 + 0.003417 + 0.003917 + 0.00375 + 0.003875 + 0.004042 + 0.00425 + 0.003709 + 0.003917 + 0.004166 + 0.004 + 0.003916 + 0.004292 + 0.003833 + 0.004291 + 0.003792 + 0.003583 + 0.003791 + 0.004167 + 0.003791 + 0.004209 + 0.003875 + 0.004542 + 0.004125 + 0.003875 + 0.004125 + 0.004084 + 0.003958 + 0.004 + 0.003834 + 0.003666 + 0.003875 + 0.004042 + 0.004041 + 0.004334 + 0.00425 + 0.004 + 0.004042 + 0.004 + 0.004292 + 0.003834 + 0.004292 + 0.005 + 0.005209 + 0.005875 + 0.006459 + 0.005875 + 0.005542 + 0.005875 + 0.004625 + 0.004084 + 0.004334 + 0.004375 + 0.004375 + 0.004375 + 0.005416 + 0.005167 + 0.005292 + 0.004708 + 0.005042 + 0.004834 + 0.004625 + 0.003666 + 0.004209 + 0.003959 + 0.004292 + 0.004041 + 0.004292 + 0.005 + 0.004792 + 0.006167 + 0.006208 + 0.006083 + 0.005708 + 0.005917 + 0.005458 + 0.0045 + 0.00625 + 0.005875 + 0.006 + 0.005833 + 0.00575 + 0.006042 + 0.004708 + 0.003875 + 0.006 + 0.003375 + 0.003291 + 0.003875 + 0.00425 + 0.004 + 0.004167 + 0.00425 + 0.003375 + 0.003125 + 0.01225 + 0.003542 + 0.003167 + 0.003708 + 0.003417 + 0.003333 + 0.003583 + 0.003292 + 0.003625 + 0.003667 + 0.003833 + 0.0035 + 0.003042 + 0.003292 + 0.003166 + 0.004375 + 0.003959 + 0.00425 + 0.003792 + 0.0035 + 0.003042 + 0.003375 + 0.003125 + 0.003334 + 0.003541 + 0.003167 + 0.003375 + 0.0035 + 0.003708 + 0.003542 + 0.003666 + 0.003792 + 0.003292 + 0.003542 + 0.003291 + 0.003167 + 0.003542 + 0.003666 + 0.003917 + 0.003708 + 0.004041 + 0.0035 + 0.003541 + 0.003542 + 0.003458 + 0.003833 + 0.00375 + 0.004875 + 0.004208 + 0.005709 + 0.005334 + 0.005459 + 0.005084 + 0.005583 + 0.00475 + 0.00425 + 0.003625 + 0.003458 + 0.004125 + 0.003959 + 0.003958 + 0.003583 + 0.003333 + 0.00325 + 0.003333 + 0.005084 + 0.004792 + 0.00475 + 0.004417 + 0.004458 + 0.004625 + 0.004083 + 0.003375 + 0.004042 + 0.004917 + 0.005458 + 0.004417 + 0.006 + 0.00525 + 0.005167 + 0.005167 + 0.0045 + 0.005209 + 0.004666 + 0.004417 + 0.007 + 0.006375 + 0.006417 + 0.006208 + 0.005833 + 0.004542 + 0.004333 + 0.004333 + 0.003875 + 0.004667 + 0.004667 + 0.004292 + 0.0045 + 0.004042 + 0.004375 + 0.003958 + 0.0045 + 0.004333 + 0.004416 + 0.004417 + 0.004125 + 0.003833 + 0.004333 + 0.004542 + 0.004292 + 0.00475 + 0.004541 + 0.00425 + 0.00425 + 0.004708 + 0.004375 + 0.004541 + 0.004167 + 0.004542 + 0.004292 + 0.004042 + 0.004083 + 0.004334 + 0.004833 + 0.004541 + 0.00425 + 0.004209 + 0.004167 + 0.00375 + 0.003917 + 0.004542 + 0.004125 + 0.004375 + 0.004125 + 0.004459 + 0.004708 + 0.004291 + 0.004959 + 0.004875 + 0.005 + 0.004834 + 0.004834 + 0.009125 + 0.0055 + 0.005584 + 0.005791 + 0.005542 + 0.005875 + 0.005375 + 0.006 + 0.0055 + 0.006334 + 0.006792 + 0.0065 + 0.0065 + 0.005625 + 0.004667 + 0.005208 + 0.004542 + 0.004209 + 0.004208 + 0.004667 + 0.0045 + 0.0055 + 0.005791 + 0.01325 + 0.005625 + 0.00475 + 0.004625 + 0.004417 + 0.00525 + 0.005084 + 0.005667 + 0.005666 + 0.006834 + 0.006667 + 0.007125 + 0.008041 + 0.00675 + 0.006083 + 0.005208 + 0.004375 + 0.004708 + 0.005583 + 0.00675 + 0.006625 + 0.006792 + 0.006625 + 0.0065 + 0.006958 + 0.005625 + 0.004959 + 0.005083 + 0.005042 + 0.004292 + 0.004041 + 0.004084 + 0.00375 + 0.005417 + 0.013333 + 0.004 + 0.004458 + 0.004333 + 0.0045 + 0.004333 + 0.003916 + 0.004 + 0.004125 + 0.004083 + 0.00425 + 0.004417 + 0.004416 + 0.004208 + 0.004042 + 0.00425 + 0.00425 + 0.004209 + 0.004 + 0.003792 + 0.004125 + 0.004541 + 0.003958 + 0.004083 + 0.004083 + 0.003875 + 0.00375 + 0.003875 + 0.004583 + 0.0045 + 0.003875 + 0.004 + 0.004 + 0.004292 + 0.004041 + 0.004292 + 0.004125 + 0.003708 + 0.004084 + 0.004541 + 0.004917 + 0.004667 + 0.004125 + 0.008208 + 0.005333 + 0.005167 + 0.004791 + 0.005292 + 0.00575 + 0.00475 + 0.004917 + 0.005083 + 0.005875 + 0.006208 + 0.005833 + 0.005958 + 0.005542 + 0.004542 + 0.004792 + 0.004416 + 0.004625 + 0.004208 + 0.004583 + 0.004875 + 0.004334 + 0.004625 + 0.004334 + 0.006041 + 0.005417 + 0.005458 + 0.004916 + 0.005375 + 0.005042 + 0.003958 + 0.004333 + 0.005166 + 0.005084 + 0.004583 + 0.007166 + 0.006292 + 0.005917 + 0.005666 + 0.00625 + 0.004709 + 0.005292 + 0.007666 + 0.00625 + 0.006042 + 0.005959 + 0.005875 + 0.0055 + 0.004208 + 0.004083 + 0.004125 + 0.004 + 0.003667 + 0.003791 + 0.003792 + 0.003667 + 0.004375 + 0.007875 + 0.004042 + 0.004708 + 0.004875 + 0.004833 + 0.004708 + 0.004292 + 0.004125 + 0.003958 + 0.007 + 0.003833 + 0.004375 + 0.004125 + 0.004167 + 0.004084 + 0.003875 + 0.004209 + 0.004042 + 0.004083 + 0.004291 + 0.003917 + 0.003917 + 0.004166 + 0.00425 + 0.004625 + 0.004458 + 0.004917 + 0.004041 + 0.004 + 0.004042 + 0.003917 + 0.003875 + 0.004083 + 0.00375 + 0.004458 + 0.003917 + 0.004041 + 0.003875 + 0.004125 + 0.004375 + 0.004375 + 0.005666 + 0.005292 + 0.004834 + 0.004542 + 0.004292 + 0.004458 + 0.003875 + 0.004291 + 0.005083 + 0.004792 + 0.005042 + 0.005875 + 0.006084 + 0.005542 + 0.006125 + 0.004833 + 0.004791 + 0.004417 + 0.00475 + 0.004208 + 0.004583 + 0.004167 + 0.004458 + 0.004625 + 0.004459 + 0.004208 + 0.004417 + 0.004834 + 0.005625 + 0.005042 + 0.005167 + 0.005541 + 0.005375 + 0.004958 + 0.005042 + 0.004958 + 0.00475 + 0.004542 + 0.005791 + 0.006042 + 0.006209 + 0.005709 + 0.005334 + 0.0055 + 0.00475 + 0.006542 + 0.006 + 0.005958 + 0.006333 + 0.006 + 0.004291 + 0.004042 + 0.003667 + 0.004083 + 0.003833 + 0.00375 + 0.003916 + 0.00475 + 0.004208 + 0.003792 + 0.003417 + 0.004292 + 0.00425 + 0.003875 + 0.004125 + 0.00375 + 0.004084 + 0.003834 + 0.003875 + 0.00375 + 0.004208 + 0.00425 + 0.004167 + 0.004208 + 0.004 + 0.004125 + 0.003625 + 0.003417 + 0.003792 + 0.003458 + 0.004 + 0.003583 + 0.003542 + 0.004 + 0.00375 + 0.003833 + 0.003708 + 0.004417 + 0.004167 + 0.004083 + 0.004041 + 0.004042 + 0.004 + 0.004042 + 0.004208 + 0.003875 + 0.003791 + 0.004083 + 0.004375 + 0.004917 + 0.003834 + 0.004084 + 0.004459 + 0.005917 + 0.004791 + 0.005208 + 0.00525 + 0.004875 + 0.005375 + 0.004667 + 0.004458 + 0.005167 + 0.005833 + 0.006334 + 0.006333 + 0.004917 + 0.004834 + 0.004959 + 0.004584 + 0.004084 + 0.004417 + 0.004459 + 0.004667 + 0.004041 + 0.00425 + 0.00425 + 0.004458 + 0.004458 + 0.004333 + 0.005167 + 0.005375 + 0.005041 + 0.004916 + 0.005125 + 0.005042 + 0.005 + 0.004458 + 0.004792 + 0.004833 + 0.005583 + 0.005917 + 0.00625 + 0.004917 + 0.005666 + 0.005833 + 0.006458 + 0.006083 + 0.006625 + 0.006708 + 0.006416 + 0.005958 + 0.005458 + 0.009292 + 0.005708 + 0.005458 + 0.005125 + 0.005583 + 0.005375 + 0.005709 + 0.004709 + 0.005625 + 0.004667 + 0.005583 + 0.005166 + 0.005167 + 0.005084 + 0.004375 + 0.005125 + 0.004958 + 0.005125 + 0.005417 + 0.004875 + 0.005083 + 0.004959 + 0.005167 + 0.004833 + 0.004542 + 0.005166 + 0.004334 + 0.004166 + 0.005167 + 0.005625 + 0.005667 + 0.00525 + 0.005834 + 0.004959 + 0.004291 + 0.004417 + 0.0045 + 0.004541 + 0.004834 + 0.004334 + 0.005 + 0.004959 + 0.00475 + 0.00525 + 0.004333 + 0.005041 + 0.006125 + 0.0065 + 0.006541 + 0.006333 + 0.005417 + 0.005416 + 0.005125 + 0.006708 + 0.005833 + 0.0055 + 0.005583 + 0.004833 + 0.004792 + 0.005167 + 0.004416 + 0.007208 + 0.006667 + 0.006667 + 0.006375 + 0.005917 + 0.0055 + 0.0055 + 0.00525 + 0.005458 + 0.005167 + 0.006167 + 0.006583 + 0.005833 + 0.00575 + 0.005042 + 0.00475 + 0.006834 + 0.005041 + 0.005 + 0.005583 + 0.004875 + 0.004375 + 0.005292 + 0.005292 + 0.006584 + 0.006625 + 0.008375 + 0.006375 + 0.005375 + 0.0055 + 0.004541 + 0.006542 + 0.007666 + 0.005833 + 0.006208 + 0.007042 + 0.006167 + 0.005792 + 0.004875 + 0.004708 + 0.0045 + 0.004375 + 0.004458 + 0.012458 + 0.005167 + 0.005625 + 0.0055 + 0.005209 + 0.006084 + 0.012417 + 0.004875 + 0.005125 + 0.005167 + 0.01225 + 0.005208 + 0.005791 + 0.005 + 0.006 + 0.005833 + 0.008083 + 0.013334 + 0.007333 + 0.006875 + 0.007375 + 0.005459 + 0.006125 + 0.007459 + 0.004792 + 0.004958 + 0.005375 + 0.005041 + 0.004667 + 0.004541 + 0.004375 + 0.004583 + 0.004292 + 0.004667 + 0.005458 + 0.005083 + 0.004417 + 0.00525 + 0.004917 + 0.0055 + 0.004458 + 0.006208 + 0.004958 + 0.005292 + 0.004792 + 0.00525 + 0.004542 + 0.0045 + 0.004084 + 0.00425 + 0.004334 + 0.004042 + 0.00425 + 0.015334 + 0.006417 + 0.005875 + 0.00575 + 0.005792 + 0.005958 + 0.006958 + 0.004917 + 0.004208 + 0.004625 + 0.004375 + 0.0045 + 0.004208 + 0.004541 + 0.004417 + 0.00425 + 0.004625 + 0.004625 + 0.004417 + 0.004458 + 0.00425 + 0.004208 + 0.004458 + 0.004208 + 0.004333 + 0.004291 + 0.006917 + 0.006666 + 0.00625 + 0.005708 + 0.005917 + 0.005041 + 0.006292 + 0.006 + 0.005333 + 0.00575 + 0.006208 + 0.005333 + 0.005083 + 0.004875 + 0.006 + 0.006334 + 0.004792 + 0.004833 + 0.005208 + 0.004584 + 0.004 + 0.004125 + 0.004041 + 0.003459 + 0.004916 + 0.004042 + 0.0035 + 0.003709 + 0.004375 + 0.004125 + 0.004041 + 0.003625 + 0.004291 + 0.003917 + 0.003625 + 0.00425 + 0.00425 + 0.004166 + 0.004542 + 0.003917 + 0.004 + 0.004291 + 0.004208 + 0.005167 + 0.004 + 0.00425 + 0.004125 + 0.003666 + 0.003792 + 0.003834 + 0.003459 + 0.003875 + 0.004042 + 0.003834 + 0.004041 + 0.004 + 0.003875 + 0.004209 + 0.004167 + 0.004208 + 0.003958 + 0.003833 + 0.003958 + 0.003708 + 0.004125 + 0.004375 + 0.004792 + 0.0045 + 0.004208 + 0.004291 + 0.0045 + 0.004292 + 0.004625 + 0.004458 + 0.005459 + 0.005917 + 0.006333 + 0.0055 + 0.004959 + 0.006041 + 0.005459 + 0.004667 + 0.004333 + 0.0045 + 0.00425 + 0.0055 + 0.005417 + 0.005333 + 0.005041 + 0.005042 + 0.004959 + 0.004625 + 0.0045 + 0.004208 + 0.004042 + 0.004459 + 0.004458 + 0.004458 + 0.004375 + 0.004125 + 0.004458 + 0.010958 + 0.006042 + 0.006334 + 0.006542 + 0.005708 + 0.004916 + 0.00475 + 0.004042 + 0.005625 + 0.006292 + 0.006083 + 0.005791 + 0.006125 + 0.00575 + 0.005083 + 0.005625 + 0.00475 + 0.005125 + 0.004791 + 0.004542 + 0.004792 + 0.004416 + 0.004042 + 0.004375 + 0.003417 + 0.004375 + 0.004417 + 0.004041 + 0.004334 + 0.003958 + 0.004417 + 0.004125 + 0.003958 + 0.004 + 0.003833 + 0.004167 + 0.0045 + 0.004084 + 0.003958 + 0.004292 + 0.004 + 0.003917 + 0.004 + 0.004084 + 0.004125 + 0.004084 + 0.004334 + 0.004125 + 0.003959 + 0.004 + 0.003709 + 0.004084 + 0.003791 + 0.003792 + 0.004084 + 0.004083 + 0.004041 + 0.003792 + 0.004542 + 0.004167 + 0.004083 + 0.00375 + 0.003792 + 0.003875 + 0.004417 + 0.004125 + 0.004125 + 0.004167 + 0.004458 + 0.004583 + 0.004 + 0.0045 + 0.004084 + 0.004875 + 0.004959 + 0.006084 + 0.006167 + 0.005625 + 0.00625 + 0.005958 + 0.005 + 0.004541 + 0.004209 + 0.004334 + 0.005459 + 0.005416 + 0.005166 + 0.004833 + 0.004959 + 0.005042 + 0.004666 + 0.00425 + 0.004625 + 0.004333 + 0.0045 + 0.004709 + 0.00425 + 0.004167 + 0.004416 + 0.004042 + 0.005333 + 0.0065 + 0.006417 + 0.006 + 0.005792 + 0.005792 + 0.00525 + 0.00475 + 0.005542 + 0.006375 + 0.006125 + 0.006041 + 0.005625 + 0.006 + 0.005292 + 0.004917 + 0.004375 + 0.004375 + 0.004209 + 0.004167 + 0.003958 + 0.004125 + 0.003959 + 0.00425 + 0.003834 + 0.003417 + 0.004166 + 0.004042 + 0.003875 + 0.003875 + 0.003917 + 0.00375 + 0.004 + 0.003875 + 0.004 + 0.003917 + 0.003833 + 0.004375 + 0.003708 + 0.003959 + 0.003959 + 0.00425 + 0.00425 + 0.004041 + 0.004209 + 0.004083 + 0.004083 + 0.003917 + 0.004334 + 0.003625 + 0.003583 + 0.003708 + 0.003583 + 0.003625 + 0.003916 + 0.003833 + 0.003667 + 0.003959 + 0.004166 + 0.003958 + 0.00375 + 0.003959 + 0.003834 + 0.004292 + 0.004375 + 0.008208 + 0.005166 + 0.005167 + 0.005083 + 0.005166 + 0.005084 + 0.004584 + 0.004125 + 0.005166 + 0.005125 + 0.005041 + 0.006167 + 0.006166 + 0.006084 + 0.005834 + 0.0055 + 0.00475 + 0.004208 + 0.004167 + 0.003916 + 0.004208 + 0.005334 + 0.005291 + 0.005167 + 0.004958 + 0.005375 + 0.005042 + 0.004083 + 0.004042 + 0.004667 + 0.004208 + 0.004208 + 0.004041 + 0.004375 + 0.004042 + 0.005 + 0.005125 + 0.006375 + 0.005833 + 0.005708 + 0.005791 + 0.006125 + 0.004708 + 0.00475 + 0.00525 + 0.006208 + 0.005791 + 0.005958 + 0.005709 + 0.005709 + 0.005458 + 0.00425 + 0.00425 + 0.003959 + 0.004334 + 0.003791 + 0.004125 + 0.004083 + 0.003708 + 0.003917 + 0.00375 + 0.003625 + 0.003459 + 0.003875 + 0.003625 + 0.00375 + 0.003875 + 0.003625 + 0.003792 + 0.004084 + 0.003708 + 0.003958 + 0.003917 + 0.003916 + 0.00375 + 0.00425 + 0.003625 + 0.003917 + 0.003917 + 0.004125 + 0.003792 + 0.004417 + 0.003875 + 0.003917 + 0.003916 + 0.003834 + 0.003292 + 0.003333 + 0.003625 + 0.003917 + 0.00375 + 0.004041 + 0.003625 + 0.003791 + 0.004083 + 0.003834 + 0.003834 + 0.003791 + 0.004 + 0.004042 + 0.004125 + 0.003917 + 0.004125 + 0.008166 + 0.005083 + 0.004917 + 0.004791 + 0.005125 + 0.005 + 0.005209 + 0.00475 + 0.004875 + 0.005 + 0.006 + 0.006417 + 0.005541 + 0.004834 + 0.004875 + 0.004834 + 0.004166 + 0.004084 + 0.003916 + 0.003958 + 0.004417 + 0.004083 + 0.004333 + 0.003916 + 0.004667 + 0.004125 + 0.005542 + 0.004833 + 0.004791 + 0.004875 + 0.004666 + 0.004875 + 0.005042 + 0.004792 + 0.005 + 0.005042 + 0.006292 + 0.006042 + 0.005958 + 0.005625 + 0.004958 + 0.0055 + 0.004416 + 0.005542 + 0.006042 + 0.005833 + 0.005541 + 0.005333 + 0.004667 + 0.003666 + 0.004083 + 0.003875 + 0.003667 + 0.003916 + 0.004125 + 0.003667 + 0.004458 + 0.003458 + 0.003541 + 0.003917 + 0.003875 + 0.003875 + 0.0035 + 0.003583 + 0.003542 + 0.004 + 0.003834 + 0.003875 + 0.004167 + 0.004042 + 0.00375 + 0.003667 + 0.00375 + 0.003792 + 0.003958 + 0.003791 + 0.004458 + 0.003709 + 0.003959 + 0.004208 + 0.00425 + 0.00375 + 0.004083 + 0.005333 + 0.004167 + 0.003334 + 0.003875 + 0.003625 + 0.003541 + 0.003708 + 0.003583 + 0.003875 + 0.003833 + 0.003792 + 0.003792 + 0.003958 + 0.004 + 0.004083 + 0.004708 + 0.00425 + 0.004041 + 0.003792 + 0.008208 + 0.004959 + 0.004667 + 0.004959 + 0.006042 + 0.006125 + 0.004917 + 0.004833 + 0.004958 + 0.0065 + 0.005834 + 0.004667 + 0.004792 + 0.004667 + 0.00475 + 0.004125 + 0.004084 + 0.005 + 0.004625 + 0.004625 + 0.00475 + 0.004667 + 0.004833 + 0.004 + 0.004209 + 0.004 + 0.004041 + 0.005458 + 0.005084 + 0.004958 + 0.004458 + 0.005583 + 0.005709 + 0.00475 + 0.005334 + 0.005 + 0.009041 + 0.005875 + 0.004708 + 0.005542 + 0.00625 + 0.005709 + 0.004625 + 0.004791 + 0.005709 + 0.005625 + 0.005792 + 0.005 + 0.004375 + 0.004417 + 0.003834 + 0.004209 + 0.003625 + 0.003792 + 0.004708 + 0.003792 + 0.003666 + 0.003917 + 0.004083 + 0.00375 + 0.003708 + 0.003791 + 0.0035 + 0.00375 + 0.003792 + 0.003667 + 0.003458 + 0.003875 + 0.004292 + 0.003833 + 0.003958 + 0.003917 + 0.01575 + 0.003917 + 0.003875 + 0.00425 + 0.004584 + 0.004083 + 0.004708 + 0.004667 + 0.004958 + 0.003833 + 0.004875 + 0.004916 + 0.005875 + 0.004666 + 0.00475 + 0.00475 + 0.004667 + 0.004958 + 0.004541 + 0.004917 + 0.004083 + 0.00375 + 0.003959 + 0.004375 + 0.004084 + 0.004209 + 0.004333 + 0.0045 + 0.004041 + 0.003833 + 0.004084 + 0.003959 + 0.005792 + 0.006416 + 0.005667 + 0.006333 + 0.00575 + 0.005833 + 0.006333 + 0.005833 + 0.004625 + 0.005333 + 0.004708 + 0.004708 + 0.004 + 0.004 + 0.004 + 0.00375 + 0.004042 + 0.004541 + 0.004875 + 0.004875 + 0.005208 + 0.005375 + 0.005208 + 0.005167 + 0.00525 + 0.005208 + 0.0055 + 0.007875 + 0.006583 + 0.006458 + 0.006333 + 0.006417 + 0.006709 + 0.007209 + 0.005209 + 0.006167 + 0.005333 + 0.004916 + 0.005 + 0.00525 + 0.006125 + 0.006334 + 0.005542 + 0.005333 + 0.005583 + 0.005708 + 0.005041 + 0.004792 + 0.004667 + 0.004208 + 0.004 + 0.004125 + 0.003666 + 0.003583 + 0.004042 + 0.003917 + 0.004 + 0.004125 + 0.004166 + 0.003709 + 0.003584 + 0.003625 + 0.004209 + 0.003667 + 0.003791 + 0.003833 + 0.003708 + 0.003833 + 0.004208 + 0.004 + 0.003708 + 0.003708 + 0.004084 + 0.003959 + 0.004125 + 0.003792 + 0.003792 + 0.003875 + 0.003791 + 0.003708 + 0.004375 + 0.004125 + 0.003875 + 0.003416 + 0.003667 + 0.003875 + 0.003625 + 0.00375 + 0.003791 + 0.003584 + 0.00375 + 0.004 + 0.004291 + 0.004209 + 0.003917 + 0.004083 + 0.004125 + 0.004125 + 0.0045 + 0.00425 + 0.006917 + 0.006708 + 0.006333 + 0.00625 + 0.006167 + 0.006333 + 0.006 + 0.007291 + 0.005416 + 0.005125 + 0.005125 + 0.005292 + 0.00475 + 0.005209 + 0.005167 + 0.005166 + 0.00475 + 0.004375 + 0.003791 + 0.003958 + 0.004333 + 0.004125 + 0.004291 + 0.004209 + 0.004042 + 0.003958 + 0.005208 + 0.007375 + 0.006791 + 0.006333 + 0.006625 + 0.006167 + 0.00625 + 0.006583 + 0.006708 + 0.005125 + 0.006542 + 0.006 + 0.006375 + 0.006583 + 0.007083 + 0.006916 + 0.00675 + 0.00575 + 0.005625 + 0.005 + 0.004875 + 0.00475 + 0.004792 + 0.004042 + 0.004 + 0.004333 + 0.003916 + 0.0035 + 0.003667 + 0.004541 + 0.004292 + 0.003917 + 0.003333 + 0.003958 + 0.004208 + 0.003708 + 0.003708 + 0.003834 + 0.004125 + 0.003708 + 0.003792 + 0.003584 + 0.004083 + 0.004 + 0.004792 + 0.003792 + 0.00375 + 0.003834 + 0.003833 + 0.004166 + 0.003458 + 0.003541 + 0.004167 + 0.004042 + 0.004042 + 0.003917 + 0.004125 + 0.003958 + 0.003875 + 0.003583 + 0.00375 + 0.003417 + 0.003666 + 0.003583 + 0.00375 + 0.003542 + 0.00375 + 0.004084 + 0.004417 + 0.004292 + 0.004 + 0.004084 + 0.003958 + 0.004083 + 0.004208 + 0.00525 + 0.010125 + 0.006541 + 0.006417 + 0.006459 + 0.006375 + 0.006625 + 0.005125 + 0.005541 + 0.004542 + 0.004125 + 0.004083 + 0.004291 + 0.00475 + 0.005125 + 0.005334 + 0.005167 + 0.005083 + 0.005166 + 0.005042 + 0.005084 + 0.00475 + 0.004834 + 0.004125 + 0.004167 + 0.0045 + 0.005042 + 0.005333 + 0.006125 + 0.006542 + 0.006209 + 0.006417 + 0.006375 + 0.006125 + 0.005458 + 0.007541 + 0.006542 + 0.006584 + 0.006417 + 0.0065 + 0.00625 + 0.007417 + 0.005 + 0.004584 + 0.004708 + 0.00475 + 0.004875 + 0.00475 + 0.005167 + 0.005416 + 0.005584 + 0.005208 + 0.003916 + 0.003834 + 0.004209 + 0.00375 + 0.003667 + 0.004125 + 0.003875 + 0.003667 + 0.003792 + 0.003667 + 0.0035 + 0.003542 + 0.004042 + 0.004 + 0.0035 + 0.003625 + 0.003666 + 0.004042 + 0.003959 + 0.003584 + 0.004084 + 0.003667 + 0.003875 + 0.00375 + 0.003792 + 0.00375 + 0.003833 + 0.003958 + 0.004209 + 0.003958 + 0.004125 + 0.004042 + 0.003959 + 0.004209 + 0.004166 + 0.003917 + 0.00375 + 0.003709 + 0.004125 + 0.004334 + 0.004084 + 0.003958 + 0.004208 + 0.004292 + 0.004166 + 0.004417 + 0.00425 + 0.004458 + 0.004958 + 0.005042 + 0.005834 + 0.006708 + 0.006042 + 0.006125 + 0.006292 + 0.005833 + 0.005333 + 0.005375 + 0.004833 + 0.004125 + 0.004541 + 0.00425 + 0.005709 + 0.005167 + 0.005333 + 0.004375 + 0.005209 + 0.005167 + 0.005667 + 0.004959 + 0.005125 + 0.004708 + 0.003792 + 0.004333 + 0.003958 + 0.004792 + 0.005 + 0.004834 + 0.007375 + 0.006792 + 0.006416 + 0.006125 + 0.005458 + 0.006416 + 0.006333 + 0.009875 + 0.0065 + 0.006334 + 0.006167 + 0.006291 + 0.006167 + 0.004875 + 0.004834 + 0.004375 + 0.003667 + 0.003958 + 0.004167 + 0.003875 + 0.003958 + 0.004 + 0.004334 + 0.003959 + 0.004083 + 0.004083 + 0.004 + 0.003959 + 0.00425 + 0.003875 + 0.003833 + 0.003708 + 0.003875 + 0.003458 + 0.004083 + 0.003458 + 0.004125 + 0.003541 + 0.003583 + 0.003667 + 0.004333 + 0.004291 + 0.003958 + 0.00375 + 0.003709 + 0.003625 + 0.003625 + 0.003791 + 0.003875 + 0.003958 + 0.003958 + 0.004333 + 0.004208 + 0.003917 + 0.004 + 0.004333 + 0.003833 + 0.003917 + 0.003542 + 0.003958 + 0.003416 + 0.003875 + 0.003833 + 0.00525 + 0.005584 + 0.004959 + 0.005084 + 0.005042 + 0.005042 + 0.004958 + 0.006458 + 0.006333 + 0.005208 + 0.005917 + 0.006333 + 0.006833 + 0.006083 + 0.005917 + 0.005709 + 0.005291 + 0.00525 + 0.005041 + 0.003875 + 0.004084 + 0.005042 + 0.005625 + 0.005209 + 0.0055 + 0.005292 + 0.005084 + 0.005417 + 0.005208 + 0.005208 + 0.004333 + 0.004083 + 0.004125 + 0.005125 + 0.006833 + 0.02525 + 0.031208 + 0.010125 + 0.006916 + 0.00575 + 0.005041 + 0.0045 + 0.005 + 0.006584 + 0.006709 + 0.006125 + 0.006292 + 0.006083 + 0.005125 + 0.00625 + 0.005 + 0.004584 + 0.004667 + 0.015166 + 0.004042 + 0.004084 + 0.004584 + 0.005875 + 0.004958 + 0.004625 + 0.003708 + 0.005083 + 0.0055 + 0.003958 + 0.004416 + 0.004167 + 0.004458 + 0.004167 + 0.004333 + 0.004209 + 0.003875 + 0.004041 + 0.004167 + 0.003958 + 0.003541 + 0.004041 + 0.004542 + 0.004084 + 0.00375 + 0.00375 + 0.004375 + 0.004416 + 0.003875 + 0.003916 + 0.004042 + 0.003708 + 0.004125 + 0.003708 + 0.004041 + 0.004417 + 0.003791 + 0.004334 + 0.004666 + 0.003958 + 0.004625 + 0.004125 + 0.004375 + 0.004 + 0.004833 + 0.004667 + 0.003875 + 0.004167 + 0.003875 + 0.004666 + 0.004792 + 0.004791 + 0.004833 + 0.005833 + 0.005166 + 0.006625 + 0.006334 + 0.006916 + 0.00625 + 0.006292 + 0.005709 + 0.004833 + 0.005375 + 0.005125 + 0.005125 + 0.007542 + 0.005791 + 0.005333 + 0.005375 + 0.005708 + 0.005541 + 0.005125 + 0.00475 + 0.004916 + 0.0045 + 0.004416 + 0.005083 + 0.004458 + 0.004417 + 0.005 + 0.006166 + 0.008458 + 0.007458 + 0.007 + 0.00725 + 0.006792 + 0.006292 + 0.007458 + 0.005875 + 0.007542 + 0.007 + 0.006917 + 0.007291 + 0.006708 + 0.0075 + 0.006625 + 0.005292 + 0.00525 + 0.005125 + 0.004958 + 0.005125 + 0.005666 + 0.005208 + 0.006917 + 0.006209 + 0.005917 + 0.005833 + 0.005583 + 0.005542 + 0.004666 + 0.00525 + 0.004792 + 0.004709 + 0.004791 + 0.004084 + 0.004417 + 0.004416 + 0.004458 + 0.005 + 0.004125 + 0.004291 + 0.004042 + 0.003958 + 0.004084 + 0.004208 + 0.003958 + 0.004541 + 0.004125 + 0.003917 + 0.003917 + 0.004333 + 0.003792 + 0.003875 + 0.00375 + 0.003917 + 0.004291 + 0.003625 + 0.00425 + 0.004292 + 0.003792 + 0.004542 + 0.003958 + 0.004667 + 0.004209 + 0.004041 + 0.00475 + 0.0045 + 0.00425 + 0.004 + 0.00425 + 0.004292 + 0.00375 + 0.004 + 0.004584 + 0.004917 + 0.005125 + 0.006916 + 0.006458 + 0.006125 + 0.005791 + 0.006125 + 0.004833 + 0.003875 + 0.004209 + 0.004459 + 0.004625 + 0.004208 + 0.004417 + 0.005625 + 0.005583 + 0.004958 + 0.005208 + 0.005125 + 0.005125 + 0.004208 + 0.003791 + 0.004 + 0.00425 + 0.004333 + 0.00425 + 0.005083 + 0.005042 + 0.005875 + 0.006125 + 0.005958 + 0.005834 + 0.005916 + 0.005708 + 0.004583 + 0.006167 + 0.0065 + 0.006125 + 0.005667 + 0.005875 + 0.0055 + 0.005666 + 0.005417 + 0.004541 + 0.004833 + 0.0045 + 0.004291 + 0.004083 + 0.00375 + 0.004542 + 0.004875 + 0.0035 + 0.004 + 0.003958 + 0.00425 + 0.004 + 0.003667 + 0.004041 + 0.003959 + 0.003834 + 0.004 + 0.004 + 0.004292 + 0.004084 + 0.004042 + 0.003875 + 0.003958 + 0.003417 + 0.003541 + 0.003792 + 0.00375 + 0.003959 + 0.004084 + 0.004083 + 0.003708 + 0.003875 + 0.004167 + 0.003916 + 0.00375 + 0.003834 + 0.004041 + 0.004084 + 0.004209 + 0.004209 + 0.004042 + 0.003875 + 0.003833 + 0.00375 + 0.004125 + 0.003959 + 0.004 + 0.00425 + 0.004709 + 0.004125 + 0.009875 + 0.004208 + 0.004458 + 0.00425 + 0.004125 + 0.004333 + 0.005041 + 0.004875 + 0.0065 + 0.006292 + 0.005667 + 0.005959 + 0.0055 + 0.00475 + 0.004375 + 0.004666 + 0.0045 + 0.004709 + 0.00425 + 0.004292 + 0.004083 + 0.006208 + 0.004958 + 0.004875 + 0.004916 + 0.005458 + 0.005292 + 0.0045 + 0.004167 + 0.004125 + 0.00425 + 0.003875 + 0.004292 + 0.005208 + 0.004875 + 0.004541 + 0.006167 + 0.005667 + 0.006041 + 0.0055 + 0.005041 + 0.005542 + 0.005292 + 0.005375 + 0.008791 + 0.004791 + 0.004583 + 0.004292 + 0.00475 + 0.003125 + 0.003042 + 0.003542 + 0.003291 + 0.003208 + 0.003167 + 0.003084 + 0.003625 + 0.003375 + 0.003708 + 0.003333 + 0.00325 + 0.003209 + 0.003375 + 0.003167 + 0.003458 + 0.003209 + 0.003542 + 0.003583 + 0.003667 + 0.003542 + 0.003209 + 0.003084 + 0.003084 + 0.003334 + 0.003667 + 0.003333 + 0.003291 + 0.003375 + 0.003209 + 0.003 + 0.004042 + 0.003708 + 0.00375 + 0.003167 + 0.003375 + 0.003083 + 0.003375 + 0.003375 + 0.003292 + 0.003291 + 0.003083 + 0.0035 + 0.003208 + 0.003333 + 0.003708 + 0.003542 + 0.003333 + 0.004125 + 0.004708 + 0.004583 + 0.004166 + 0.004167 + 0.004084 + 0.004292 + 0.004416 + 0.003792 + 0.004083 + 0.004291 + 0.004584 + 0.005417 + 0.006083 + 0.005625 + 0.005084 + 0.005 + 0.004542 + 0.003417 + 0.003708 + 0.003542 + 0.003791 + 0.003542 + 0.004125 + 0.0045 + 0.004042 + 0.004375 + 0.004708 + 0.004583 + 0.0045 + 0.003708 + 0.003417 + 0.003625 + 0.003709 + 0.004333 + 0.004375 + 0.004417 + 0.004041 + 0.005 + 0.0055 + 0.005208 + 0.0055 + 0.006459 + 0.005459 + 0.006292 + 0.006 + 0.006791 + 0.005625 + 0.005333 + 0.006042 + 0.006666 + 0.006542 + 0.006417 + 0.005958 + 0.005292 + 0.004833 + 0.004625 + 0.005375 + 0.004834 + 0.0045 + 0.005375 + 0.005 + 0.005042 + 0.004875 + 0.005625 + 0.005 + 0.005166 + 0.005667 + 0.005542 + 0.004292 + 0.005666 + 0.006042 + 0.006 + 0.005708 + 0.005875 + 0.004542 + 0.004292 + 0.004292 + 0.005167 + 0.004875 + 0.004667 + 0.004458 + 0.004375 + 0.004375 + 0.004583 + 0.004833 + 0.004208 + 0.004125 + 0.004292 + 0.004625 + 0.004167 + 0.004166 + 0.00425 + 0.003959 + 0.004667 + 0.004625 + 0.003833 + 0.004542 + 0.005167 + 0.005209 + 0.005875 + 0.007167 + 0.006375 + 0.006458 + 0.005625 + 0.005292 + 0.00475 + 0.004666 + 0.0045 + 0.005541 + 0.005208 + 0.004583 + 0.006667 + 0.006709 + 0.006584 + 0.006209 + 0.006625 + 0.005375 + 0.004708 + 0.005708 + 0.005666 + 0.006083 + 0.005583 + 0.005833 + 0.006042 + 0.005542 + 0.00575 + 0.006584 + 0.006042 + 0.005958 + 0.005875 + 0.005583 + 0.0055 + 0.004792 + 0.004625 + 0.004833 + 0.006041 + 0.006792 + 0.006 + 0.007209 + 0.010042 + 0.007125 + 0.007042 + 0.007 + 0.005833 + 0.006625 + 0.006541 + 0.007 + 0.007125 + 0.007 + 0.006041 + 0.0055 + 0.005167 + 0.004542 + 0.004416 + 0.004417 + 0.004542 + 0.005958 + 0.005417 + 0.005167 + 0.00575 + 0.005417 + 0.004916 + 0.005209 + 0.005417 + 0.005583 + 0.004916 + 0.005417 + 0.005 + 0.005084 + 0.005333 + 0.0055 + 0.005916 + 0.005042 + 0.005625 + 0.004625 + 0.004709 + 0.004834 + 0.004917 + 0.005208 + 0.005375 + 0.005459 + 0.005584 + 0.003917 + 0.004458 + 0.004 + 0.004334 + 0.004375 + 0.003833 + 0.003875 + 0.003875 + 0.003917 + 0.005333 + 0.00675 + 0.006833 + 0.007959 + 0.0065 + 0.005375 + 0.006625 + 0.005667 + 0.005875 + 0.006833 + 0.007541 + 0.007833 + 0.006625 + 0.006958 + 0.005417 + 0.005833 + 0.005 + 0.005167 + 0.006958 + 0.006708 + 0.005541 + 0.007083 + 0.006583 + 0.00725 + 0.007458 + 0.006375 + 0.005667 + 0.005208 + 0.005458 + 0.004459 + 0.004291 + 0.004542 + 0.004459 + 0.004458 + 0.004666 + 0.005417 + 0.005416 + 0.004917 + 0.005584 + 0.005125 + 0.005416 + 0.004458 + 0.005334 + 0.005167 + 0.005584 + 0.005417 + 0.00475 + 0.006666 + 0.006667 + 0.0055 + 0.0055 + 0.005625 + 0.00475 + 0.005084 + 0.005125 + 0.006833 + 0.005833 + 0.006166 + 0.005791 + 0.005875 + 0.004625 + 0.004 + 0.004334 + 0.004167 + 0.00375 + 0.003792 + 0.003792 + 0.004833 + 0.005042 + 0.004041 + 0.003917 + 0.00475 + 0.004208 + 0.00375 + 0.004375 + 0.004334 + 0.004542 + 0.003959 + 0.00425 + 0.004125 + 0.003917 + 0.004084 + 0.004209 + 0.004125 + 0.004125 + 0.003875 + 0.004 + 0.004125 + 0.004084 + 0.004083 + 0.004541 + 0.004 + 0.004041 + 0.003709 + 0.003709 + 0.004291 + 0.003709 + 0.004167 + 0.004458 + 0.003833 + 0.004041 + 0.004042 + 0.004167 + 0.004167 + 0.003667 + 0.004167 + 0.004333 + 0.004042 + 0.004084 + 0.004583 + 0.004458 + 0.004292 + 0.004375 + 0.004584 + 0.004875 + 0.005125 + 0.00525 + 0.005041 + 0.005917 + 0.00575 + 0.005125 + 0.004917 + 0.004625 + 0.006209 + 0.006334 + 0.004959 + 0.004959 + 0.005167 + 0.004792 + 0.0045 + 0.004417 + 0.004458 + 0.004333 + 0.00425 + 0.004083 + 0.004625 + 0.004208 + 0.004292 + 0.0045 + 0.004458 + 0.00425 + 0.005333 + 0.004958 + 0.005208 + 0.005083 + 0.0065 + 0.005667 + 0.004958 + 0.0055 + 0.005666 + 0.006792 + 0.005959 + 0.005709 + 0.006292 + 0.005458 + 0.005833 + 0.004917 + 0.004875 + 0.00675 + 0.0065 + 0.005667 + 0.004791 + 0.00475 + 0.00475 + 0.00375 + 0.003917 + 0.003916 + 0.004417 + 0.004209 + 0.004291 + 0.004459 + 0.004375 + 0.004416 + 0.003958 + 0.004083 + 0.003875 + 0.003666 + 0.003958 + 0.004167 + 0.003917 + 0.003875 + 0.003875 + 0.004416 + 0.004167 + 0.004041 + 0.00375 + 0.004166 + 0.003875 + 0.00425 + 0.003958 + 0.00425 + 0.004167 + 0.004166 + 0.003792 + 0.003791 + 0.003583 + 0.003542 + 0.003917 + 0.00425 + 0.004083 + 0.004209 + 0.004125 + 0.003834 + 0.004458 + 0.004 + 0.004042 + 0.004 + 0.003959 + 0.004459 + 0.004666 + 0.004542 + 0.004584 + 0.004209 + 0.004375 + 0.004375 + 0.004084 + 0.003958 + 0.004292 + 0.006625 + 0.006542 + 0.005791 + 0.005458 + 0.005542 + 0.005208 + 0.004958 + 0.00625 + 0.005291 + 0.004625 + 0.005 + 0.00475 + 0.004625 + 0.004291 + 0.004792 + 0.004208 + 0.004291 + 0.004375 + 0.004166 + 0.004084 + 0.004084 + 0.004417 + 0.004375 + 0.004375 + 0.004375 + 0.003958 + 0.004042 + 0.00675 + 0.006334 + 0.006084 + 0.006 + 0.006208 + 0.005125 + 0.004583 + 0.004208 + 0.003708 + 0.0065 + 0.006167 + 0.006 + 0.006 + 0.006 + 0.00525 + 0.006084 + 0.005292 + 0.004458 + 0.004833 + 0.004666 + 0.004833 + 0.00425 + 0.003833 + 0.003625 + 0.004666 + 0.004084 + 0.003667 + 0.00425 + 0.003958 + 0.00425 + 0.004125 + 0.004167 + 0.003708 + 0.004458 + 0.003875 + 0.003625 + 0.004333 + 0.003917 + 0.003959 + 0.004625 + 0.004084 + 0.004042 + 0.003875 + 0.004166 + 0.004 + 0.003834 + 0.004083 + 0.004041 + 0.003792 + 0.003833 + 0.003667 + 0.003709 + 0.004125 + 0.003542 + 0.004 + 0.004042 + 0.004208 + 0.003625 + 0.003583 + 0.003958 + 0.0045 + 0.003917 + 0.004125 + 0.003958 + 0.004292 + 0.004 + 0.00425 + 0.004083 + 0.004375 + 0.004083 + 0.00425 + 0.003833 + 0.004042 + 0.004333 + 0.005166 + 0.006584 + 0.006417 + 0.005625 + 0.005875 + 0.0055 + 0.005 + 0.0045 + 0.00425 + 0.004291 + 0.004125 + 0.004875 + 0.005333 + 0.005167 + 0.004792 + 0.004875 + 0.004917 + 0.00475 + 0.004375 + 0.004 + 0.004334 + 0.0045 + 0.004334 + 0.004375 + 0.004084 + 0.004333 + 0.004875 + 0.006792 + 0.006334 + 0.006167 + 0.005875 + 0.005875 + 0.005167 + 0.005875 + 0.005083 + 0.006542 + 0.00575 + 0.005792 + 0.005084 + 0.004459 + 0.006792 + 0.005875 + 0.004875 + 0.004375 + 0.005041 + 0.004333 + 0.004041 + 0.00375 + 0.003666 + 0.003459 + 0.004125 + 0.004459 + 0.00375 + 0.004417 + 0.003541 + 0.003542 + 0.003458 + 0.004292 + 0.003667 + 0.003583 + 0.003875 + 0.004083 + 0.004042 + 0.003959 + 0.004083 + 0.003916 + 0.003917 + 0.004083 + 0.004333 + 0.00425 + 0.00425 + 0.004208 + 0.004 + 0.003875 + 0.003875 + 0.004292 + 0.003875 + 0.003917 + 0.003875 + 0.003834 + 0.0035 + 0.003833 + 0.003792 + 0.00375 + 0.004125 + 0.003792 + 0.003958 + 0.004041 + 0.003875 + 0.003791 + 0.003834 + 0.004375 + 0.004375 + 0.003958 + 0.004167 + 0.004125 + 0.004292 + 0.004042 + 0.004 + 0.004083 + 0.005084 + 0.006542 + 0.0065 + 0.006 + 0.005625 + 0.005209 + 0.005167 + 0.004458 + 0.003958 + 0.003959 + 0.003792 + 0.004708 + 0.004833 + 0.005125 + 0.004875 + 0.004917 + 0.004708 + 0.004166 + 0.004333 + 0.004167 + 0.004041 + 0.00425 + 0.004167 + 0.004041 + 0.004167 + 0.004583 + 0.00475 + 0.006125 + 0.005666 + 0.005917 + 0.005875 + 0.005833 + 0.005333 + 0.005792 + 0.005 + 0.005459 + 0.005167 + 0.005833 + 0.005375 + 0.004666 + 0.005792 + 0.005792 + 0.005292 + 0.004666 + 0.004583 + 0.004708 + 0.004292 + 0.003625 + 0.004 + 0.00375 + 0.004291 + 0.003542 + 0.004666 + 0.004 + 0.004125 + 0.003834 + 0.003584 + 0.003875 + 0.003875 + 0.004 + 0.003959 + 0.003917 + 0.003791 + 0.003917 + 0.004 + 0.003792 + 0.004125 + 0.004 + 0.003833 + 0.003917 + 0.003833 + 0.003916 + 0.003708 + 0.004041 + 0.003958 + 0.004209 + 0.004 + 0.003541 + 0.003625 + 0.003583 + 0.003666 + 0.003458 + 0.0035 + 0.0035 + 0.004041 + 0.003875 + 0.004083 + 0.004041 + 0.003792 + 0.003916 + 0.004042 + 0.003958 + 0.004208 + 0.004083 + 0.004083 + 0.003958 + 0.004333 + 0.004334 + 0.00425 + 0.003958 + 0.004917 + 0.006125 + 0.006167 + 0.005291 + 0.005667 + 0.005959 + 0.005292 + 0.003666 + 0.003709 + 0.004625 + 0.005625 + 0.004875 + 0.004542 + 0.004458 + 0.004125 + 0.003792 + 0.004083 + 0.004333 + 0.004 + 0.004291 + 0.004292 + 0.004167 + 0.003916 + 0.003834 + 0.004 + 0.004458 + 0.004 + 0.004833 + 0.006416 + 0.005958 + 0.005666 + 0.005875 + 0.005333 + 0.005541 + 0.003875 + 0.005083 + 0.006667 + 0.006125 + 0.005333 + 0.005291 + 0.00575 + 0.005458 + 0.005042 + 0.004167 + 0.003916 + 0.003875 + 0.004375 + 0.003583 + 0.004 + 0.003708 + 0.004041 + 0.003875 + 0.003875 + 0.003542 + 0.004166 + 0.003708 + 0.00375 + 0.003583 + 0.003542 + 0.003833 + 0.003791 + 0.004 + 0.003667 + 0.003959 + 0.003834 + 0.003917 + 0.003792 + 0.004125 + 0.003667 + 0.003625 + 0.003834 + 0.00375 + 0.003917 + 0.003875 + 0.003791 + 0.004 + 0.003833 + 0.004125 + 0.003875 + 0.004708 + 0.00425 + 0.004417 + 0.004084 + 0.003917 + 0.003541 + 0.00375 + 0.004541 + 0.004875 + 0.005458 + 0.005125 + 0.005125 + 0.005417 + 0.006459 + 0.005334 + 0.005167 + 0.00475 + 0.003834 + 0.004125 + 0.004042 + 0.003958 + 0.004875 + 0.00475 + 0.004792 + 0.005875 + 0.006 + 0.005583 + 0.005958 + 0.005667 + 0.00475 + 0.003959 + 0.003959 + 0.004125 + 0.003959 + 0.004 + 0.005 + 0.004875 + 0.00475 + 0.005209 + 0.004833 + 0.004708 + 0.004416 + 0.004375 + 0.004958 + 0.004958 + 0.004834 + 0.005959 + 0.005625 + 0.006208 + 0.00625 + 0.007625 + 0.007042 + 0.006334 + 0.005292 + 0.005 + 0.00375 + 0.003708 + 0.004917 + 0.005416 + 0.006667 + 0.006416 + 0.005959 + 0.005917 + 0.006166 + 0.005209 + 0.005 + 0.004958 + 0.004958 + 0.003833 + 0.00425 + 0.003916 + 0.004042 + 0.004208 + 0.003917 + 0.004 + 0.003541 + 0.004292 + 0.003834 + 0.003625 + 0.003917 + 0.003416 + 0.0035 + 0.003583 + 0.003917 + 0.003875 + 0.003833 + 0.003916 + 0.004 + 0.003792 + 0.003792 + 0.003958 + 0.003917 + 0.004125 + 0.003709 + 0.004542 + 0.004083 + 0.004333 + 0.004167 + 0.004125 + 0.003583 + 0.004125 + 0.003792 + 0.003833 + 0.003625 + 0.003916 + 0.003667 + 0.003542 + 0.003666 + 0.003584 + 0.004 + 0.004208 + 0.003875 + 0.003917 + 0.004041 + 0.00425 + 0.00425 + 0.004084 + 0.004625 + 0.004333 + 0.005042 + 0.00575 + 0.005459 + 0.006541 + 0.006167 + 0.005916 + 0.006417 + 0.006167 + 0.007 + 0.006458 + 0.005125 + 0.004833 + 0.00525 + 0.005125 + 0.005292 + 0.005209 + 0.005 + 0.004334 + 0.004125 + 0.004417 + 0.004167 + 0.004083 + 0.004084 + 0.004083 + 0.004166 + 0.004042 + 0.004291 + 0.004042 + 0.004084 + 0.0065 + 0.006375 + 0.0065 + 0.006542 + 0.006791 + 0.006084 + 0.007625 + 0.006292 + 0.005166 + 0.006417 + 0.006417 + 0.006208 + 0.00625 + 0.006083 + 0.010166 + 0.007042 + 0.005708 + 0.005541 + 0.005458 + 0.005167 + 0.004708 + 0.004833 + 0.004916 + 0.003667 + 0.004167 + 0.003416 + 0.003625 + 0.003875 + 0.004167 + 0.004167 + 0.003958 + 0.003625 + 0.003708 + 0.003709 + 0.003667 + 0.003667 + 0.004083 + 0.004 + 0.003958 + 0.003833 + 0.004 + 0.003667 + 0.003792 + 0.004333 + 0.004166 + 0.00375 + 0.003959 + 0.00425 + 0.003875 + 0.003916 + 0.004 + 0.003916 + 0.003792 + 0.00375 + 0.003875 + 0.004042 + 0.003625 + 0.003625 + 0.003583 + 0.003625 + 0.003917 + 0.003417 + 0.003666 + 0.003792 + 0.004208 + 0.003917 + 0.004167 + 0.004208 + 0.004084 + 0.004125 + 0.003459 + 0.004167 + 0.00425 + 0.004542 + 0.004916 + 0.006292 + 0.006917 + 0.005958 + 0.006625 + 0.006583 + 0.006208 + 0.005125 + 0.005041 + 0.005625 + 0.0055 + 0.004916 + 0.005792 + 0.005583 + 0.005083 + 0.005166 + 0.005458 + 0.005167 + 0.005041 + 0.00525 + 0.005417 + 0.004666 + 0.004208 + 0.004125 + 0.004 + 0.004375 + 0.003583 + 0.004792 + 0.004958 + 0.006625 + 0.006667 + 0.006458 + 0.006458 + 0.006375 + 0.005459 + 0.006542 + 0.007333 + 0.00675 + 0.006375 + 0.006333 + 0.006417 + 0.00575 + 0.006833 + 0.004875 + 0.004916 + 0.004959 + 0.004667 + 0.005041 + 0.004625 + 0.004541 + 0.005542 + 0.004625 + 0.004667 + 0.005291 + 0.003958 + 0.004209 + 0.003959 + 0.003667 + 0.003583 + 0.003666 + 0.003625 + 0.003583 + 0.00375 + 0.003792 + 0.003666 + 0.003917 + 0.003833 + 0.00375 + 0.00375 + 0.00375 + 0.004 + 0.00375 + 0.003708 + 0.003958 + 0.003958 + 0.003958 + 0.003542 + 0.003791 + 0.004083 + 0.003709 + 0.003792 + 0.003708 + 0.00375 + 0.003833 + 0.003625 + 0.003834 + 0.0035 + 0.003667 + 0.0035 + 0.003833 + 0.004125 + 0.004125 + 0.003875 + 0.003708 + 0.004417 + 0.004583 + 0.004083 + 0.004167 + 0.00425 + 0.004125 + 0.004709 + 0.004875 + 0.006125 + 0.006666 + 0.006416 + 0.007083 + 0.006083 + 0.006167 + 0.005375 + 0.00525 + 0.004458 + 0.005459 + 0.005709 + 0.005125 + 0.005292 + 0.0055 + 0.004958 + 0.005625 + 0.005125 + 0.005 + 0.004459 + 0.004208 + 0.003792 + 0.004125 + 0.003917 + 0.004208 + 0.004208 + 0.004958 + 0.006083 + 0.006625 + 0.006292 + 0.005834 + 0.006125 + 0.006417 + 0.005542 + 0.0075 + 0.006334 + 0.006208 + 0.006375 + 0.006083 + 0.0065 + 0.006375 + 0.006917 + 0.005667 + 0.005583 + 0.00475 + 0.004791 + 0.004709 + 0.004875 + 0.004833 + 0.004917 + 0.004875 + 0.003792 + 0.00375 + 0.003958 + 0.003959 + 0.004 + 0.003875 + 0.004 + 0.004 + 0.003667 + 0.003709 + 0.003791 + 0.003709 + 0.003792 + 0.003917 + 0.003834 + 0.004 + 0.004 + 0.003917 + 0.003708 + 0.004375 + 0.00375 + 0.004209 + 0.003792 + 0.004083 + 0.003916 + 0.004042 + 0.003833 + 0.003917 + 0.003791 + 0.003833 + 0.003958 + 0.003875 + 0.00375 + 0.003917 + 0.003666 + 0.003708 + 0.003917 + 0.004084 + 0.003667 + 0.004083 + 0.004084 + 0.00425 + 0.003958 + 0.004042 + 0.004 + 0.004125 + 0.004042 + 0.004083 + 0.004917 + 0.004792 + 0.006666 + 0.006417 + 0.00625 + 0.006542 + 0.006459 + 0.006292 + 0.005792 + 0.00525 + 0.005125 + 0.005334 + 0.004959 + 0.006125 + 0.006208 + 0.006417 + 0.018833 + 0.011459 + 0.006458 + 0.005333 + 0.005375 + 0.005042 + 0.004541 + 0.003792 + 0.004125 + 0.003958 + 0.003875 + 0.005584 + 0.005292 + 0.006875 + 0.006125 + 0.006083 + 0.0055 + 0.00575 + 0.00525 + 0.004209 + 0.006292 + 0.005834 + 0.005834 + 0.006125 + 0.006 + 0.005292 + 0.004667 + 0.004042 + 0.004084 + 0.00375 + 0.004584 + 0.004916 + 0.004667 + 0.004041 + 0.0035 + 0.0035 + 0.003791 + 0.003792 + 0.004667 + 0.004542 + 0.004292 + 0.004208 + 0.004417 + 0.003959 + 0.004208 + 0.003833 + 0.003708 + 0.003792 + 0.003875 + 0.003667 + 0.003584 + 0.003958 + 0.003958 + 0.004 + 0.004 + 0.004292 + 0.004625 + 0.003958 + 0.003916 + 0.004375 + 0.004 + 0.003708 + 0.00425 + 0.00425 + 0.003916 + 0.004042 + 0.0045 + 0.003709 + 0.003959 + 0.00425 + 0.004125 + 0.003917 + 0.003667 + 0.003958 + 0.003833 + 0.003334 + 0.005584 + 0.0055 + 0.005042 + 0.004875 + 0.0055 + 0.005666 + 0.004833 + 0.00425 + 0.004625 + 0.005458 + 0.00525 + 0.004792 + 0.006416 + 0.006667 + 0.00625 + 0.006375 + 0.005542 + 0.005667 + 0.005541 + 0.005333 + 0.005583 + 0.005 + 0.004333 + 0.005334 + 0.004834 + 0.00475 + 0.00475 + 0.005708 + 0.005459 + 0.005917 + 0.005041 + 0.005417 + 0.005625 + 0.005334 + 0.005125 + 0.005709 + 0.005709 + 0.005625 + 0.007208 + 0.007042 + 0.006959 + 0.006542 + 0.006834 + 0.005958 + 0.007375 + 0.0075 + 0.008 + 0.007083 + 0.00675 + 0.006584 + 0.006375 + 0.004708 + 0.005333 + 0.00475 + 0.004667 + 0.005084 + 0.007209 + 0.005667 + 0.006166 + 0.005458 + 0.00525 + 0.003917 + 0.004542 + 0.004834 + 0.004917 + 0.004333 + 0.004208 + 0.004167 + 0.004 + 0.004167 + 0.004292 + 0.003917 + 0.004 + 0.003875 + 0.004542 + 0.003792 + 0.003917 + 0.003791 + 0.004458 + 0.004292 + 0.004125 + 0.003833 + 0.004042 + 0.004125 + 0.00375 + 0.004083 + 0.003958 + 0.003834 + 0.003833 + 0.003958 + 0.004125 + 0.003834 + 0.004041 + 0.004542 + 0.003792 + 0.004 + 0.003958 + 0.003625 + 0.003917 + 0.004042 + 0.004334 + 0.004125 + 0.004041 + 0.003667 + 0.005542 + 0.004834 + 0.004584 + 0.004584 + 0.004958 + 0.00625 + 0.005125 + 0.004667 + 0.004917 + 0.006167 + 0.006166 + 0.005625 + 0.004917 + 0.00475 + 0.005292 + 0.004291 + 0.004125 + 0.004292 + 0.003958 + 0.004333 + 0.004042 + 0.00425 + 0.003625 + 0.004167 + 0.004 + 0.004292 + 0.004 + 0.005833 + 0.005 + 0.0045 + 0.004833 + 0.005084 + 0.006167 + 0.005208 + 0.005084 + 0.005167 + 0.00475 + 0.006583 + 0.006 + 0.005125 + 0.005916 + 0.005708 + 0.005583 + 0.005125 + 0.005958 + 0.006125 + 0.005917 + 0.006083 + 0.004458 + 0.004125 + 0.00375 + 0.003666 + 0.003375 + 0.004208 + 0.004 + 0.004375 + 0.003375 + 0.003708 + 0.003708 + 0.003792 + 0.003833 + 0.003917 + 0.00375 + 0.004 + 0.003875 + 0.003875 + 0.003875 + 0.003958 + 0.003833 + 0.003625 + 0.003292 + 0.003583 + 0.003791 + 0.003458 + 0.003542 + 0.00425 + 0.004 + 0.0035 + 0.003959 + 0.003958 + 0.004041 + 0.003542 + 0.004 + 0.003917 + 0.003833 + 0.003625 + 0.004041 + 0.003792 + 0.004 + 0.003833 + 0.004042 + 0.003792 + 0.003791 + 0.003917 + 0.004 + 0.003291 + 0.004542 + 0.003875 + 0.004333 + 0.004 + 0.003666 + 0.004083 + 0.003792 + 0.003917 + 0.004958 + 0.006125 + 0.006333 + 0.00575 + 0.005583 + 0.0055 + 0.006458 + 0.005875 + 0.005833 + 0.004833 + 0.004959 + 0.005084 + 0.004167 + 0.004583 + 0.003917 + 0.003958 + 0.004125 + 0.004333 + 0.004167 + 0.004334 + 0.004334 + 0.005625 + 0.004625 + 0.004333 + 0.004791 + 0.00475 + 0.005291 + 0.004208 + 0.004792 + 0.005208 + 0.004875 + 0.005041 + 0.005625 + 0.005916 + 0.005625 + 0.005208 + 0.005833 + 0.005458 + 0.004833 + 0.00575 + 0.006209 + 0.005584 + 0.005792 + 0.005916 + 0.004583 + 0.003583 + 0.00375 + 0.003625 + 0.00375 + 0.003667 + 0.00375 + 0.003959 + 0.003334 + 0.003875 + 0.003917 + 0.004041 + 0.003792 + 0.004041 + 0.00375 + 0.003875 + 0.003791 + 0.004084 + 0.003834 + 0.003708 + 0.003792 + 0.004 + 0.003709 + 0.003709 + 0.003667 + 0.004041 + 0.003625 + 0.003542 + 0.00375 + 0.003667 + 0.003916 + 0.003583 + 0.00425 + 0.003833 + 0.003834 + 0.00325 + 0.003667 + 0.003791 + 0.004041 + 0.003875 + 0.004042 + 0.004042 + 0.004 + 0.003667 + 0.003916 + 0.003708 + 0.003959 + 0.003958 + 0.004208 + 0.004291 + 0.004417 + 0.004458 + 0.004541 + 0.006209 + 0.004625 + 0.004708 + 0.004791 + 0.005292 + 0.005334 + 0.004875 + 0.004917 + 0.006625 + 0.005792 + 0.005916 + 0.006167 + 0.005125 + 0.004333 + 0.00425 + 0.004333 + 0.004042 + 0.004125 + 0.004542 + 0.004375 + 0.0045 + 0.003917 + 0.004291 + 0.003833 + 0.006167 + 0.004959 + 0.005166 + 0.004625 + 0.005333 + 0.004125 + 0.004667 + 0.005167 + 0.004958 + 0.004625 + 0.00525 + 0.006375 + 0.005709 + 0.005584 + 0.006083 + 0.005458 + 0.005667 + 0.005042 + 0.006125 + 0.006125 + 0.006208 + 0.006 + 0.005833 + 0.0045 + 0.0045 + 0.003667 + 0.003916 + 0.003792 + 0.003791 + 0.003708 + 0.004 + 0.004542 + 0.004041 + 0.004375 + 0.00425 + 0.0045 + 0.004208 + 0.004083 + 0.004166 + 0.004083 + 0.004042 + 0.004 + 0.004 + 0.004084 + 0.003875 + 0.003667 + 0.003958 + 0.003625 + 0.003667 + 0.003583 + 0.003834 + 0.003791 + 0.003667 + 0.003625 + 0.003709 + 0.003542 + 0.00375 + 0.004459 + 0.004709 + 0.0045 + 0.004833 + 0.004833 + 0.00475 + 0.005041 + 0.004917 + 0.00525 + 0.004625 + 0.00475 + 0.004708 + 0.004625 + 0.004292 + 0.004792 + 0.004708 + 0.004459 + 0.004291 + 0.004167 + 0.004541 + 0.005625 + 0.0045 + 0.004292 + 0.005209 + 0.00575 + 0.005416 + 0.005167 + 0.005 + 0.004375 + 0.005917 + 0.006542 + 0.005 + 0.005125 + 0.005 + 0.004667 + 0.004333 + 0.003708 + 0.004125 + 0.004041 + 0.00425 + 0.004166 + 0.00425 + 0.004 + 0.004375 + 0.004292 + 0.004167 + 0.003875 + 0.00575 + 0.004709 + 0.00475 + 0.005416 + 0.005833 + 0.005667 + 0.004375 + 0.004583 + 0.005833 + 0.006667 + 0.005458 + 0.005292 + 0.005667 + 0.005583 + 0.005292 + 0.005959 + 0.005667 + 0.005916 + 0.005708 + 0.00475 + 0.004166 + 0.00375 + 0.004 + 0.003584 + 0.003375 + 0.003333 + 0.004458 + 0.004292 + 0.003166 + 0.003708 + 0.004292 + 0.004084 + 0.006209 + 0.003709 + 0.003916 + 0.004083 + 0.00375 + 0.00375 + 0.00375 + 0.003792 + 0.004042 + 0.003834 + 0.003667 + 0.004 + 0.004084 + 0.00375 + 0.003792 + 0.003834 + 0.003625 + 0.0035 + 0.003625 + 0.003625 + 0.003833 + 0.003708 + 0.004 + 0.00425 + 0.004209 + 0.004458 + 0.00475 + 0.004916 + 0.004833 + 0.005083 + 0.00475 + 0.004625 + 0.004083 + 0.00425 + 0.003917 + 0.004625 + 0.0045 + 0.005959 + 0.006042 + 0.006458 + 0.005875 + 0.004875 + 0.005042 + 0.005458 + 0.005416 + 0.007959 + 0.006709 + 0.006375 + 0.006583 + 0.005959 + 0.005958 + 0.006417 + 0.005291 + 0.00475 + 0.004791 + 0.005209 + 0.00625 + 0.006 + 0.006 + 0.005208 + 0.004667 + 0.004958 + 0.00425 + 0.005 + 0.004667 + 0.005208 + 0.00425 + 0.004375 + 0.004583 + 0.004375 + 0.0045 + 0.004458 + 0.006833 + 0.006875 + 0.006375 + 0.006959 + 0.006208 + 0.005542 + 0.007791 + 0.006375 + 0.006583 + 0.00725 + 0.006167 + 0.006666 + 0.005 + 0.006833 + 0.006916 + 0.006125 + 0.005167 + 0.005041 + 0.005208 + 0.005125 + 0.005542 + 0.005125 + 0.004625 + 0.004542 + 0.003709 + 0.004417 + 0.005 + 0.006083 + 0.005583 + 0.005458 + 0.00475 + 0.004708 + 0.004917 + 0.004834 + 0.005208 + 0.004708 + 0.005 + 0.004459 + 0.004875 + 0.004875 + 0.005125 + 0.005792 + 0.005625 + 0.005625 + 0.004958 + 0.004583 + 0.004583 + 0.004125 + 0.004125 + 0.00425 + 0.004292 + 0.004667 + 0.004291 + 0.004458 + 0.004167 + 0.004458 + 0.004125 + 0.004459 + 0.004 + 0.004167 + 0.004708 + 0.005583 + 0.005625 + 0.005792 + 0.0055 + 0.005166 + 0.004958 + 0.005791 + 0.005458 + 0.005042 + 0.005166 + 0.005167 + 0.00525 + 0.005959 + 0.007083 + 0.005583 + 0.005125 + 0.004917 + 0.005417 + 0.006292 + 0.007375 + 0.005541 + 0.005375 + 0.005166 + 0.005583 + 0.005083 + 0.005167 + 0.005041 + 0.005542 + 0.007083 + 0.00775 + 0.007084 + 0.006333 + 0.007458 + 0.008875 + 0.008125 + 0.007916 + 0.00575 + 0.006958 + 0.006 + 0.008708 + 0.007708 + 0.00625 + 0.006375 + 0.006667 + 0.005709 + 0.012708 + 0.003959 + 0.004792 + 0.005292 + 0.004791 + 0.004917 + 0.008958 + 0.006166 + 0.005417 + 0.005333 + 0.005041 + 0.00475 + 0.003458 + 0.003542 + 0.0035 + 0.003542 + 0.003083 + 0.00375 + 0.0035 + 0.003458 + 0.011333 + 0.003792 + 0.004333 + 0.003333 + 0.003458 + 0.003708 + 0.003541 + 0.003375 + 0.003292 + 0.002959 + 0.003167 + 0.003417 + 0.00375 + 0.003542 + 0.004 + 0.0035 + 0.003459 + 0.00325 + 0.003292 + 0.003125 + 0.003709 + 0.00325 + 0.003125 + 0.003458 + 0.004083 + 0.004458 + 0.004583 + 0.004583 + 0.004125 + 0.00325 + 0.003333 + 0.003166 + 0.0035 + 0.003791 + 0.004209 + 0.004375 + 0.004334 + 0.004125 + 0.004833 + 0.004833 + 0.004667 + 0.003875 + 0.004375 + 0.003917 + 0.00375 + 0.004375 + 0.003917 + 0.006667 + 0.005625 + 0.005667 + 0.005625 + 0.005709 + 0.005875 + 0.005709 + 0.006667 + 0.004958 + 0.004625 + 0.004417 + 0.004333 + 0.003833 + 0.004667 + 0.004709 + 0.005084 + 0.005 + 0.005334 + 0.004792 + 0.004541 + 0.004625 + 0.004708 + 0.00475 + 0.005125 + 0.005375 + 0.004208 + 0.004083 + 0.006667 + 0.006834 + 0.006959 + 0.006667 + 0.00575 + 0.005542 + 0.006125 + 0.0075 + 0.00525 + 0.005958 + 0.005792 + 0.006041 + 0.005959 + 0.00575 + 0.006834 + 0.0065 + 0.005125 + 0.004958 + 0.0045 + 0.004583 + 0.004625 + 0.004625 + 0.004 + 0.004084 + 0.004541 + 0.00425 + 0.0035 + 0.003792 + 0.004084 + 0.004125 + 0.004083 + 0.003792 + 0.004125 + 0.004708 + 0.005 + 0.004666 + 0.004542 + 0.005375 + 0.005375 + 0.004875 + 0.005166 + 0.004958 + 0.005792 + 0.004625 + 0.00475 + 0.005542 + 0.005625 + 0.006 + 0.006 + 0.005167 + 0.004542 + 0.004833 + 0.004 + 0.005083 + 0.005167 + 0.004375 + 0.0045 + 0.004667 + 0.004167 + 0.004417 + 0.004625 + 0.004542 + 0.004667 + 0.004334 + 0.00525 + 0.005375 + 0.006084 + 0.006458 + 0.006375 + 0.006042 + 0.004916 + 0.005042 + 0.005291 + 0.005083 + 0.006459 + 0.006 + 0.007542 + 0.006041 + 0.005542 + 0.005375 + 0.005417 + 0.004375 + 0.004792 + 0.00425 + 0.004375 + 0.004042 + 0.004666 + 0.005583 + 0.0055 + 0.005833 + 0.005875 + 0.005916 + 0.00575 + 0.005125 + 0.005584 + 0.005292 + 0.00425 + 0.004125 + 0.0045 + 0.005 + 0.004958 + 0.005416 + 0.006833 + 0.007375 + 0.007208 + 0.006708 + 0.006791 + 0.006292 + 0.005542 + 0.007041 + 0.006541 + 0.006375 + 0.006875 + 0.006292 + 0.006375 + 0.006125 + 0.005042 + 0.004542 + 0.004166 + 0.00475 + 0.003666 + 0.004166 + 0.004084 + 0.004875 + 0.005 + 0.003833 + 0.00375 + 0.004458 + 0.010083 + 0.004083 + 0.004042 + 0.004125 + 0.004166 + 0.003917 + 0.004167 + 0.004125 + 0.003833 + 0.004625 + 0.004291 + 0.00425 + 0.005292 + 0.005292 + 0.005833 + 0.005833 + 0.00575 + 0.005541 + 0.005833 + 0.016583 + 0.006958 + 0.005042 + 0.018791 + 0.006875 + 0.005167 + 0.005333 + 0.094792 + 0.006042 + 0.004583 + 0.00475 + 0.004459 + 0.00475 + 0.004625 + 0.005 + 0.004791 + 0.0045 + 0.005042 + 0.008917 + 0.006167 + 0.005541 + 0.006333 + 0.0155 + 0.0085 + 0.005625 + 0.005458 + 0.006292 + 0.007709 + 0.006834 + 0.006209 + 0.03525 + 0.023958 + 0.007875 + 0.00775 + 0.006625 + 0.006459 + 0.006542 + 0.006125 + 0.005375 + 0.006125 + 0.005459 + 0.005209 + 0.005542 + 0.005791 + 0.005125 + 0.005542 + 0.005125 + 0.005417 + 0.005125 + 0.005959 + 0.006084 + 0.005291 + 0.005917 + 0.005667 + 0.006958 + 0.0065 + 0.006209 + 0.007083 + 0.006333 + 0.007541 + 0.007416 + 0.006042 + 0.006417 + 0.005959 + 0.005791 + 0.005625 + 0.006541 + 0.032041 + 0.007875 + 0.007875 + 0.015583 + 0.008083 + 0.00525 + 0.005208 + 0.004417 + 0.004416 + 0.004458 + 0.005 + 0.004541 + 0.004458 + 0.003959 + 0.004375 + 0.004 + 0.003792 + 0.003958 + 0.00425 + 0.004417 + 0.004625 + 0.004292 + 0.004375 + 0.004417 + 0.0055 + 0.005333 + 0.005417 + 0.005334 + 0.004625 + 0.004667 + 0.005 + 0.004625 + 0.005 + 0.004792 + 0.004792 + 0.005125 + 0.004542 + 0.004709 + 0.004875 + 0.004125 + 0.004167 + 0.004333 + 0.0045 + 0.004667 + 0.004875 + 0.004458 + 0.004833 + 0.005292 + 0.005125 + 0.005459 + 0.005375 + 0.006292 + 0.005125 + 0.005083 + 0.004709 + 0.005167 + 0.005083 + 0.005417 + 0.007041 + 0.005542 + 0.006708 + 0.005791 + 0.005375 + 0.00575 + 0.005541 + 0.007125 + 0.006333 + 0.005917 + 0.005625 + 0.005667 + 0.004917 + 0.005084 + 0.005375 + 0.005333 + 0.013417 + 0.009625 + 0.006083 + 0.005584 + 0.005542 + 0.005625 + 0.00625 + 0.005625 + 0.004916 + 0.005083 + 0.005459 + 0.005125 + 0.004917 + 0.006792 + 0.006458 + 0.005833 + 0.005875 + 0.007208 + 0.007 + 0.006833 + 0.005958 + 0.003959 + 0.006459 + 0.007334 + 0.010334 + 0.009375 + 0.008 + 0.006375 + 0.006459 + 0.005959 + 0.005125 + 0.00525 + 0.005417 + 0.005417 + 0.004791 + 0.005417 + 0.005666 + 0.006375 + 0.005541 + 0.00475 + 0.005041 + 0.005292 + 0.0055 + 0.00525 + 0.00525 + 0.005292 + 0.014458 + 0.005541 + 0.005167 + 0.005875 + 0.00575 + 0.005208 + 0.005208 + 0.005459 + 0.005292 + 0.005375 + 0.005291 + 0.005208 + 0.005625 + 0.005417 + 0.005458 + 0.005417 + 0.005541 + 0.004875 + 0.005083 + 0.005334 + 0.005417 + 0.005667 + 0.005291 + 0.005917 + 0.005709 + 0.004834 + 0.005291 + 0.004917 + 0.005417 + 0.005625 + 0.005333 + 0.0065 + 0.006459 + 0.014041 + 0.009542 + 0.005292 + 0.004917 + 0.004875 + 0.005083 + 0.005042 + 0.014125 + 0.006583 + 0.006042 + 0.006792 + 0.006708 + 0.006791 + 0.007958 + 0.007083 + 0.005542 + 0.004625 + 0.005084 + 0.004792 + 0.005875 + 0.006167 + 0.00525 + 0.005542 + 0.005375 + 0.00575 + 0.005833 + 0.006417 + 0.005375 + 0.007334 + 0.013334 + 0.006375 + 0.006667 + 0.007875 + 0.008791 + 0.009167 + 0.008292 + 0.008458 + 0.007208 + 0.006291 + 0.011167 + 0.008708 + 0.008542 + 0.006667 + 0.010417 + 0.007834 + 0.006292 + 0.008375 + 0.007958 + 0.007416 + 0.007208 + 0.00575 + 0.005417 + 0.005542 + 0.011791 + 0.00475 + 0.004667 + 0.004834 + 0.004125 + 0.005625 + 0.005917 + 0.005 + 0.0055 + 0.005833 + 0.005084 + 0.004917 + 0.004792 + 0.005417 + 0.005083 + 0.004667 + 0.0045 + 0.004375 + 0.004542 + 0.004584 + 0.004709 + 0.005333 + 0.004708 + 0.004791 + 0.004542 + 0.004625 + 0.0045 + 0.004875 + 0.004333 + 0.005208 + 0.004709 + 0.006 + 0.00575 + 0.004375 + 0.00425 + 0.0045 + 0.004625 + 0.005208 + 0.006167 + 0.005625 + 0.017209 + 0.009125 + 0.0055 + 0.00625 + 0.005917 + 0.006542 + 0.006375 + 0.005 + 0.00475 + 0.00475 + 0.004708 + 0.004625 + 0.00525 + 0.006833 + 0.0065 + 0.007166 + 0.006459 + 0.006042 + 0.00525 + 0.005958 + 0.008084 + 0.007166 + 0.0065 + 0.005916 + 0.006292 + 0.005708 + 0.006125 + 0.005833 + 0.006583 + 0.005708 + 0.005041 + 0.005125 + 0.004708 + 0.004625 + 0.004542 + 0.004916 + 0.004792 + 0.005375 + 0.006875 + 0.005625 + 0.005875 + 0.006375 + 0.006959 + 0.007 + 0.00725 + 0.007375 + 0.007917 + 0.006458 + 0.006292 + 0.005459 + 0.006166 + 0.006792 + 0.006542 + 0.005458 + 0.007083 + 0.00625 + 0.006125 + 0.005167 + 0.00575 + 0.004959 + 0.004167 + 0.004208 + 0.004625 + 0.004291 + 0.004583 + 0.00475 + 0.004791 + 0.004167 + 0.004584 + 0.004833 + 0.004708 + 0.0045 + 0.004333 + 0.004208 + 0.004333 + 0.004458 + 0.004042 + 0.00425 + 0.004459 + 0.00475 + 0.004167 + 0.004458 + 0.004208 + 0.003917 + 0.004292 + 0.004833 + 0.004625 + 0.004542 + 0.004291 + 0.004459 + 0.004333 + 0.004166 + 0.004084 + 0.0045 + 0.004625 + 0.004667 + 0.004292 + 0.004042 + 0.0045 + 0.004833 + 0.004458 + 0.00425 + 0.0045 + 0.004167 + 0.004292 + 0.004542 + 0.004375 + 0.004375 + 0.004375 + 0.004792 + 0.004916 + 0.004417 + 0.004416 + 0.006417 + 0.0055 + 0.00625 + 0.006417 + 0.006083 + 0.006 + 0.005542 + 0.0165 + 0.006708 + 0.00525 + 0.00525 + 0.005959 + 0.006042 + 0.006208 + 0.005458 + 0.005791 + 0.005583 + 0.00525 + 0.004292 + 0.004625 + 0.004625 + 0.004583 + 0.004709 + 0.004834 + 0.005333 + 0.005375 + 0.007792 + 0.006625 + 0.005958 + 0.007875 + 0.007 + 0.006917 + 0.007083 + 0.006458 + 0.007333 + 0.006958 + 0.00525 + 0.006625 + 0.006958 + 0.006917 + 0.006458 + 0.0075 + 0.007167 + 0.006959 + 0.006417 + 0.005125 + 0.004416 + 0.005417 + 0.0055 + 0.006083 + 0.006 + 0.004834 + 0.004834 + 0.004875 + 0.005209 + 0.004125 + 0.004792 + 0.004458 + 0.004541 + 0.005042 + 0.004791 + 0.0045 + 0.027041 + 0.005875 + 0.005542 + 0.005291 + 0.004708 + 0.004583 + 0.004542 + 0.00475 + 0.004292 + 0.004708 + 0.0045 + 0.004625 + 0.00475 + 0.004458 + 0.004625 + 0.005125 + 0.005084 + 0.005458 + 0.005333 + 0.005291 + 0.005459 + 0.005209 + 0.005291 + 0.005458 + 0.00525 + 0.006 + 0.005125 + 0.005459 + 0.00525 + 0.0055 + 0.005791 + 0.005583 + 0.004583 + 0.004833 + 0.004583 + 0.004833 + 0.004792 + 0.004625 + 0.004708 + 0.004708 + 0.013375 + 0.006792 + 0.006667 + 0.0065 + 0.00625 + 0.006667 + 0.006667 + 0.007375 + 0.006375 + 0.005666 + 0.005791 + 0.005583 + 0.005333 + 0.005208 + 0.005375 + 0.005041 + 0.004375 + 0.004417 + 0.004209 + 0.003667 + 0.00425 + 0.004666 + 0.004333 + 0.004583 + 0.004334 + 0.004375 + 0.004625 + 0.009792 + 0.006875 + 0.007166 + 0.006875 + 0.006417 + 0.006292 + 0.006625 + 0.00525 + 0.003875 + 0.006666 + 0.007208 + 0.006334 + 0.006709 + 0.006625 + 0.006625 + 0.006958 + 0.009458 + 0.005334 + 0.004875 + 0.005167 + 0.005209 + 0.005334 + 0.004792 + 0.005042 + 0.005291 + 0.003833 + 0.004667 + 0.004083 + 0.004333 + 0.004292 + 0.00425 + 0.003917 + 0.003958 + 0.004083 + 0.003667 + 0.003916 + 0.004167 + 0.00425 + 0.003959 + 0.00375 + 0.00425 + 0.004083 + 0.00425 + 0.004417 + 0.004417 + 0.003542 + 0.005041 + 0.004959 + 0.005125 + 0.00475 + 0.005041 + 0.004958 + 0.005 + 0.004875 + 0.004875 + 0.004875 + 0.004125 + 0.004125 + 0.003916 + 0.003625 + 0.004083 + 0.004166 + 0.004459 + 0.003959 + 0.004542 + 0.004416 + 0.004125 + 0.004458 + 0.004167 + 0.004167 + 0.00425 + 0.004083 + 0.004167 + 0.003834 + 0.005292 + 0.005541 + 0.00675 + 0.006667 + 0.006417 + 0.006333 + 0.006125 + 0.005292 + 0.0055 + 0.005917 + 0.004833 + 0.004625 + 0.004416 + 0.004459 + 0.004375 + 0.00575 + 0.005583 + 0.005333 + 0.005667 + 0.005333 + 0.005208 + 0.005125 + 0.005375 + 0.005709 + 0.00475 + 0.004792 + 0.00525 + 0.005417 + 0.006792 + 0.00675 + 0.006542 + 0.006291 + 0.006208 + 0.006792 + 0.005708 + 0.006792 + 0.007166 + 0.006958 + 0.006417 + 0.006 + 0.006583 + 0.005583 + 0.004708 + 0.004542 + 0.004333 + 0.005292 + 0.005334 + 0.005208 + 0.004916 + 0.004583 + 0.005833 + 0.004042 + 0.003958 + 0.004583 + 0.004791 + 0.004584 + 0.004083 + 0.003791 + 0.003959 + 0.003833 + 0.004042 + 0.003791 + 0.003875 + 0.004375 + 0.005041 + 0.004875 + 0.005875 + 0.005209 + 0.004917 + 0.004625 + 0.00475 + 0.00475 + 0.004792 + 0.004042 + 0.004416 + 0.004167 + 0.004542 + 0.004834 + 0.005125 + 0.004917 + 0.004084 + 0.003958 + 0.00425 + 0.004125 + 0.004208 + 0.004083 + 0.003875 + 0.003667 + 0.00375 + 0.00425 + 0.005208 + 0.007041 + 0.004042 + 0.005583 + 0.005417 + 0.005292 + 0.004958 + 0.005458 + 0.005416 + 0.00575 + 0.006541 + 0.006167 + 0.005167 + 0.005125 + 0.0065 + 0.007167 + 0.006333 + 0.005791 + 0.005417 + 0.005834 + 0.005209 + 0.005459 + 0.004959 + 0.004917 + 0.004333 + 0.004709 + 0.004584 + 0.004167 + 0.004083 + 0.003917 + 0.005667 + 0.005709 + 0.005375 + 0.005375 + 0.00575 + 0.005625 + 0.004708 + 0.006541 + 0.006542 + 0.005917 + 0.005666 + 0.006208 + 0.006875 + 0.006291 + 0.005417 + 0.007667 + 0.006833 + 0.006708 + 0.007333 + 0.006666 + 0.013792 + 0.006375 + 0.006583 + 0.005083 + 0.004666 + 0.004458 + 0.004042 + 0.004584 + 0.004458 + 0.00425 + 0.00525 + 0.00425 + 0.005375 + 0.005584 + 0.004333 + 0.004583 + 0.004583 + 0.004709 + 0.004542 + 0.004083 + 0.003875 + 0.004334 + 0.004459 + 0.004542 + 0.004292 + 0.004334 + 0.004 + 0.004084 + 0.004209 + 0.004416 + 0.004875 + 0.004458 + 0.005125 + 0.0045 + 0.005167 + 0.004833 + 0.004166 + 0.004375 + 0.004958 + 0.005375 + 0.005625 + 0.004958 + 0.00475 + 0.004958 + 0.004417 + 0.004792 + 0.004791 + 0.004792 + 0.005125 + 0.0055 + 0.016375 + 0.013 + 0.014042 + 0.005458 + 0.006166 + 0.006 + 0.005292 + 0.006416 + 0.00575 + 0.0055 + 0.007167 + 0.006625 + 0.005959 + 0.006041 + 0.014084 + 0.007958 + 0.006625 + 0.006334 + 0.00525 + 0.006125 + 0.004625 + 0.004541 + 0.004917 + 0.004959 + 0.00475 + 0.005042 + 0.004583 + 0.004875 + 0.005291 + 0.005292 + 0.006792 + 0.005833 + 0.006166 + 0.00525 + 0.005416 + 0.006417 + 0.006 + 0.007125 + 0.007166 + 0.007667 + 0.007375 + 0.0085 + 0.007166 + 0.006542 + 0.005917 + 0.00725 + 0.006667 + 0.006417 + 0.006959 + 0.006084 + 0.006542 + 0.006416 + 0.006833 + 0.004875 + 0.003958 + 0.004458 + 0.00425 + 0.004208 + 0.003958 + 0.004125 + 0.0045 + 0.004334 + 0.004167 + 0.003958 + 0.004916 + 0.004375 + 0.00425 + 0.004375 + 0.004458 + 0.004458 + 0.004 + 0.004125 + 0.004083 + 0.004042 + 0.004333 + 0.004125 + 0.004375 + 0.004083 + 0.003875 + 0.00375 + 0.004458 + 0.004375 + 0.004 + 0.0045 + 0.004375 + 0.004291 + 0.004209 + 0.004375 + 0.004166 + 0.004334 + 0.003917 + 0.004458 + 0.004375 + 0.00425 + 0.004083 + 0.00425 + 0.004417 + 0.004083 + 0.004 + 0.004333 + 0.00375 + 0.004334 + 0.00475 + 0.004167 + 0.004417 + 0.005416 + 0.005 + 0.004666 + 0.005167 + 0.004542 + 0.005583 + 0.006 + 0.006167 + 0.005916 + 0.006917 + 0.00725 + 0.00675 + 0.006583 + 0.005792 + 0.0055 + 0.006375 + 0.005916 + 0.004625 + 0.005083 + 0.005666 + 0.0055 + 0.005542 + 0.00575 + 0.006416 + 0.006083 + 0.005334 + 0.005209 + 0.004 + 0.004583 + 0.00425 + 0.004625 + 0.00475 + 0.005125 + 0.005416 + 0.005833 + 0.006875 + 0.006666 + 0.006917 + 0.006791 + 0.006584 + 0.005584 + 0.006834 + 0.006083 + 0.00625 + 0.007 + 0.006666 + 0.0065 + 0.006292 + 0.005209 + 0.005 + 0.00475 + 0.004333 + 0.00375 + 0.003834 + 0.004042 + 0.003916 + 0.006583 + 0.004125 + 0.003833 + 0.004709 + 0.004291 + 0.004125 + 0.004166 + 0.003917 + 0.004666 + 0.004292 + 0.00425 + 0.00425 + 0.005375 + 0.005459 + 0.005292 + 0.004875 + 0.004709 + 0.004417 + 0.0045 + 0.005 + 0.004333 + 0.004458 + 0.004334 + 0.0045 + 0.004666 + 0.004458 + 0.004292 + 0.005125 + 0.005458 + 0.005292 + 0.005125 + 0.005292 + 0.00425 + 0.004667 + 0.005042 + 0.005208 + 0.005167 + 0.004667 + 0.004083 + 0.004125 + 0.004041 + 0.004709 + 0.005 + 0.005208 + 0.005125 + 0.005875 + 0.005333 + 0.004167 + 0.004084 + 0.004541 + 0.005167 + 0.005166 + 0.005167 + 0.006 + 0.006958 + 0.00675 + 0.006291 + 0.00575 + 0.005708 + 0.006166 + 0.005666 + 0.005375 + 0.005875 + 0.005709 + 0.005417 + 0.005583 + 0.005458 + 0.005583 + 0.005542 + 0.005542 + 0.005667 + 0.005084 + 0.006917 + 0.005209 + 0.005959 + 0.005792 + 0.00675 + 0.006708 + 0.006208 + 0.007 + 0.007625 + 0.011458 + 0.007583 + 0.006541 + 0.005667 + 0.006125 + 0.006333 + 0.006875 + 0.007791 + 0.006208 + 0.00525 + 0.005209 + 0.004625 + 0.004208 + 0.004167 + 0.003958 + 0.004041 + 0.004167 + 0.00375 + 0.005167 + 0.0045 + 0.00475 + 0.0045 + 0.004458 + 0.004917 + 0.004084 + 0.003833 + 0.004208 + 0.00475 + 0.004125 + 0.004542 + 0.004625 + 0.00475 + 0.00425 + 0.004083 + 0.00425 + 0.004125 + 0.004125 + 0.004 + 0.00425 + 0.004625 + 0.003667 + 0.004208 + 0.004583 + 0.004333 + 0.0045 + 0.004541 + 0.004625 + 0.003708 + 0.004584 + 0.004625 + 0.004625 + 0.004375 + 0.00425 + 0.004833 + 0.004167 + 0.003959 + 0.004 + 0.004208 + 0.00425 + 0.004083 + 0.004375 + 0.011458 + 0.0045 + 0.0045 + 0.004417 + 0.004667 + 0.004792 + 0.005 + 0.005625 + 0.008167 + 0.006834 + 0.006708 + 0.006375 + 0.006459 + 0.00675 + 0.007375 + 0.005291 + 0.005292 + 0.005083 + 0.005084 + 0.005 + 0.0055 + 0.004416 + 0.004458 + 0.004333 + 0.004125 + 0.003792 + 0.00475 + 0.004375 + 0.004541 + 0.00425 + 0.004583 + 0.004375 + 0.004167 + 0.004334 + 0.004 + 0.006125 + 0.00625 + 0.005583 + 0.006125 + 0.00575 + 0.005792 + 0.007083 + 0.005417 + 0.006208 + 0.005875 + 0.006458 + 0.005625 + 0.005125 + 0.005959 + 0.006417 + 0.00625 + 0.00475 + 0.005166 + 0.004125 + 0.003875 + 0.003833 + 0.003667 + 0.003791 + 0.003875 + 0.004292 + 0.004291 + 0.004209 + 0.003834 + 0.00375 + 0.004167 + 0.004166 + 0.004417 + 0.00375 + 0.003834 + 0.003916 + 0.004083 + 0.003917 + 0.004125 + 0.003791 + 0.004167 + 0.004167 + 0.003625 + 0.003667 + 0.003916 + 0.003792 + 0.003875 + 0.003709 + 0.003916 + 0.004084 + 0.003834 + 0.004125 + 0.003791 + 0.004 + 0.003958 + 0.004084 + 0.003666 + 0.0045 + 0.004875 + 0.005042 + 0.0045 + 0.003917 + 0.003833 + 0.004292 + 0.003375 + 0.004333 + 0.004334 + 0.004625 + 0.004292 + 0.004333 + 0.00375 + 0.004417 + 0.005125 + 0.005166 + 0.005917 + 0.007584 + 0.006292 + 0.005334 + 0.005625 + 0.006042 + 0.005416 + 0.006083 + 0.005041 + 0.005125 + 0.004541 + 0.00425 + 0.004833 + 0.004917 + 0.005208 + 0.00525 + 0.00575 + 0.00625 + 0.005541 + 0.004667 + 0.005042 + 0.004416 + 0.004583 + 0.004584 + 0.00525 + 0.005542 + 0.005083 + 0.007208 + 0.006792 + 0.006084 + 0.006417 + 0.006583 + 0.006125 + 0.006667 + 0.005125 + 0.005708 + 0.006041 + 0.005833 + 0.006 + 0.006 + 0.006083 + 0.006125 + 0.005792 + 0.005084 + 0.00475 + 0.0045 + 0.004083 + 0.003666 + 0.003792 + 0.003458 + 0.004208 + 0.004083 + 0.004167 + 0.003584 + 0.003834 + 0.00375 + 0.004292 + 0.00425 + 0.00375 + 0.00375 + 0.004042 + 0.004125 + 0.004042 + 0.00375 + 0.004417 + 0.00375 + 0.004042 + 0.003834 + 0.004041 + 0.004125 + 0.0035 + 0.003292 + 0.0035 + 0.003917 + 0.004042 + 0.004209 + 0.004084 + 0.003916 + 0.003667 + 0.004208 + 0.003875 + 0.003958 + 0.003667 + 0.004042 + 0.004083 + 0.00425 + 0.003917 + 0.004 + 0.003875 + 0.004041 + 0.003625 + 0.0045 + 0.004625 + 0.0045 + 0.00425 + 0.004625 + 0.004208 + 0.004125 + 0.004125 + 0.00425 + 0.005458 + 0.005958 + 0.00625 + 0.00575 + 0.005583 + 0.005334 + 0.006042 + 0.005916 + 0.005375 + 0.004875 + 0.005 + 0.005208 + 0.004458 + 0.004541 + 0.004167 + 0.004334 + 0.004625 + 0.004542 + 0.004375 + 0.00425 + 0.00425 + 0.004375 + 0.004208 + 0.004083 + 0.004458 + 0.004167 + 0.004167 + 0.005875 + 0.006584 + 0.005875 + 0.005917 + 0.0055 + 0.004709 + 0.006084 + 0.00575 + 0.00525 + 0.005917 + 0.006083 + 0.005541 + 0.005417 + 0.006125 + 0.006 + 0.006083 + 0.004959 + 0.004542 + 0.004834 + 0.003625 + 0.004042 + 0.003833 + 0.003666 + 0.004375 + 0.004167 + 0.004083 + 0.003833 + 0.004083 + 0.004375 + 0.004333 + 0.003958 + 0.004 + 0.00725 + 0.004 + 0.003958 + 0.004 + 0.004 + 0.004 + 0.004084 + 0.00425 + 0.003833 + 0.003833 + 0.003916 + 0.004333 + 0.003958 + 0.003833 + 0.004042 + 0.003708 + 0.003584 + 0.003625 + 0.003792 + 0.003958 + 0.00375 + 0.0035 + 0.005709 + 0.005 + 0.004417 + 0.00425 + 0.004084 + 0.004375 + 0.004875 + 0.004917 + 0.00525 + 0.005 + 0.0055 + 0.005625 + 0.005666 + 0.005291 + 0.005333 + 0.0045 + 0.005458 + 0.005125 + 0.005084 + 0.005834 + 0.007375 + 0.006875 + 0.006791 + 0.0055 + 0.004833 + 0.004625 + 0.00475 + 0.004666 + 0.004375 + 0.004583 + 0.006459 + 0.006 + 0.005333 + 0.005375 + 0.005167 + 0.005625 + 0.005459 + 0.005375 + 0.005375 + 0.00475 + 0.004292 + 0.004041 + 0.004375 + 0.004625 + 0.004208 + 0.004333 + 0.004958 + 0.009917 + 0.00625 + 0.006125 + 0.006375 + 0.006 + 0.005875 + 0.00625 + 0.006 + 0.006709 + 0.0065 + 0.0065 + 0.006542 + 0.006625 + 0.00625 + 0.006125 + 0.005208 + 0.005 + 0.005041 + 0.004542 + 0.004459 + 0.004 + 0.004458 + 0.004958 + 0.005125 + 0.003958 + 0.003917 + 0.004 + 0.004167 + 0.004084 + 0.00375 + 0.003958 + 0.00375 + 0.003916 + 0.003792 + 0.004084 + 0.004041 + 0.00425 + 0.004333 + 0.005042 + 0.005167 + 0.005583 + 0.005375 + 0.005417 + 0.004625 + 0.004375 + 0.004292 + 0.004209 + 0.003875 + 0.004083 + 0.004083 + 0.00425 + 0.004292 + 0.004083 + 0.004542 + 0.005166 + 0.005958 + 0.005833 + 0.006 + 0.005292 + 0.005083 + 0.004542 + 0.005167 + 0.004792 + 0.005958 + 0.005041 + 0.007083 + 0.006625 + 0.006375 + 0.005292 + 0.005458 + 0.004958 + 0.005417 + 0.005375 + 0.006042 + 0.005958 + 0.005958 + 0.00775 + 0.00675 + 0.006667 + 0.005917 + 0.005292 + 0.005583 + 0.005459 + 0.005417 + 0.005666 + 0.006375 + 0.006541 + 0.006042 + 0.005709 + 0.005375 + 0.005084 + 0.005166 + 0.005708 + 0.005542 + 0.006958 + 0.006333 + 0.006292 + 0.005583 + 0.00575 + 0.00575 + 0.0075 + 0.0075 + 0.007417 + 0.006833 + 0.007916 + 0.007125 + 0.0065 + 0.005834 + 0.00625 + 0.006125 + 0.006083 + 0.006458 + 0.008958 + 0.041792 + 0.013083 + 0.006458 + 0.005666 + 0.0055 + 0.005125 + 0.005 + 0.004958 + 0.005083 + 0.004333 + 0.005667 + 0.004167 + 0.003958 + 0.004583 + 0.005125 + 0.005125 + 0.00525 + 0.005125 + 0.0045 + 0.004708 + 0.0045 + 0.004666 + 0.004167 + 0.004333 + 0.00475 + 0.004416 + 0.004 + 0.003833 + 0.00425 + 0.004125 + 0.004333 + 0.00425 + 0.003917 + 0.004375 + 0.004083 + 0.004 + 0.003958 + 0.004167 + 0.00375 + 0.003708 + 0.004333 + 0.004042 + 0.003916 + 0.004125 + 0.009833 + 0.004334 + 0.004 + 0.004209 + 0.004334 + 0.004084 + 0.004542 + 0.004875 + 0.004875 + 0.004625 + 0.0045 + 0.004417 + 0.004416 + 0.00425 + 0.005416 + 0.005458 + 0.009792 + 0.007792 + 0.007167 + 0.006959 + 0.007209 + 0.007625 + 0.007083 + 0.007708 + 0.006708 + 0.005834 + 0.005625 + 0.005542 + 0.004959 + 0.004875 + 0.004834 + 0.004834 + 0.005083 + 0.005583 + 0.005041 + 0.004917 + 0.005416 + 0.005 + 0.005 + 0.00575 + 0.004666 + 0.004875 + 0.004708 + 0.006375 + 0.007459 + 0.008292 + 0.016875 + 0.017 + 0.00925 + 0.007667 + 0.006375 + 0.005625 + 0.005916 + 0.0055 + 0.005708 + 0.007417 + 0.007084 + 0.0065 + 0.006792 + 0.005542 + 0.005333 + 0.005125 + 0.005958 + 0.004958 + 0.004375 + 0.004125 + 0.004417 + 0.006084 + 0.004917 + 0.005625 + 0.005875 + 0.00475 + 0.005291 + 0.005 + 0.005167 + 0.00525 + 0.005 + 0.005375 + 0.004583 + 0.004708 + 0.004291 + 0.004542 + 0.004666 + 0.003833 + 0.004625 + 0.004 + 0.004125 + 0.004333 + 0.004375 + 0.004916 + 0.005458 + 0.00575 + 0.005166 + 0.005459 + 0.005625 + 0.005542 + 0.005208 + 0.005 + 0.005417 + 0.004958 + 0.005375 + 0.005709 + 0.005541 + 0.005 + 0.005125 + 0.005084 + 0.004791 + 0.005458 + 0.005375 + 0.004834 + 0.00575 + 0.005708 + 0.00625 + 0.006208 + 0.006958 + 0.006 + 0.007167 + 0.006916 + 0.006584 + 0.006209 + 0.005709 + 0.007959 + 0.007125 + 0.006875 + 0.006208 + 0.005 + 0.00525 + 0.005 + 0.005375 + 0.005583 + 0.009708 + 0.004458 + 0.005334 + 0.006333 + 0.005667 + 0.00575 + 0.006208 + 0.00725 + 0.005625 + 0.006709 + 0.008708 + 0.006875 + 0.007416 + 0.009042 + 0.007292 + 0.00475 + 0.004583 + 0.005833 + 0.005792 + 0.005125 + 0.0055 + 0.004917 + 0.005209 + 0.00525 + 0.004125 + 0.005625 + 0.00525 + 0.005958 + 0.005667 + 0.0045 + 0.004541 + 0.003459 + 0.003084 + 0.003375 + 0.003708 + 0.003667 + 0.00375 + 0.004542 + 0.003292 + 0.003 + 0.003333 + 0.004 + 0.003459 + 0.003542 + 0.003917 + 0.004042 + 0.003375 + 0.003667 + 0.003333 + 0.002958 + 0.003625 + 0.0035 + 0.003417 + 0.003209 + 0.003375 + 0.003209 + 0.00325 + 0.003334 + 0.003916 + 0.003667 + 0.003666 + 0.00325 + 0.003667 + 0.003209 + 0.003416 + 0.0035 + 0.003042 + 0.003458 + 0.003375 + 0.00325 + 0.003959 + 0.003709 + 0.00425 + 0.003584 + 0.003416 + 0.003292 + 0.003459 + 0.003959 + 0.003584 + 0.003625 + 0.004 + 0.003833 + 0.003666 + 0.003875 + 0.003542 + 0.003625 + 0.003667 + 0.007 + 0.006208 + 0.005667 + 0.005166 + 0.004459 + 0.004916 + 0.006792 + 0.005375 + 0.005416 + 0.004583 + 0.004667 + 0.004417 + 0.004375 + 0.005292 + 0.004917 + 0.004833 + 0.003834 + 0.003916 + 0.003542 + 0.004 + 0.003917 + 0.00425 + 0.004959 + 0.005459 + 0.005208 + 0.0055 + 0.00825 + 0.007041 + 0.006458 + 0.006292 + 0.006791 + 0.00675 + 0.0075 + 0.007958 + 0.006083 + 0.007208 + 0.007 + 0.006583 + 0.00675 + 0.00625 + 0.008083 + 0.006292 + 0.005542 + 0.00525 + 0.00525 + 0.005834 + 0.005208 + 0.005791 + 0.005417 + 0.005417 + 0.005708 + 0.004708 + 0.005166 + 0.006083 + 0.00525 + 0.005083 + 0.00475 + 0.00475 + 0.004875 + 0.004334 + 0.004959 + 0.004875 + 0.003959 + 0.004167 + 0.003917 + 0.004 + 0.003834 + 0.003792 + 0.004417 + 0.003958 + 0.003958 + 0.003875 + 0.00425 + 0.004334 + 0.004125 + 0.003958 + 0.004042 + 0.003792 + 0.004334 + 0.004333 + 0.004292 + 0.004292 + 0.003792 + 0.003708 + 0.004167 + 0.004208 + 0.004 + 0.004417 + 0.004167 + 0.003791 + 0.004083 + 0.004417 + 0.004167 + 0.004542 + 0.004458 + 0.004666 + 0.004083 + 0.004625 + 0.004458 + 0.00475 + 0.005417 + 0.006625 + 0.006458 + 0.0065 + 0.005833 + 0.005958 + 0.005417 + 0.004166 + 0.004083 + 0.004667 + 0.004375 + 0.004334 + 0.005709 + 0.005125 + 0.004959 + 0.004959 + 0.005208 + 0.004916 + 0.005167 + 0.0055 + 0.005292 + 0.004792 + 0.004541 + 0.004583 + 0.004042 + 0.004708 + 0.004458 + 0.005709 + 0.004792 + 0.006541 + 0.006958 + 0.006834 + 0.006375 + 0.006292 + 0.005542 + 0.006541 + 0.00775 + 0.006916 + 0.006375 + 0.005958 + 0.006458 + 0.006 + 0.007416 + 0.005208 + 0.00525 + 0.005542 + 0.005333 + 0.00475 + 0.004917 + 0.005458 + 0.004458 + 0.004791 + 0.003833 + 0.004208 + 0.004875 + 0.004792 + 0.004875 + 0.004875 + 0.005042 + 0.005041 + 0.005334 + 0.005291 + 0.005125 + 0.004917 + 0.00525 + 0.004792 + 0.004792 + 0.005041 + 0.004875 + 0.004458 + 0.004833 + 0.004959 + 0.004458 + 0.003958 + 0.004375 + 0.004208 + 0.003833 + 0.004083 + 0.004042 + 0.003875 + 0.004125 + 0.004584 + 0.004875 + 0.005 + 0.004916 + 0.005 + 0.004875 + 0.004542 + 0.004375 + 0.004875 + 0.005167 + 0.005042 + 0.004583 + 0.004584 + 0.004542 + 0.004542 + 0.004042 + 0.004833 + 0.005334 + 0.005583 + 0.006666 + 0.006625 + 0.008375 + 0.014 + 0.006792 + 0.005292 + 0.005041 + 0.0055 + 0.004208 + 0.0045 + 0.005625 + 0.005958 + 0.005542 + 0.005209 + 0.006084 + 0.005042 + 0.004875 + 0.005292 + 0.005167 + 0.00525 + 0.004834 + 0.005416 + 0.005542 + 0.005834 + 0.006875 + 0.006459 + 0.005542 + 0.006625 + 0.008334 + 0.005833 + 0.005333 + 0.0055 + 0.005542 + 0.005208 + 0.0045 + 0.004917 + 0.026375 + 0.016041 + 0.008708 + 0.006458 + 0.014292 + 0.011375 + 0.010125 + 0.0065 + 0.006292 + 0.005166 + 0.004958 + 0.005042 + 0.005875 + 0.0055 + 0.005333 + 0.005792 + 0.005125 + 0.005417 + 0.005584 + 0.005417 + 0.005208 + 0.005333 + 0.00475 + 0.003666 + 0.003792 + 0.004042 + 0.004208 + 0.004917 + 0.005208 + 0.006208 + 0.0055 + 0.005666 + 0.005459 + 0.004959 + 0.004958 + 0.004708 + 0.005417 + 0.005125 + 0.005083 + 0.005375 + 0.005 + 0.004958 + 0.0045 + 0.005 + 0.0055 + 0.005458 + 0.005333 + 0.005583 + 0.005459 + 0.00525 + 0.004917 + 0.005125 + 0.005042 + 0.005208 + 0.004916 + 0.006042 + 0.006084 + 0.006292 + 0.006666 + 0.006292 + 0.005417 + 0.005166 + 0.005708 + 0.005708 + 0.00625 + 0.006292 + 0.007792 + 0.016042 + 0.00625 + 0.006375 + 0.006333 + 0.007208 + 0.005833 + 0.0055 + 0.005709 + 0.007709 + 0.006834 + 0.006375 + 0.005083 + 0.00525 + 0.025958 + 0.005791 + 0.005417 + 0.004917 + 0.004875 + 0.00475 + 0.004958 + 0.0045 + 0.004875 + 0.004708 + 0.005625 + 0.006625 + 0.008083 + 0.007708 + 0.007042 + 0.006583 + 0.005958 + 0.006583 + 0.006042 + 0.004833 + 0.007084 + 0.006458 + 0.007 + 0.006541 + 0.007083 + 0.006708 + 0.008459 + 0.006292 + 0.005042 + 0.005042 + 0.004375 + 0.004584 + 0.004833 + 0.0045 + 0.003708 + 0.004125 + 0.004417 + 0.003667 + 0.00425 + 0.004291 + 0.004083 + 0.004209 + 0.004 + 0.004334 + 0.003792 + 0.004042 + 0.003959 + 0.004041 + 0.004167 + 0.003916 + 0.004083 + 0.004292 + 0.004584 + 0.009334 + 0.004584 + 0.005125 + 0.005625 + 0.006291 + 0.005333 + 0.0055 + 0.005791 + 0.0055 + 0.005541 + 0.004875 + 0.004583 + 0.004417 + 0.005167 + 0.005125 + 0.005333 + 0.006333 + 0.005959 + 0.032334 + 0.006208 + 0.026292 + 0.008208 + 0.006583 + 0.00625 + 0.005583 + 0.005 + 0.005083 + 0.005292 + 0.005667 + 0.004834 + 0.00525 + 0.005708 + 0.00675 + 0.018791 + 0.017916 + 0.009041 + 0.007625 + 0.007417 + 0.00775 + 0.006166 + 0.005542 + 0.006 + 0.005958 + 0.005708 + 0.005708 + 0.007084 + 0.006458 + 0.005708 + 0.006375 + 0.005458 + 0.005583 + 0.005333 + 0.005208 + 0.0055 + 0.005584 + 0.005958 + 0.005833 + 0.00575 + 0.006584 + 0.006834 + 0.008209 + 0.007917 + 0.006625 + 0.006708 + 0.006375 + 0.006541 + 0.006125 + 0.007125 + 0.009083 + 0.006916 + 0.006583 + 0.005875 + 0.007083 + 0.006208 + 0.007833 + 0.007125 + 0.005875 + 0.005917 + 0.019042 + 0.004792 + 0.00475 + 0.005375 + 0.005458 + 0.004417 + 0.004625 + 0.005666 + 0.006625 + 0.005208 + 0.005708 + 0.005542 + 0.004833 + 0.005166 + 0.004917 + 0.004917 + 0.004125 + 0.004042 + 0.004125 + 0.00425 + 0.007041 + 0.006125 + 0.009791 + 0.006917 + 0.008334 + 0.008667 + 0.006666 + 0.005958 + 0.00675 + 0.006917 + 0.006584 + 0.006333 + 0.005916 + 0.0055 + 0.005167 + 0.005333 + 0.005833 + 0.005583 + 0.005458 + 0.006209 + 0.006125 + 0.005083 + 0.005542 + 0.005167 + 0.006875 + 0.007666 + 0.00525 + 0.005833 + 0.005917 + 0.006416 + 0.005334 + 0.005 + 0.005 + 0.00525 + 0.005416 + 0.005792 + 0.008541 + 0.007625 + 0.00675 + 0.006791 + 0.006667 + 0.006333 + 0.005917 + 0.005791 + 0.005708 + 0.00525 + 0.004583 + 0.009875 + 0.006333 + 0.005666 + 0.005667 + 0.005958 + 0.013208 + 0.0065 + 0.004625 + 0.004541 + 0.004792 + 0.005625 + 0.004958 + 0.004791 + 0.004625 + 0.005541 + 0.005792 + 0.007125 + 0.006959 + 0.007667 + 0.007666 + 0.006542 + 0.006375 + 0.006666 + 0.006833 + 0.00775 + 0.006875 + 0.006375 + 0.006084 + 0.006 + 0.005959 + 0.004792 + 0.004084 + 0.004458 + 0.004625 + 0.004542 + 0.004625 + 0.00425 + 0.004875 + 0.004083 + 0.004375 + 0.004375 + 0.004375 + 0.004541 + 0.004208 + 0.004333 + 0.004417 + 0.004875 + 0.004875 + 0.004667 + 0.004375 + 0.004167 + 0.004375 + 0.004292 + 0.005 + 0.007416 + 0.004917 + 0.004291 + 0.004334 + 0.004375 + 0.0045 + 0.004541 + 0.004542 + 0.004416 + 0.004833 + 0.004625 + 0.004542 + 0.004208 + 0.0045 + 0.0045 + 0.004625 + 0.004667 + 0.004833 + 0.004292 + 0.004417 + 0.0045 + 0.004375 + 0.004667 + 0.004708 + 0.005208 + 0.007833 + 0.006167 + 0.005792 + 0.005958 + 0.005833 + 0.006125 + 0.006333 + 0.006166 + 0.005708 + 0.005292 + 0.005292 + 0.006208 + 0.007042 + 0.007084 + 0.007375 + 0.007167 + 0.006042 + 0.005541 + 0.005458 + 0.004958 + 0.004625 + 0.00475 + 0.006208 + 0.005792 + 0.005833 + 0.00625 + 0.005709 + 0.005958 + 0.005583 + 0.005916 + 0.005042 + 0.004625 + 0.004834 + 0.004917 + 0.0045 + 0.004875 + 0.005458 + 0.015833 + 0.007833 + 0.0065 + 0.00625 + 0.010167 + 0.005875 + 0.005042 + 0.006 + 0.007 + 0.00775 + 0.007125 + 0.006917 + 0.006333 + 0.007416 + 0.00725 + 0.011875 + 0.004541 + 0.004167 + 0.004667 + 0.005083 + 0.005875 + 0.005208 + 0.013875 + 0.004667 + 0.004417 + 0.005291 + 0.005584 + 0.005208 + 0.004958 + 0.00425 + 0.01175 + 0.004375 + 0.004333 + 0.004333 + 0.004417 + 0.004666 + 0.004334 + 0.00425 + 0.004333 + 0.00425 + 0.00425 + 0.004417 + 0.004417 + 0.004084 + 0.004125 + 0.004917 + 0.005083 + 0.004875 + 0.004916 + 0.005583 + 0.005708 + 0.00575 + 0.005458 + 0.005291 + 0.00525 + 0.005208 + 0.005167 + 0.00475 + 0.004458 + 0.004458 + 0.004167 + 0.00375 + 0.004459 + 0.008709 + 0.005167 + 0.00575 + 0.005292 + 0.004375 + 0.004917 + 0.01075 + 0.005375 + 0.004042 + 0.005417 + 0.005416 + 0.005584 + 0.006666 + 0.007584 + 0.006916 + 0.006292 + 0.005209 + 0.0055 + 0.006459 + 0.005375 + 0.005708 + 0.005167 + 0.004459 + 0.004709 + 0.005084 + 0.00575 + 0.004709 + 0.006709 + 0.005875 + 0.005958 + 0.005583 + 0.00575 + 0.005709 + 0.005417 + 0.0055 + 0.006083 + 0.005834 + 0.005209 + 0.006083 + 0.005916 + 0.005833 + 0.006458 + 0.005917 + 0.005208 + 0.005583 + 0.006208 + 0.006333 + 0.005833 + 0.006333 + 0.005708 + 0.005417 + 0.004375 + 0.003916 + 0.004292 + 0.003708 + 0.004 + 0.004042 + 0.004125 + 0.003792 + 0.004625 + 0.003875 + 0.003833 + 0.004125 + 0.004333 + 0.004375 + 0.004333 + 0.003792 + 0.004375 + 0.00425 + 0.004125 + 0.004583 + 0.003792 + 0.003916 + 0.004042 + 0.004125 + 0.003917 + 0.003833 + 0.004041 + 0.004208 + 0.004208 + 0.004042 + 0.004541 + 0.004708 + 0.004625 + 0.005583 + 0.005291 + 0.012125 + 0.005666 + 0.005208 + 0.005416 + 0.005375 + 0.005542 + 0.005333 + 0.005333 + 0.005458 + 0.005167 + 0.005125 + 0.005084 + 0.004291 + 0.004833 + 0.004917 + 0.004583 + 0.005333 + 0.00625 + 0.005667 + 0.005292 + 0.005208 + 0.005 + 0.005209 + 0.006417 + 0.005625 + 0.005458 + 0.005459 + 0.007 + 0.006958 + 0.006792 + 0.005542 + 0.005291 + 0.005917 + 0.005709 + 0.005375 + 0.00525 + 0.005041 + 0.004167 + 0.004209 + 0.004125 + 0.004334 + 0.004167 + 0.004 + 0.004292 + 0.005916 + 0.0055 + 0.006 + 0.0055 + 0.005792 + 0.003833 + 0.005583 + 0.005416 + 0.005458 + 0.005958 + 0.006959 + 0.006875 + 0.006375 + 0.004666 + 0.004917 + 0.005333 + 0.005083 + 0.004959 + 0.006875 + 0.006667 + 0.0065 + 0.006167 + 0.005417 + 0.005333 + 0.004792 + 0.005 + 0.00475 + 0.003833 + 0.003958 + 0.004666 + 0.004084 + 0.003834 + 0.004708 + 0.004666 + 0.003875 + 0.004458 + 0.004625 + 0.004 + 0.004 + 0.003917 + 0.004 + 0.003833 + 0.004416 + 0.004 + 0.004042 + 0.003917 + 0.003959 + 0.004042 + 0.003875 + 0.003667 + 0.004333 + 0.004166 + 0.004334 + 0.004375 + 0.004209 + 0.004083 + 0.003958 + 0.004125 + 0.004 + 0.004084 + 0.003875 + 0.004 + 0.003792 + 0.003917 + 0.004416 + 0.003833 + 0.004167 + 0.003959 + 0.003958 + 0.004083 + 0.0045 + 0.004417 + 0.004458 + 0.004041 + 0.012792 + 0.004417 + 0.004166 + 0.004459 + 0.005 + 0.006583 + 0.0065 + 0.00625 + 0.006916 + 0.006125 + 0.006875 + 0.0075 + 0.006958 + 0.00575 + 0.005667 + 0.005584 + 0.005709 + 0.004834 + 0.005125 + 0.005333 + 0.005458 + 0.00625 + 0.005666 + 0.005291 + 0.005583 + 0.005625 + 0.005166 + 0.004125 + 0.003916 + 0.005208 + 0.006 + 0.005666 + 0.006625 + 0.006625 + 0.0065 + 0.006834 + 0.006209 + 0.005959 + 0.007167 + 0.005541 + 0.006375 + 0.006458 + 0.006792 + 0.006583 + 0.007167 + 0.007084 + 0.006334 + 0.006334 + 0.005417 + 0.005125 + 0.004875 + 0.005 + 0.004625 + 0.004 + 0.004084 + 0.004209 + 0.004208 + 0.003458 + 0.003875 + 0.004625 + 0.004417 + 0.003708 + 0.004166 + 0.004208 + 0.004 + 0.003958 + 0.003959 + 0.004333 + 0.004125 + 0.003792 + 0.004333 + 0.003959 + 0.004041 + 0.003792 + 0.004 + 0.004041 + 0.004292 + 0.003875 + 0.004292 + 0.004292 + 0.0045 + 0.004 + 0.003666 + 0.003875 + 0.004042 + 0.003917 + 0.004167 + 0.004042 + 0.003959 + 0.004042 + 0.004333 + 0.004042 + 0.004041 + 0.004 + 0.004083 + 0.004167 + 0.004375 + 0.004083 + 0.004417 + 0.004708 + 0.004333 + 0.004 + 0.00425 + 0.004417 + 0.00525 + 0.006459 + 0.006584 + 0.006125 + 0.006333 + 0.006083 + 0.006833 + 0.007125 + 0.00675 + 0.005708 + 0.005208 + 0.005625 + 0.005292 + 0.005209 + 0.005417 + 0.005584 + 0.004542 + 0.004166 + 0.004417 + 0.004333 + 0.004417 + 0.00475 + 0.004667 + 0.004084 + 0.004334 + 0.005125 + 0.005667 + 0.005667 + 0.006125 + 0.006084 + 0.006167 + 0.006083 + 0.006333 + 0.007 + 0.006542 + 0.008916 + 0.005708 + 0.006792 + 0.006209 + 0.005875 + 0.007292 + 0.006834 + 0.005959 + 0.006041 + 0.0055 + 0.007 + 0.005333 + 0.004708 + 0.004292 + 0.004 + 0.004 + 0.003833 + 0.00425 + 0.004042 + 0.003667 + 0.004542 + 0.004583 + 0.004625 + 0.004917 + 0.004 + 0.004041 + 0.00475 + 0.004584 + 0.012708 + 0.004083 + 0.00425 + 0.004375 + 0.00425 + 0.004625 + 0.003959 + 0.003958 + 0.003708 + 0.003667 + 0.015 + 0.004417 + 0.00375 + 0.004375 + 0.004083 + 0.003875 + 0.004833 + 0.00475 + 0.00475 + 0.004917 + 0.005 + 0.004667 + 0.004542 + 0.00475 + 0.008667 + 0.004542 + 0.004459 + 0.004833 + 0.004416 + 0.00425 + 0.004542 + 0.004667 + 0.005083 + 0.00475 + 0.00525 + 0.012 + 0.006334 + 0.005417 + 0.006 + 0.006959 + 0.007042 + 0.007375 + 0.006792 + 0.006958 + 0.008458 + 0.036667 + 0.014459 + 0.006958 + 0.008292 + 0.0065 + 0.006 + 0.005667 + 0.005458 + 0.005583 + 0.005791 + 0.005875 + 0.005541 + 0.005708 + 0.004709 + 0.005459 + 0.0045 + 0.004833 + 0.005292 + 0.005 + 0.005292 + 0.01025 + 0.00625 + 0.006583 + 0.006167 + 0.006209 + 0.015917 + 0.012791 + 0.008542 + 0.005708 + 0.007458 + 0.006458 + 0.006792 + 0.007791 + 0.007708 + 0.008208 + 0.007 + 0.006292 + 0.005584 + 0.005792 + 0.005333 + 0.005791 + 0.005542 + 0.005417 + 0.005666 + 0.005083 + 0.004083 + 0.003834 + 0.004125 + 0.004333 + 0.004042 + 0.004 + 0.004333 + 0.004209 + 0.004166 + 0.004417 + 0.0045 + 0.004292 + 0.004458 + 0.003958 + 0.004 + 0.004292 + 0.004333 + 0.004416 + 0.00425 + 0.004625 + 0.003833 + 0.004167 + 0.004166 + 0.004042 + 0.004208 + 0.004166 + 0.004167 + 0.004125 + 0.004042 + 0.0045 + 0.004792 + 0.00425 + 0.004583 + 0.004375 + 0.004208 + 0.004292 + 0.004083 + 0.004083 + 0.004292 + 0.004333 + 0.004625 + 0.00475 + 0.004583 + 0.004792 + 0.004875 + 0.00425 + 0.004625 + 0.004042 + 0.00475 + 0.005208 + 0.005958 + 0.005833 + 0.006083 + 0.005916 + 0.006083 + 0.005416 + 0.004292 + 0.015042 + 0.004625 + 0.005791 + 0.005792 + 0.005542 + 0.005 + 0.004125 + 0.00425 + 0.00475 + 0.003542 + 0.004334 + 0.00475 + 0.004875 + 0.004917 + 0.005 + 0.004708 + 0.005167 + 0.005625 + 0.005459 + 0.006709 + 0.00725 + 0.006125 + 0.005583 + 0.005333 + 0.005542 + 0.005416 + 0.004667 + 0.005541 + 0.007042 + 0.006625 + 0.006625 + 0.007375 + 0.006208 + 0.006333 + 0.006625 + 0.005041 + 0.004875 + 0.004417 + 0.004459 + 0.004417 + 0.004416 + 0.003958 + 0.004792 + 0.00425 + 0.004542 + 0.003833 + 0.00425 + 0.004625 + 0.004292 + 0.004042 + 0.004458 + 0.003958 + 0.004125 + 0.004417 + 0.004 + 0.004375 + 0.004125 + 0.004375 + 0.004542 + 0.004417 + 0.00525 + 0.006 + 0.005666 + 0.005417 + 0.004792 + 0.004208 + 0.005416 + 0.004917 + 0.006042 + 0.00475 + 0.005292 + 0.005792 + 0.005792 + 0.04875 + 0.007542 + 0.005042 + 0.005125 + 0.00575 + 0.005292 + 0.00525 + 0.00525 + 0.005333 + 0.005625 + 0.006042 + 0.008875 + 0.005792 + 0.005708 + 0.004958 + 0.005083 + 0.005792 + 0.006125 + 0.006167 + 0.007166 + 0.006459 + 0.005791 + 0.006875 + 0.006667 + 0.007417 + 0.006583 + 0.005958 + 0.005417 + 0.0055 + 0.005625 + 0.005083 + 0.005042 + 0.005 + 0.004875 + 0.005292 + 0.006584 + 0.00525 + 0.005542 + 0.004333 + 0.004375 + 0.004333 + 0.004041 + 0.004459 + 0.004458 + 0.00475 + 0.004167 + 0.005333 + 0.005875 + 0.004541 + 0.00625 + 0.005917 + 0.006209 + 0.005708 + 0.00525 + 0.006209 + 0.004834 + 0.006667 + 0.006 + 0.0065 + 0.006041 + 0.00625 + 0.005708 + 0.004417 + 0.00425 + 0.004208 + 0.004083 + 0.004459 + 0.004666 + 0.00475 + 0.004667 + 0.004375 + 0.00425 + 0.003792 + 0.004583 + 0.003958 + 0.00425 + 0.004459 + 0.004083 + 0.004 + 0.00425 + 0.004125 + 0.00425 + 0.004333 + 0.004625 + 0.004417 + 0.00425 + 0.004 + 0.004333 + 0.004791 + 0.004875 + 0.004125 + 0.0045 + 0.004584 + 0.004084 + 0.00425 + 0.0045 + 0.004042 + 0.004125 + 0.004042 + 0.00425 + 0.004542 + 0.003875 + 0.004584 + 0.004666 + 0.004625 + 0.004125 + 0.004625 + 0.004625 + 0.004958 + 0.00475 + 0.004667 + 0.005792 + 0.005583 + 0.00525 + 0.005334 + 0.00525 + 0.005209 + 0.004334 + 0.004959 + 0.004959 + 0.005292 + 0.005166 + 0.007 + 0.006375 + 0.005791 + 0.005542 + 0.006458 + 0.004958 + 0.004167 + 0.005792 + 0.00525 + 0.005 + 0.006292 + 0.005875 + 0.005458 + 0.0055 + 0.005458 + 0.005708 + 0.005291 + 0.00575 + 0.004875 + 0.004792 + 0.004291 + 0.0045 + 0.004333 + 0.004416 + 0.00525 + 0.005 + 0.006625 + 0.006208 + 0.005833 + 0.006083 + 0.006 + 0.005291 + 0.004792 + 0.006833 + 0.005958 + 0.012042 + 0.006375 + 0.005708 + 0.004916 + 0.006625 + 0.00525 + 0.004458 + 0.004917 + 0.0045 + 0.004792 + 0.004458 + 0.004 + 0.004292 + 0.003875 + 0.004375 + 0.0035 + 0.004375 + 0.003667 + 0.004416 + 0.004 + 0.004209 + 0.003875 + 0.003916 + 0.00375 + 0.004625 + 0.004625 + 0.00475 + 0.00475 + 0.004667 + 0.005 + 0.004875 + 0.005042 + 0.005334 + 0.004917 + 0.005125 + 0.005125 + 0.005333 + 0.004833 + 0.004375 + 0.004708 + 0.005 + 0.004541 + 0.004917 + 0.005792 + 0.00425 + 0.004167 + 0.004334 + 0.00525 + 0.005375 + 0.005167 + 0.004625 + 0.005416 + 0.005084 + 0.004916 + 0.005458 + 0.005375 + 0.005334 + 0.005666 + 0.005458 + 0.005875 + 0.005292 + 0.005291 + 0.005625 + 0.006625 + 0.006375 + 0.007584 + 0.006292 + 0.006958 + 0.0065 + 0.006375 + 0.005959 + 0.005166 + 0.006042 + 0.005834 + 0.005667 + 0.004958 + 0.004417 + 0.00425 + 0.004667 + 0.004417 + 0.004333 + 0.005417 + 0.005709 + 0.005375 + 0.005667 + 0.00525 + 0.006 + 0.005083 + 0.005291 + 0.006917 + 0.006167 + 0.006209 + 0.008542 + 0.00675 + 0.006417 + 0.006125 + 0.006083 + 0.005625 + 0.005041 + 0.006375 + 0.0065 + 0.006416 + 0.006375 + 0.00625 + 0.00525 + 0.005167 + 0.004417 + 0.004292 + 0.004375 + 0.004542 + 0.004458 + 0.004291 + 0.006 + 0.005833 + 0.005375 + 0.00525 + 0.004917 + 0.004916 + 0.004708 + 0.01375 + 0.005209 + 0.005042 + 0.004667 + 0.004084 + 0.004084 + 0.003709 + 0.003916 + 0.003875 + 0.004125 + 0.003708 + 0.004208 + 0.003916 + 0.004292 + 0.004166 + 0.004083 + 0.004125 + 0.004083 + 0.003833 + 0.00375 + 0.003667 + 0.003541 + 0.003625 + 0.004042 + 0.003791 + 0.00375 + 0.004417 + 0.004 + 0.004042 + 0.004125 + 0.004 + 0.004333 + 0.003958 + 0.00375 + 0.004708 + 0.004458 + 0.006917 + 0.005292 + 0.005375 + 0.005167 + 0.005125 + 0.005125 + 0.005167 + 0.004458 + 0.005208 + 0.004833 + 0.005583 + 0.007 + 0.006 + 0.006125 + 0.005666 + 0.005292 + 0.004958 + 0.004458 + 0.004209 + 0.004625 + 0.004292 + 0.004541 + 0.004167 + 0.004541 + 0.004292 + 0.003791 + 0.004042 + 0.004334 + 0.00425 + 0.006 + 0.004917 + 0.00525 + 0.005208 + 0.005083 + 0.005 + 0.005584 + 0.004791 + 0.005083 + 0.004833 + 0.006041 + 0.005958 + 0.005917 + 0.00525 + 0.005708 + 0.005208 + 0.016542 + 0.00625 + 0.005959 + 0.005459 + 0.005875 + 0.005625 + 0.004708 + 0.004041 + 0.004458 + 0.004125 + 0.004084 + 0.003958 + 0.003875 + 0.00475 + 0.003958 + 0.004042 + 0.003542 + 0.004834 + 0.004625 + 0.004416 + 0.004542 + 0.0045 + 0.005209 + 0.005334 + 0.005125 + 0.005125 + 0.004417 + 0.004166 + 0.004583 + 0.004 + 0.004375 + 0.004375 + 0.004875 + 0.005042 + 0.005125 + 0.005458 + 0.005125 + 0.0045 + 0.004375 + 0.004292 + 0.004291 + 0.004292 + 0.004209 + 0.004042 + 0.004375 + 0.0045 + 0.01575 + 0.007625 + 0.007042 + 0.005625 + 0.005209 + 0.004958 + 0.004792 + 0.005167 + 0.005166 + 0.005125 + 0.004958 + 0.005416 + 0.005833 + 0.0085 + 0.005833 + 0.004583 + 0.004333 + 0.006166 + 0.005125 + 0.005167 + 0.005208 + 0.011625 + 0.007292 + 0.00625 + 0.00625 + 0.006042 + 0.005583 + 0.005416 + 0.00525 + 0.005167 + 0.005792 + 0.006125 + 0.006167 + 0.005709 + 0.005875 + 0.005042 + 0.004708 + 0.005917 + 0.005459 + 0.005292 + 0.005916 + 0.0045 + 0.0045 + 0.004458 + 0.005625 + 0.0055 + 0.004875 + 0.005375 + 0.00625 + 0.00625 + 0.006542 + 0.005208 + 0.006541 + 0.006125 + 0.006291 + 0.006875 + 0.006583 + 0.00625 + 0.005875 + 0.0065 + 0.004375 + 0.004333 + 0.004583 + 0.00475 + 0.004833 + 0.005208 + 0.004458 + 0.004792 + 0.00475 + 0.004083 + 0.0045 + 0.004458 + 0.004208 + 0.004208 + 0.003958 + 0.003792 + 0.004417 + 0.004041 + 0.004542 + 0.00425 + 0.004166 + 0.004 + 0.004667 + 0.004 + 0.004167 + 0.003875 + 0.004167 + 0.004375 + 0.004083 + 0.004542 + 0.004125 + 0.004291 + 0.004917 + 0.005 + 0.006084 + 0.005917 + 0.00575 + 0.00475 + 0.00525 + 0.004959 + 0.004792 + 0.004584 + 0.005083 + 0.005167 + 0.004709 + 0.00525 + 0.004958 + 0.005208 + 0.005375 + 0.005708 + 0.005375 + 0.006125 + 0.005459 + 0.006959 + 0.006458 + 0.006375 + 0.006208 + 0.00725 + 0.007042 + 0.007041 + 0.007208 + 0.009333 + 0.007292 + 0.00725 + 0.007042 + 0.006292 + 0.004041 + 0.005459 + 0.00575 + 0.006416 + 0.006125 + 0.006083 + 0.005125 + 0.005709 + 0.005708 + 0.006583 + 0.007125 + 0.005916 + 0.005792 + 0.005375 + 0.005375 + 0.0055 + 0.005375 + 0.00575 + 0.006375 + 0.007041 + 0.007333 + 0.136667 + 0.011209 + 0.007667 + 0.006833 + 0.005458 + 0.006125 + 0.007333 + 0.009 + 0.0055 + 0.005709 + 0.005167 + 0.005625 + 0.008125 + 0.005583 + 0.004541 + 0.00425 + 0.004958 + 0.004333 + 0.004125 + 0.004125 + 0.005458 + 0.004875 + 0.004125 + 0.00425 + 0.004292 + 0.006583 + 0.004375 + 0.004666 + 0.004167 + 0.003875 + 0.004375 + 0.004458 + 0.004625 + 0.004375 + 0.004333 + 0.004459 + 0.004375 + 0.004333 + 0.004458 + 0.0045 + 0.004166 + 0.00425 + 0.004666 + 0.0045 + 0.004042 + 0.004208 + 0.003958 + 0.004292 + 0.004583 + 0.0045 + 0.004 + 0.004042 + 0.003958 + 0.0045 + 0.004166 + 0.004167 + 0.004 + 0.004333 + 0.004333 + 0.00375 + 0.003958 + 0.00525 + 0.005458 + 0.004708 + 0.004792 + 0.004667 + 0.00975 + 0.004583 + 0.004875 + 0.004333 + 0.006 + 0.006625 + 0.005666 + 0.005333 + 0.005375 + 0.007625 + 0.00575 + 0.005833 + 0.005334 + 0.004208 + 0.005583 + 0.004708 + 0.004375 + 0.004292 + 0.004333 + 0.004334 + 0.00425 + 0.005709 + 0.005167 + 0.005083 + 0.004834 + 0.00475 + 0.004458 + 0.015125 + 0.006917 + 0.006084 + 0.005792 + 0.009708 + 0.008458 + 0.007041 + 0.006625 + 0.007208 + 0.006417 + 0.009042 + 0.006875 + 0.00575 + 0.006958 + 0.00675 + 0.006625 + 0.006792 + 0.008041 + 0.006541 + 0.0065 + 0.006167 + 0.005375 + 0.005541 + 0.004958 + 0.004625 + 0.005166 + 0.005541 + 0.005792 + 0.006625 + 0.005667 + 0.005875 + 0.005959 + 0.00625 + 0.006042 + 0.005417 + 0.004959 + 0.005167 + 0.004958 + 0.004667 + 0.0045 + 0.005167 + 0.0055 + 0.005333 + 0.006 + 0.008209 + 0.006209 + 0.006167 + 0.005459 + 0.005375 + 0.005667 + 0.007042 + 0.005833 + 0.006417 + 0.005834 + 0.006459 + 0.005333 + 0.005542 + 0.005541 + 0.018417 + 0.018959 + 0.0065 + 0.005291 + 0.005542 + 0.063083 + 0.005667 + 0.005458 + 0.006375 + 0.005959 + 0.0155 + 0.007709 + 0.005875 + 0.005667 + 0.005458 + 0.005791 + 0.004833 + 0.004542 + 0.004917 + 0.005708 + 0.007959 + 0.008959 + 0.006458 + 0.007333 + 0.007 + 0.007375 + 0.013167 + 0.00825 + 0.006167 + 0.006375 + 0.009 + 0.005792 + 0.006042 + 0.005625 + 0.004834 + 0.004834 + 0.004666 + 0.005042 + 0.005125 + 0.005167 + 0.004541 + 0.0045 + 0.004541 + 0.004708 + 0.004375 + 0.0045 + 0.004875 + 0.006375 + 0.00725 + 0.006667 + 0.006833 + 0.007083 + 0.0065 + 0.006584 + 0.006125 + 0.007542 + 0.007084 + 0.006875 + 0.006958 + 0.006625 + 0.006584 + 0.007875 + 0.007084 + 0.005875 + 0.005417 + 0.005625 + 0.005416 + 0.004833 + 0.005375 + 0.005208 + 0.005375 + 0.006042 + 0.003958 + 0.004583 + 0.00475 + 0.00425 + 0.004292 + 0.004083 + 0.004041 + 0.004125 + 0.004625 + 0.005291 + 0.005333 + 0.005625 + 0.005708 + 0.004541 + 0.005292 + 0.00525 + 0.005375 + 0.00525 + 0.005209 + 0.004916 + 0.005208 + 0.00525 + 0.005125 + 0.005083 + 0.0055 + 0.005291 + 0.005417 + 0.005541 + 0.005084 + 0.005375 + 0.00625 + 0.005333 + 0.005083 + 0.005292 + 0.0055 + 0.005125 + 0.0055 + 0.005125 + 0.005958 + 0.005666 + 0.005875 + 0.006166 + 0.006292 + 0.006834 + 0.0055 + 0.005541 + 0.0055 + 0.0055 + 0.006541 + 0.007417 + 0.007167 + 0.006166 + 0.005458 + 0.006875 + 0.007084 + 0.007875 + 0.006209 + 0.007875 + 0.007084 + 0.00625 + 0.006125 + 0.005292 + 0.020208 + 0.006917 + 0.005166 + 0.005292 + 0.005 + 0.00475 + 0.005333 + 0.004875 + 0.005125 + 0.005208 + 0.00525 + 0.005167 + 0.005208 + 0.005958 + 0.006125 + 0.007583 + 0.006375 + 0.006292 + 0.005958 + 0.0065 + 0.00525 + 0.005917 + 0.008083 + 0.007958 + 0.006916 + 0.007 + 0.006292 + 0.005959 + 0.005709 + 0.004959 + 0.004792 + 0.005041 + 0.0055 + 0.023167 + 0.006 + 0.0055 + 0.004583 + 0.005958 + 0.004416 + 0.005125 + 0.005583 + 0.00525 + 0.005125 + 0.005042 + 0.005375 + 0.005084 + 0.005625 + 0.005333 + 0.005125 + 0.004416 + 0.004708 + 0.004583 + 0.005125 + 0.005208 + 0.005708 + 0.014833 + 0.007625 + 0.005458 + 0.0055 + 0.004958 + 0.004917 + 0.00525 + 0.004834 + 0.004667 + 0.005375 + 0.005208 + 0.006208 + 0.006375 + 0.00625 + 0.005625 + 0.005667 + 0.012208 + 0.008167 + 0.005916 + 0.005458 + 0.004417 + 0.004375 + 0.0055 + 0.006958 + 0.005125 + 0.005625 + 0.005416 + 0.005125 + 0.005167 + 0.005208 + 0.005958 + 0.00675 + 0.007459 + 0.008584 + 0.00875 + 0.007584 + 0.007709 + 0.006458 + 0.0065 + 0.005875 + 0.004833 + 0.006333 + 0.005708 + 0.005917 + 0.006625 + 0.005084 + 0.008042 + 0.006583 + 0.007125 + 0.006875 + 0.005667 + 0.005208 + 0.005708 + 0.005916 + 0.005125 + 0.005791 + 0.0055 + 0.006083 + 0.007042 + 0.006584 + 0.006625 + 0.006708 + 0.009 + 0.007083 + 0.007709 + 0.007667 + 0.006708 + 0.006917 + 0.006292 + 0.006125 + 0.008209 + 0.007459 + 0.007292 + 0.007041 + 0.006583 + 0.006208 + 0.005791 + 0.005208 + 0.00575 + 0.005958 + 0.005875 + 0.005042 + 0.006291 + 0.006583 + 0.004834 + 0.005375 + 0.005666 + 0.004667 + 0.004875 + 0.005083 + 0.006916 + 0.004625 + 0.005084 + 0.005334 + 0.0055 + 0.005375 + 0.004875 + 0.004709 + 0.005667 + 0.005416 + 0.004583 + 0.004583 + 0.004875 + 0.004625 + 0.004584 + 0.00475 + 0.004625 + 0.005083 + 0.005791 + 0.005417 + 0.006042 + 0.00725 + 0.008125 + 0.008334 + 0.007084 + 0.006 + 0.007292 + 0.005875 + 0.007 + 0.007375 + 0.008167 + 0.007208 + 0.009291 + 0.007042 + 0.005917 + 0.005833 + 0.005333 + 0.006209 + 0.005958 + 0.006958 + 0.006541 + 0.008875 + 0.011541 + 0.008208 + 0.007417 + 0.007459 + 0.007209 + 0.008 + 0.009416 + 0.006208 + 0.005375 + 0.005709 + 0.005334 + 0.005209 + 0.005333 + 0.005667 + 0.006042 + 0.006125 + 0.006209 + 0.005584 + 0.005875 + 0.005708 + 0.00625 + 0.00575 + 0.004917 + 0.004458 + 0.004667 + 0.005208 + 0.006292 + 0.007459 + 0.007459 + 0.007292 + 0.007208 + 0.006416 + 0.00675 + 0.007041 + 0.006125 + 0.006834 + 0.007125 + 0.007292 + 0.007042 + 0.006709 + 0.007459 + 0.007 + 0.006458 + 0.005833 + 0.005167 + 0.005208 + 0.004792 + 0.004917 + 0.004791 + 0.005542 + 0.005625 + 0.004667 + 0.004334 + 0.005167 + 0.005333 + 0.005 + 0.004708 + 0.004667 + 0.004125 + 0.004375 + 0.00475 + 0.004709 + 0.004459 + 0.004875 + 0.004708 + 0.004959 + 0.004291 + 0.004334 + 0.004458 + 0.004375 + 0.004583 + 0.004917 + 0.005167 + 0.004208 + 0.004958 + 0.00425 + 0.00425 + 0.004625 + 0.004583 + 0.004417 + 0.005 + 0.00425 + 0.004084 + 0.004459 + 0.004625 + 0.004584 + 0.0045 + 0.004667 + 0.004541 + 0.004334 + 0.004709 + 0.0055 + 0.004792 + 0.004459 + 0.005 + 0.005042 + 0.004334 + 0.0045 + 0.004792 + 0.004417 + 0.005375 + 0.0075 + 0.007167 + 0.006792 + 0.006709 + 0.00675 + 0.007 + 0.005583 + 0.005166 + 0.005 + 0.006625 + 0.00675 + 0.006 + 0.006209 + 0.005834 + 0.005584 + 0.005541 + 0.0055 + 0.005166 + 0.005209 + 0.004834 + 0.004542 + 0.004584 + 0.0045 + 0.004875 + 0.004708 + 0.004541 + 0.005709 + 0.007667 + 0.006833 + 0.007041 + 0.006375 + 0.006583 + 0.006916 + 0.005666 + 0.008 + 0.007708 + 0.007292 + 0.006459 + 0.006417 + 0.007291 + 0.008458 + 0.00725 + 0.006 + 0.005709 + 0.005417 + 0.005541 + 0.005875 + 0.005583 + 0.005625 + 0.005833 + 0.006792 + 0.005291 + 0.004125 + 0.004583 + 0.0055 + 0.005625 + 0.005208 + 0.015583 + 0.01175 + 0.006291 + 0.005708 + 0.005583 + 0.005708 + 0.005708 + 0.005291 + 0.004792 + 0.005334 + 0.005625 + 0.005375 + 0.005542 + 0.005416 + 0.005625 + 0.005375 + 0.005334 + 0.005375 + 0.004792 + 0.004541 + 0.004708 + 0.005333 + 0.00575 + 0.005542 + 0.005792 + 0.00525 + 0.005709 + 0.0055 + 0.005041 + 0.005459 + 0.004916 + 0.004292 + 0.004583 + 0.005875 + 0.005208 + 0.004333 + 0.004583 + 0.004583 + 0.004666 + 0.004292 + 0.004959 + 0.0045 + 0.006167 + 0.007375 + 0.007292 + 0.006334 + 0.005209 + 0.005584 + 0.005375 + 0.006042 + 0.004625 + 0.004667 + 0.006292 + 0.005875 + 0.005541 + 0.005958 + 0.006292 + 0.00575 + 0.005667 + 0.006041 + 0.013583 + 0.005667 + 0.00525 + 0.005125 + 0.005291 + 0.005083 + 0.00475 + 0.004833 + 0.004375 + 0.007416 + 0.007208 + 0.006542 + 0.006292 + 0.006209 + 0.005917 + 0.00525 + 0.004584 + 0.004041 + 0.00675 + 0.006708 + 0.006541 + 0.006167 + 0.006541 + 0.005625 + 0.006167 + 0.005333 + 0.005166 + 0.004958 + 0.005083 + 0.005083 + 0.004834 + 0.004792 + 0.0055 + 0.004917 + 0.003875 + 0.004167 + 0.004541 + 0.004458 + 0.004292 + 0.004291 + 0.004417 + 0.004416 + 0.004208 + 0.004125 + 0.004666 + 0.005083 + 0.007125 + 0.004041 + 0.004208 + 0.004 + 0.004125 + 0.004291 + 0.004167 + 0.004417 + 0.004 + 0.004292 + 0.004459 + 0.004792 + 0.005375 + 0.004334 + 0.004209 + 0.003833 + 0.004125 + 0.003958 + 0.004291 + 0.005 + 0.005208 + 0.004916 + 0.005541 + 0.005208 + 0.004917 + 0.005084 + 0.005125 + 0.005333 + 0.004667 + 0.00425 + 0.004584 + 0.004333 + 0.004167 + 0.004166 + 0.004625 + 0.0055 + 0.004959 + 0.007417 + 0.006625 + 0.008125 + 0.006709 + 0.00675 + 0.0065 + 0.006292 + 0.005375 + 0.004542 + 0.005083 + 0.005042 + 0.0045 + 0.004542 + 0.006541 + 0.006208 + 0.0055 + 0.005791 + 0.004708 + 0.00475 + 0.005 + 0.004834 + 0.00475 + 0.004584 + 0.004791 + 0.004292 + 0.004416 + 0.004958 + 0.005042 + 0.006875 + 0.006792 + 0.006292 + 0.006208 + 0.006208 + 0.006125 + 0.005834 + 0.007709 + 0.006875 + 0.006458 + 0.006458 + 0.00625 + 0.006542 + 0.006084 + 0.005625 + 0.004125 + 0.003875 + 0.003667 + 0.003791 + 0.004708 + 0.004541 + 0.004542 + 0.00575 + 0.006542 + 0.00525 + 0.005583 + 0.005333 + 0.005708 + 0.005417 + 0.00525 + 0.005084 + 0.005292 + 0.005041 + 0.004292 + 0.004334 + 0.0045 + 0.004542 + 0.004041 + 0.003875 + 0.003833 + 0.004125 + 0.004417 + 0.004042 + 0.004125 + 0.003875 + 0.003959 + 0.00425 + 0.004084 + 0.004291 + 0.004 + 0.004041 + 0.003833 + 0.003875 + 0.004291 + 0.00425 + 0.004083 + 0.003958 + 0.004167 + 0.004542 + 0.004125 + 0.004 + 0.004167 + 0.004583 + 0.005667 + 0.005625 + 0.005541 + 0.005458 + 0.0055 + 0.005458 + 0.005083 + 0.005291 + 0.0055 + 0.005625 + 0.005333 + 0.005375 + 0.00625 + 0.006708 + 0.007 + 0.006667 + 0.005625 + 0.0055 + 0.005583 + 0.00575 + 0.0045 + 0.004417 + 0.004583 + 0.004875 + 0.006 + 0.005333 + 0.005417 + 0.006667 + 0.00475 + 0.005166 + 0.005208 + 0.004792 + 0.004667 + 0.004833 + 0.004541 + 0.003417 + 0.005625 + 0.005125 + 0.005 + 0.006875 + 0.006708 + 0.006708 + 0.006583 + 0.005291 + 0.006458 + 0.006458 + 0.007625 + 0.006792 + 0.006334 + 0.006584 + 0.006458 + 0.006167 + 0.005 + 0.005583 + 0.004834 + 0.003875 + 0.004083 + 0.004083 + 0.00375 + 0.004625 + 0.003875 + 0.004 + 0.004333 + 0.004125 + 0.004583 + 0.00425 + 0.004333 + 0.004167 + 0.003583 + 0.004208 + 0.004458 + 0.003958 + 0.004334 + 0.004458 + 0.004166 + 0.00425 + 0.004209 + 0.003708 + 0.003875 + 0.003916 + 0.003667 + 0.004333 + 0.003958 + 0.004083 + 0.004042 + 0.003916 + 0.004125 + 0.004125 + 0.004083 + 0.00475 + 0.003875 + 0.004083 + 0.003916 + 0.004208 + 0.00425 + 0.004208 + 0.004125 + 0.0045 + 0.004166 + 0.004208 + 0.0045 + 0.006083 + 0.005583 + 0.005583 + 0.005542 + 0.00525 + 0.005291 + 0.005417 + 0.004834 + 0.00625 + 0.005375 + 0.00475 + 0.007083 + 0.006625 + 0.007167 + 0.006375 + 0.006959 + 0.005791 + 0.005667 + 0.004917 + 0.004709 + 0.004625 + 0.004542 + 0.005625 + 0.005667 + 0.005167 + 0.005458 + 0.005875 + 0.005416 + 0.005708 + 0.005208 + 0.0055 + 0.004291 + 0.004459 + 0.004334 + 0.004792 + 0.005458 + 0.004875 + 0.006542 + 0.007 + 0.006333 + 0.006666 + 0.006375 + 0.005583 + 0.0065 + 0.007 + 0.006833 + 0.006916 + 0.006333 + 0.007208 + 0.006458 + 0.006417 + 0.005291 + 0.005291 + 0.0055 + 0.00575 + 0.004959 + 0.005167 + 0.005125 + 0.006083 + 0.006125 + 0.005875 + 0.005125 + 0.005416 + 0.00525 + 0.004583 + 0.004459 + 0.004125 + 0.004125 + 0.004291 + 0.004292 + 0.003875 + 0.00425 + 0.004333 + 0.00425 + 0.004708 + 0.004334 + 0.00425 + 0.004125 + 0.00425 + 0.004042 + 0.004333 + 0.004541 + 0.003917 + 0.004667 + 0.004541 + 0.004209 + 0.004084 + 0.004042 + 0.004 + 0.004083 + 0.004542 + 0.004208 + 0.004042 + 0.00425 + 0.004458 + 0.00425 + 0.003917 + 0.004125 + 0.004333 + 0.0045 + 0.006208 + 0.006458 + 0.006333 + 0.005333 + 0.0055 + 0.005584 + 0.005917 + 0.005333 + 0.006625 + 0.004834 + 0.00525 + 0.006584 + 0.00725 + 0.007292 + 0.007417 + 0.006292 + 0.006625 + 0.006417 + 0.0055 + 0.0045 + 0.00475 + 0.004375 + 0.004292 + 0.004542 + 0.006167 + 0.006541 + 0.006625 + 0.006417 + 0.033791 + 0.01 + 0.005709 + 0.005334 + 0.005 + 0.005583 + 0.005167 + 0.011417 + 0.006333 + 0.005541 + 0.008417 + 0.007458 + 0.007333 + 0.006375 + 0.008333 + 0.004875 + 0.007375 + 0.007041 + 0.006875 + 0.006458 + 0.006625 + 0.005875 + 0.006583 + 0.006166 + 0.005833 + 0.005333 + 0.0055 + 0.004584 + 0.005375 + 0.005625 + 0.004292 + 0.004542 + 0.004458 + 0.00425 + 0.004708 + 0.004791 + 0.004875 + 0.004917 + 0.004542 + 0.004541 + 0.005 + 0.004375 + 0.004542 + 0.00475 + 0.004916 + 0.004667 + 0.004292 + 0.005 + 0.004333 + 0.004458 + 0.004166 + 0.00525 + 0.004625 + 0.004875 + 0.004041 + 0.004667 + 0.004208 + 0.00425 + 0.004459 + 0.004292 + 0.004416 + 0.004209 + 0.004333 + 0.004 + 0.004292 + 0.004 + 0.004125 + 0.003917 + 0.004292 + 0.004166 + 0.004208 + 0.0045 + 0.004458 + 0.004542 + 0.00475 + 0.00475 + 0.004125 + 0.004625 + 0.004375 + 0.004917 + 0.004667 + 0.005875 + 0.005042 + 0.006666 + 0.007208 + 0.006125 + 0.006208 + 0.005917 + 0.00475 + 0.004667 + 0.004834 + 0.004833 + 0.004583 + 0.004333 + 0.004125 + 0.006125 + 0.004875 + 0.005542 + 0.005709 + 0.005333 + 0.005041 + 0.004542 + 0.0045 + 0.00475 + 0.004625 + 0.004666 + 0.004458 + 0.005167 + 0.005125 + 0.006458 + 0.007084 + 0.006792 + 0.00675 + 0.006791 + 0.007042 + 0.005792 + 0.007042 + 0.007083 + 0.006708 + 0.006916 + 0.005542 + 0.006 + 0.008125 + 0.005958 + 0.005375 + 0.00575 + 0.00525 + 0.004875 + 0.005458 + 0.004458 + 0.005 + 0.004708 + 0.004083 + 0.003916 + 0.004583 + 0.004667 + 0.004292 + 0.00425 + 0.004417 + 0.004875 + 0.0055 + 0.00525 + 0.005458 + 0.005709 + 0.005792 + 0.005291 + 0.005167 + 0.005209 + 0.004834 + 0.004209 + 0.004959 + 0.004291 + 0.004 + 0.004542 + 0.004333 + 0.004291 + 0.004125 + 0.004042 + 0.004084 + 0.003542 + 0.003875 + 0.004125 + 0.004583 + 0.00425 + 0.004209 + 0.004458 + 0.004542 + 0.004209 + 0.00375 + 0.004084 + 0.00425 + 0.004125 + 0.0045 + 0.004667 + 0.004833 + 0.004375 + 0.005625 + 0.005458 + 0.005417 + 0.005125 + 0.005292 + 0.006459 + 0.005375 + 0.006417 + 0.005916 + 0.005666 + 0.005334 + 0.005084 + 0.004959 + 0.00525 + 0.004917 + 0.004709 + 0.005209 + 0.0055 + 0.005833 + 0.005875 + 0.005791 + 0.005291 + 0.005875 + 0.005291 + 0.004708 + 0.005083 + 0.004875 + 0.004666 + 0.004625 + 0.004417 + 0.004291 + 0.004167 + 0.005542 + 0.005 + 0.005042 + 0.006833 + 0.00625 + 0.006041 + 0.005667 + 0.005 + 0.005792 + 0.005042 + 0.005834 + 0.00625 + 0.00625 + 0.004917 + 0.005416 + 0.005 + 0.004375 + 0.003792 + 0.004083 + 0.004 + 0.004125 + 0.004 + 0.004333 + 0.003958 + 0.004208 + 0.003583 + 0.00425 + 0.004541 + 0.004167 + 0.004375 + 0.003792 + 0.004375 + 0.00425 + 0.003625 + 0.004 + 0.004208 + 0.003959 + 0.004167 + 0.004083 + 0.004083 + 0.004083 + 0.004083 + 0.004 + 0.004375 + 0.003583 + 0.004166 + 0.003708 + 0.004625 + 0.004041 + 0.003667 + 0.00375 + 0.003792 + 0.004167 + 0.003667 + 0.003791 + 0.004417 + 0.004292 + 0.003916 + 0.004375 + 0.004417 + 0.004667 + 0.004125 + 0.004458 + 0.004333 + 0.004833 + 0.005917 + 0.005708 + 0.005584 + 0.004959 + 0.005 + 0.005166 + 0.004625 + 0.004583 + 0.00475 + 0.005959 + 0.005209 + 0.006375 + 0.005916 + 0.006958 + 0.005833 + 0.006042 + 0.005417 + 0.004667 + 0.0045 + 0.004333 + 0.004667 + 0.004458 + 0.006166 + 0.005542 + 0.005917 + 0.005666 + 0.005292 + 0.005417 + 0.005125 + 0.005208 + 0.005167 + 0.005209 + 0.00625 + 0.006333 + 0.005291 + 0.006541 + 0.00675 + 0.0075 + 0.007334 + 0.007084 + 0.0065 + 0.00725 + 0.005084 + 0.006917 + 0.007958 + 0.006917 + 0.005792 + 0.005334 + 0.006709 + 0.006459 + 0.007791 + 0.007708 + 0.024917 + 0.007708 + 0.00475 + 0.004834 + 0.004666 + 0.004792 + 0.004584 + 0.00425 + 0.003917 + 0.003875 + 0.004375 + 0.003666 + 0.00375 + 0.004125 + 0.004291 + 0.004167 + 0.004208 + 0.004166 + 0.004 + 0.003625 + 0.005167 + 0.004458 + 0.004916 + 0.004125 + 0.004125 + 0.004 + 0.00425 + 0.004042 + 0.004167 + 0.004416 + 0.004083 + 0.004125 + 0.003792 + 0.004 + 0.003458 + 0.003833 + 0.0035 + 0.003958 + 0.004042 + 0.004167 + 0.003959 + 0.003792 + 0.004 + 0.003916 + 0.003917 + 0.004083 + 0.004083 + 0.004542 + 0.004209 + 0.004375 + 0.004125 + 0.004083 + 0.004417 + 0.0045 + 0.004125 + 0.004708 + 0.005417 + 0.0055 + 0.007042 + 0.006042 + 0.005875 + 0.005583 + 0.0055 + 0.005834 + 0.005584 + 0.004584 + 0.005167 + 0.005375 + 0.00575 + 0.005209 + 0.005459 + 0.004834 + 0.004875 + 0.004542 + 0.005291 + 0.005667 + 0.005458 + 0.005208 + 0.005375 + 0.005541 + 0.005583 + 0.00575 + 0.005625 + 0.006667 + 0.007709 + 0.006875 + 0.012083 + 0.006875 + 0.006417 + 0.007209 + 0.008 + 0.0065 + 0.005666 + 0.004875 + 0.007042 + 0.006709 + 0.006917 + 0.007709 + 0.006584 + 0.005084 + 0.004666 + 0.005375 + 0.005791 + 0.007291 + 0.005167 + 0.005542 + 0.006209 + 0.004875 + 0.005625 + 0.004916 + 0.00575 + 0.004667 + 0.004208 + 0.004625 + 0.004917 + 0.004709 + 0.005042 + 0.005834 + 0.005584 + 0.006125 + 0.005083 + 0.004292 + 0.005125 + 0.004916 + 0.004792 + 0.004333 + 0.004875 + 0.004833 + 0.0045 + 0.004666 + 0.005125 + 0.005791 + 0.005208 + 0.004875 + 0.005208 + 0.005416 + 0.005125 + 0.005416 + 0.0045 + 0.004041 + 0.004375 + 0.004625 + 0.004458 + 0.004292 + 0.003833 + 0.00425 + 0.004458 + 0.004083 + 0.004666 + 0.004625 + 0.0045 + 0.00425 + 0.004416 + 0.004542 + 0.004167 + 0.004583 + 0.0045 + 0.005583 + 0.006709 + 0.006792 + 0.006333 + 0.006333 + 0.006291 + 0.0065 + 0.006 + 0.005083 + 0.005625 + 0.005542 + 0.005791 + 0.0065 + 0.006666 + 0.005708 + 0.005708 + 0.005875 + 0.005791 + 0.005375 + 0.004958 + 0.005 + 0.005 + 0.004833 + 0.005041 + 0.004833 + 0.005042 + 0.0055 + 0.005583 + 0.006333 + 0.005625 + 0.005875 + 0.006334 + 0.006167 + 0.006 + 0.005208 + 0.006708 + 0.005667 + 0.005958 + 0.005792 + 0.005542 + 0.005667 + 0.018084 + 0.007833 + 0.006834 + 0.005334 + 0.005166 + 0.0055 + 0.006166 + 0.005667 + 0.006208 + 0.005375 + 0.004917 + 0.004708 + 0.005375 + 0.005375 + 0.005125 + 0.004875 + 0.004917 + 0.005666 + 0.005208 + 0.005084 + 0.005209 + 0.005542 + 0.004583 + 0.004875 + 0.005417 + 0.00525 + 0.005334 + 0.005208 + 0.005292 + 0.005334 + 0.005291 + 0.005375 + 0.004541 + 0.004209 + 0.00425 + 0.003959 + 0.003959 + 0.004167 + 0.003833 + 0.004 + 0.003916 + 0.004167 + 0.003958 + 0.004 + 0.003833 + 0.004125 + 0.004333 + 0.004167 + 0.004041 + 0.004458 + 0.004709 + 0.004708 + 0.004166 + 0.00425 + 0.0045 + 0.004333 + 0.005375 + 0.005917 + 0.005708 + 0.007125 + 0.00575 + 0.007125 + 0.00575 + 0.005584 + 0.00575 + 0.005583 + 0.004708 + 0.004542 + 0.004917 + 0.004666 + 0.004458 + 0.004541 + 0.0055 + 0.005833 + 0.005709 + 0.005584 + 0.006292 + 0.007125 + 0.006292 + 0.005584 + 0.005709 + 0.00475 + 0.004709 + 0.004292 + 0.004875 + 0.004167 + 0.00525 + 0.005292 + 0.007625 + 0.006667 + 0.00675 + 0.00675 + 0.006916 + 0.005708 + 0.007 + 0.006333 + 0.007292 + 0.006625 + 0.006375 + 0.007083 + 0.00575 + 0.006 + 0.005375 + 0.004667 + 0.004625 + 0.004292 + 0.004083 + 0.004208 + 0.004459 + 0.004875 + 0.003625 + 0.004125 + 0.004291 + 0.004709 + 0.003875 + 0.003792 + 0.003708 + 0.004 + 0.003666 + 0.003917 + 0.004375 + 0.004167 + 0.004625 + 0.004167 + 0.003958 + 0.004916 + 0.006 + 0.005541 + 0.005584 + 0.005375 + 0.004916 + 0.004458 + 0.00575 + 0.005917 + 0.005917 + 0.006125 + 0.005917 + 0.005542 + 0.006333 + 0.00575 + 0.005959 + 0.005042 + 0.004916 + 0.00525 + 0.005125 + 0.005291 + 0.004958 + 0.005083 + 0.0055 + 0.006791 + 0.006458 + 0.015708 + 0.020333 + 0.008917 + 0.00575 + 0.016875 + 0.006083 + 0.00625 + 0.019166 + 0.0365 + 0.042875 + 0.009292 + 0.007541 + 0.011042 + 0.007916 + 0.005625 + 0.0055 + 0.005959 + 0.005917 + 0.004625 + 0.004875 + 0.00525 + 0.005625 + 0.00625 + 0.00675 + 0.008 + 0.007417 + 0.007791 + 0.006125 + 0.006084 + 0.00575 + 0.004959 + 0.005 + 0.005 + 0.00575 + 0.006 + 0.007208 + 0.005708 + 0.005125 + 0.008292 + 0.007084 + 0.007625 + 0.005417 + 0.005333 + 0.006625 + 0.005375 + 0.006209 + 0.011875 + 0.009167 + 0.006709 + 0.007417 + 0.009917 + 0.007584 + 0.005291 + 0.006167 + 0.007166 + 0.005875 + 0.005375 + 0.005417 + 0.007916 + 0.005708 + 0.004667 + 0.00675 + 0.007625 + 0.006458 + 0.005333 + 0.006542 + 0.0065 + 0.005875 + 0.006042 + 0.006208 + 0.005458 + 0.005375 + 0.005208 + 0.004583 + 0.0045 + 0.00425 + 0.004541 + 0.004042 + 0.004833 + 0.00525 + 0.004541 + 0.004792 + 0.004708 + 0.005083 + 0.004917 + 0.00475 + 0.004541 + 0.004333 + 0.004333 + 0.004625 + 0.004542 + 0.005042 + 0.004541 + 0.004583 + 0.00475 + 0.005083 + 0.004709 + 0.004708 + 0.0045 + 0.006625 + 0.005083 + 0.004667 + 0.005041 + 0.004709 + 0.005083 + 0.004875 + 0.007791 + 0.005125 + 0.006042 + 0.005041 + 0.005417 + 0.005625 + 0.005 + 0.008333 + 0.005708 + 0.005792 + 0.004541 + 0.004 + 0.005083 + 0.005208 + 0.005 + 0.004875 + 0.004209 + 0.004416 + 0.00475 + 0.004958 + 0.004959 + 0.005083 + 0.004875 + 0.004584 + 0.004667 + 0.007917 + 0.004709 + 0.004834 + 0.005 + 0.005458 + 0.005584 + 0.005334 + 0.005292 + 0.004958 + 0.008708 + 0.005625 + 0.011125 + 0.004417 + 0.005917 + 0.00525 + 0.0055 + 0.0055 + 0.008333 + 0.005625 + 0.005708 + 0.005 + 0.004375 + 0.004334 + 0.004125 + 0.004125 + 0.00425 + 0.004667 + 0.004625 + 0.004208 + 0.004833 + 0.004167 + 0.004125 + 0.00475 + 0.004292 + 0.004334 + 0.0045 + 0.004042 + 0.004 + 0.004542 + 0.0045 + 0.003791 + 0.004208 + 0.004125 + 0.004292 + 0.004459 + 0.004375 + 0.0045 + 0.004792 + 0.004291 + 0.004459 + 0.004291 + 0.004375 + 0.004291 + 0.004542 + 0.004666 + 0.00425 + 0.004083 + 0.004458 + 0.00425 + 0.004167 + 0.004334 + 0.004459 + 0.004416 + 0.00425 + 0.004167 + 0.004333 + 0.003958 + 0.003833 + 0.004542 + 0.005 + 0.004709 + 0.004417 + 0.004541 + 0.004542 + 0.004708 + 0.004708 + 0.004583 + 0.004917 + 0.005708 + 0.008417 + 0.005625 + 0.005709 + 0.005333 + 0.004916 + 0.005708 + 0.00475 + 0.004333 + 0.004667 + 0.004916 + 0.004292 + 0.004625 + 0.007084 + 0.004875 + 0.00475 + 0.004625 + 0.004541 + 0.004875 + 0.005083 + 0.004417 + 0.004834 + 0.004959 + 0.004709 + 0.004667 + 0.004625 + 0.005209 + 0.005084 + 0.007958 + 0.005292 + 0.00575 + 0.006042 + 0.005208 + 0.005416 + 0.004917 + 0.00575 + 0.008 + 0.005916 + 0.005625 + 0.005417 + 0.005458 + 0.004916 + 0.004333 + 0.004167 + 0.0045 + 0.004542 + 0.00475 + 0.004542 + 0.0045 + 0.004875 + 0.004958 + 0.004459 + 0.003834 + 0.004542 + 0.004541 + 0.00425 + 0.004709 + 0.004375 + 0.004417 + 0.004209 + 0.004 + 0.00425 + 0.003834 + 0.004292 + 0.004041 + 0.004666 + 0.004167 + 0.004209 + 0.004209 + 0.004125 + 0.004 + 0.00425 + 0.004458 + 0.004625 + 0.004083 + 0.00425 + 0.004084 + 0.004166 + 0.004167 + 0.00425 + 0.0045 + 0.004125 + 0.004166 + 0.00425 + 0.004375 + 0.004042 + 0.004041 + 0.004167 + 0.0045 + 0.004291 + 0.004208 + 0.004667 + 0.004541 + 0.004083 + 0.00475 + 0.007791 + 0.004375 + 0.004583 + 0.004417 + 0.004333 + 0.005666 + 0.005167 + 0.005542 + 0.005125 + 0.008417 + 0.005416 + 0.005542 + 0.00475 + 0.004334 + 0.00425 + 0.004667 + 0.0045 + 0.005084 + 0.004084 + 0.004459 + 0.004625 + 0.003375]} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/point-latencies.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/point-latencies.edn new file mode 100644 index 00000000..01b09fff --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/point-latencies.edn @@ -0,0 +1,49 @@ +{:workload :point, + :valid-at 1000, + :warmup-per-variant 2000, + :rounds 7, + :per-round 2000, + :checksum 93800, + :variants + {:raw-four + {:n 14000, + :median-ms 0.004625, + :p95-ms 0.006959, + :min-ms 0.002833, + :max-ms 0.105333}, + :inline-eight + {:n 14000, + :median-ms 0.007458, + :p95-ms 0.010959, + :min-ms 0.004375, + :max-ms 1.575417}, + :filter-inline-eight + {:n 14000, + :median-ms 0.008625, + :p95-ms 0.012625, + :min-ms 0.005208, + :max-ms 0.115458}, + :filter-tx-naive + {:n 14000, + :median-ms 0.005958, + :p95-ms 0.00875, + :min-ms 0.003958, + :max-ms 0.095458}, + :filter-tx-request-memo + {:n 14000, + :median-ms 0.005666, + :p95-ms 0.008333, + :min-ms 0.003792, + :max-ms 0.1405}, + :filter-tx-shared-memo + {:n 14000, + :median-ms 0.004792, + :p95-ms 0.007084, + :min-ms 0.003041, + :max-ms 0.072791}, + :filter-tx-sparse-bounds + {:n 14000, + :median-ms 0.00475, + :p95-ms 0.007042, + :min-ms 0.002958, + :max-ms 0.136667}}} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/report.clj b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/report.clj new file mode 100644 index 00000000..8433e489 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/report.clj @@ -0,0 +1,60 @@ +(ns v9-txmeta-report + (:require [clojure.edn :as edn] + [clojure.string :as str] + [v9-txmeta-benchmark :as b])) + +(defn read-result [name] (edn/read-string (slurp (str b/out-dir name ".edn")))) +(def labels + {:raw-four "Four-slot raw control; no temporal filtering" + :inline-eight "Eight-slot inline validity, manual filter" + :filter-inline-eight "Eight-slot inline validity via d/filter" + :filter-tx-naive "Four-slot + d/filter + entity lookup per candidate" + :filter-tx-request-memo "Four-slot + d/filter + per-request bounds memo" + :filter-tx-shared-memo "Four-slot + d/filter + shared bounds memo" + :filter-tx-sparse-bounds "Four-slot + d/filter + sparse qualified-tx bounds map"}) + +(defn write-report! [] + (let [pages (:variants (read-result "page-latencies")) + points (:variants (read-result "point-latencies")) + counts (:variants (read-result "full-count-1000")) + rows (for [v b/variants] + (format "| %s | %.3f | %.3f | %.3f | %.3f | %.3f |" + (labels v) (* 1000 (:median-ms (pages v))) (* 1000 (:p95-ms (pages v))) + (* 1000 (:median-ms (points v))) (* 1000 (:p95-ms (points v))) + (:median-ms (counts v)))) + body + (str "# Datomic temporal relationship filtering: 5% qualified\n\n" + "Measured 2026-09-04 via nREPL on an Apple M4 Max, 36 GiB RAM, Java 26.0.2, Clojure 1.11.4, Datomic Peer/transactor 1.0.7705. Dedicated Peer: 6 GiB max heap. Local :dev transactor: 4 GiB heap. Git HEAD: 031144ceda0925d2cd171c0b72332d609bcc4fcc.\n\n" + "## Fixture and method\n\n" + "One isolated :dev database contained 1,000,000 logical relationships in both four-slot and eight-slot layouts, with forward and reverse datoms for each: 4,000,000 relationship datoms total. Independent index enumeration verified 1,000,000 datoms for each of f4, r4, f8, r8. There was also one allocation marker per resource. No production EACL database or source was modified.\n\n" + "All relationships share a subject and relation. In sorted endpoint eid order, indices congruent to 0 mod 40 are expired; indices congruent to 20 mod 40 are future; all others are permanent. This gives 950,000 permanent, 25,000 expired (until=900), and 25,000 future (from=1100). At valid-at=1000 all 5% are inactive. At valid-at=1200 only the expired 2.5% are inactive. Numeric times are synthetic epoch-millisecond values; the comparison logic is identical for contemporary values. Seed time was 53.219 seconds.\n\n" + "The four-slot layout obtains bounds from the relationship datom's assertion transaction. The eight-slot layout has [identity-fields caveat context from until], with both caveat fields nil. Both layouts carry identical logical bounds.\n\n" + "Resource eids were allocated before relationship writes so temporal grouping by transaction did not cluster temporal relationships in traversal order. Each block of 2,000 relationships used three assertion transactions: 1,900 permanent, 50 expired, 50 future. There are 1,500 assertion transactions total. This strongly favors metadata memoization and must not be mistaken for a one-transaction-per-relationship workload.\n\n" + "These are single-threaded, warm Peer index-read measurements, not complete EACL authorization requests, cold reads, or concurrent service latency. A freshly constructed filtered view and request-local cache allocation are included in each request where applicable. Shared metadata preparation is excluded and reported separately. No authorization answers are cached. Variant order rotates between rounds; requests use deterministic varying index positions.\n\n" + "Each page returns 20 active relationships. Each point check addresses one physical identity. The four-slot point check supplies the full tuple to d/datoms; the eight-slot point check seeks to the identity prefix and checks the returned identity. The point timings therefore compare these access paths, not tuple width in isolation.\n\n" + "Page timing: 3,500 samples per variant after 1,000 warmups. Point timing: 14,000 samples per variant after 2,000 warmups. Full forward scans: seven samples per variant. The unfiltered control returns all one million and intentionally has different semantics. Every filtered full scan returned exactly 950,000 at t=1000, and 975,000 at t=1200. There were 2,016 explicit page/point equality assertions against generated expected results.\n\n" + "## Results\n\n" + "| Read method | Page median µs | Page p95 µs | Point median µs | Point p95 µs | Full scan median ms |\n" + "|---|---:|---:|---:|---:|---:|\n" + (str/join "\n" rows) "\n\n" + "The shared memo stored 1,500 decoded transaction-bound pairs, including permanent entries. Populating it with a full relationship scan took 180.397 ms (some entries had already been touched during verification). The sparse variant instead enumerated only the from/until metadata attributes, building 1,000 qualified-tx entries from 1,000 metadata datoms in 2.619 ms in one observed preparation run. Sparse-map absence means permanent only because the map is complete for the selected immutable database basis. Both structures cache bounds, not active/inactive answers.\n\n" + "Separate instrumentation found 21–22 logical candidates through the twentieth result in sampled pages. d/filter actually invoked its predicate 34–35 times, showing read-ahead in these calls. Candidate-to-tx diversity was 2–4 transactions in the instrumented pages. These are measured predicate calls, not disk-read counts. Timing runs did not contain instrumentation counters.\n\n" + "## Rescheduling\n\n" + "Four cases were independently run and asserted on both in-memory and :dev Datomic.\n\n" + "| Attempt | Observed outcome |\n|---|---|\n" + "| Reassert identical forward/reverse tuples with new tx-meta | New transaction metadata stored; database basis-t 1005→1006; both relationship assertion tx ids stayed 13194139534317; effective expiry remained 100, not 200; relationship tx-data was empty. |\n" + "| Retract then reassert identical EAV in one transaction | Rejected with :db.error/datoms-conflict; database unchanged. |\n" + "| Assert then retract identical EAV in one transaction | Same conflict; ordering does not rescue it. |\n" + "| Retract in one transaction, reassert in the next | New assertion tx and expiry 200 observed, but the intermediate committed database has neither endpoint tuple. |\n\n" + "A further speculative d/with test verified that changing metadata on the original assertion transaction is possible and changes both sampled relationships that share that transaction. The old immutable database retains the old metadata. A separate speculative d/with test successfully replaced both eight-slot tuple values atomically by changing their from component; the old tuple values were absent and both new values present. These two follow-up cases used d/with, not a committed write.\n\n" + "## Cache implications\n\n" + "On one unchanged database basis (1003003) and one unchanged assertion tx (13194140534818), the same forward and reverse identity was inactive at valid-at=1000 and active at valid-at=1200. No transaction is required for time-dependent answers to change. d/filter alone therefore cannot make a cache keyed only by database/relationship versions coherent. Existing EACL relation stamps are changed by writes; see modules/eacl-datomic/src/eacl/datomic/schema.clj:65 and backend.clj:293.\n\n" + "A cache needs valid-time scope: exact valid-at, or a provably stable interval/epoch bounded by relevant transitions. Negative results must stop being reused when a future relationship becomes active. Metadata maps must also be scoped to their source database basis or maintained with correct version tracking. A stale incomplete sparse map would incorrectly treat an unseen qualified transaction as permanent. The measured cache is tied to one frozen basis.\n\n" + "## Interpretation\n\n" + "High confidence: metadata filtering is semantically viable; unchanged-EAV reassertion does not replace the original assertion tx; same-tx retract/reassert is rejected; clock changes need explicit cache scope. Moderate confidence for performance generalization: cached metadata is competitive with inline validity for small warm reads in this distribution, while per-candidate entity lookup is much slower on full scans. The earlier 111 ms first-page result with 999,980 expired predecessors is not representative of this evenly distributed 5% fixture. Local expired-prefix density, not merely global percentage, governs how many candidates a page skips.\n\n" + "No cold-cache, one-transaction-per-relationship, mixed concurrent writes, full authorization graph, or production cache coherence implementation was benchmarked.\n\n" + "## Reproduction and evidence\n\n" + "Scripts: benchmark.clj, rescheduling.clj, followup.clj, report.clj. Raw observations and summaries are adjacent EDN files. Load the benchmark script in a dedicated nREPL using the core classpath, call seed! with 1000000, build-sparse!, verify!, warm-shared!, run-latencies! for page (7 500 1000) and point (7 2000 2000), and run-full-counts! (7 1000) and (1 1200). Run rescheduling/run-all! for mem and dev prefixes; it deletes its own tiny databases. Cleanup the benchmark database using its saved URI.\n\n" + "Official reference: https://docs.datomic.com/reference/filters.html (filtering transaction attributes) and https://docs.datomic.com/transactions/transaction-data-reference.html (redundant assertions).\n")] + (spit (str b/out-dir "report.md") body) + {:report (str b/out-dir "report.md") :characters (count body)})) diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/report.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/report.md new file mode 100644 index 00000000..adaabf5a --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/report.md @@ -0,0 +1,66 @@ +# Datomic temporal relationship filtering: 5% qualified + +Measured 2026-09-04 via nREPL on an Apple M4 Max, 36 GiB RAM, Java 26.0.2, Clojure 1.11.4, Datomic Peer/transactor 1.0.7705. Dedicated Peer: 6 GiB max heap. Local :dev transactor: 4 GiB heap. Git HEAD: 031144ceda0925d2cd171c0b72332d609bcc4fcc. + +## Fixture and method + +One isolated :dev database contained 1,000,000 logical relationships in both four-slot and eight-slot layouts, with forward and reverse datoms for each: 4,000,000 relationship datoms total. Independent index enumeration verified 1,000,000 datoms for each of f4, r4, f8, r8. There was also one allocation marker per resource. No production EACL database or source was modified. + +All relationships share a subject and relation. In sorted endpoint eid order, indices congruent to 0 mod 40 are expired; indices congruent to 20 mod 40 are future; all others are permanent. This gives 950,000 permanent, 25,000 expired (until=900), and 25,000 future (from=1100). At valid-at=1000 all 5% are inactive. At valid-at=1200 only the expired 2.5% are inactive. Numeric times are synthetic epoch-millisecond values; the comparison logic is identical for contemporary values. Seed time was 53.219 seconds. + +The four-slot layout obtains bounds from the relationship datom's assertion transaction. The eight-slot layout has [identity-fields caveat context from until], with both caveat fields nil. Both layouts carry identical logical bounds. + +Resource eids were allocated before relationship writes so temporal grouping by transaction did not cluster temporal relationships in traversal order. Each block of 2,000 relationships used three assertion transactions: 1,900 permanent, 50 expired, 50 future. There are 1,500 assertion transactions total. This strongly favors metadata memoization and must not be mistaken for a one-transaction-per-relationship workload. + +These are single-threaded, warm Peer index-read measurements, not complete EACL authorization requests, cold reads, or concurrent service latency. A freshly constructed filtered view and request-local cache allocation are included in each request where applicable. Shared metadata preparation is excluded and reported separately. No authorization answers are cached. Variant order rotates between rounds; requests use deterministic varying index positions. + +Each page returns 20 active relationships. Each point check addresses one physical identity. The four-slot point check supplies the full tuple to d/datoms; the eight-slot point check seeks to the identity prefix and checks the returned identity. The point timings therefore compare these access paths, not tuple width in isolation. + +Page timing: 3,500 samples per variant after 1,000 warmups. Point timing: 14,000 samples per variant after 2,000 warmups. Full forward scans: seven samples per variant. The unfiltered control returns all one million and intentionally has different semantics. Every filtered full scan returned exactly 950,000 at t=1000, and 975,000 at t=1200. There were 2,016 explicit page/point equality assertions against generated expected results. + +## Results + +| Read method | Page median µs | Page p95 µs | Point median µs | Point p95 µs | Full scan median ms | +|---|---:|---:|---:|---:|---:| +| Four-slot raw control; no temporal filtering | 8.416 | 12.708 | 4.625 | 6.959 | 95.838 | +| Eight-slot inline validity, manual filter | 8.791 | 14.167 | 7.458 | 10.959 | 102.992 | +| Eight-slot inline validity via d/filter | 10.000 | 15.708 | 8.625 | 12.625 | 129.471 | +| Four-slot + d/filter + entity lookup per candidate | 34.959 | 48.166 | 5.958 | 8.750 | 860.894 | +| Four-slot + d/filter + per-request bounds memo | 11.917 | 18.125 | 5.666 | 8.333 | 136.384 | +| Four-slot + d/filter + shared bounds memo | 9.125 | 14.625 | 4.792 | 7.084 | 133.632 | +| Four-slot + d/filter + sparse qualified-tx bounds map | 9.750 | 14.917 | 4.750 | 7.042 | 130.239 | + +The shared memo stored 1,500 decoded transaction-bound pairs, including permanent entries. Populating it with a full relationship scan took 180.397 ms (some entries had already been touched during verification). The sparse variant instead enumerated only the from/until metadata attributes, building 1,000 qualified-tx entries from 1,000 metadata datoms in 2.619 ms in one observed preparation run. Sparse-map absence means permanent only because the map is complete for the selected immutable database basis. Both structures cache bounds, not active/inactive answers. + +Separate instrumentation found 21–22 logical candidates through the twentieth result in sampled pages. d/filter actually invoked its predicate 34–35 times, showing read-ahead in these calls. Candidate-to-tx diversity was 2–4 transactions in the instrumented pages. These are measured predicate calls, not disk-read counts. Timing runs did not contain instrumentation counters. + +## Rescheduling + +Four cases were independently run and asserted on both in-memory and :dev Datomic. + +| Attempt | Observed outcome | +|---|---| +| Reassert identical forward/reverse tuples with new tx-meta | New transaction metadata stored; database basis-t 1005→1006; both relationship assertion tx ids stayed 13194139534317; effective expiry remained 100, not 200; relationship tx-data was empty. | +| Retract then reassert identical EAV in one transaction | Rejected with :db.error/datoms-conflict; database unchanged. | +| Assert then retract identical EAV in one transaction | Same conflict; ordering does not rescue it. | +| Retract in one transaction, reassert in the next | New assertion tx and expiry 200 observed, but the intermediate committed database has neither endpoint tuple. | + +A further speculative d/with test verified that changing metadata on the original assertion transaction is possible and changes both sampled relationships that share that transaction. The old immutable database retains the old metadata. A separate speculative d/with test successfully replaced both eight-slot tuple values atomically by changing their from component; the old tuple values were absent and both new values present. These two follow-up cases used d/with, not a committed write. + +## Cache implications + +On one unchanged database basis (1003003) and one unchanged assertion tx (13194140534818), the same forward and reverse identity was inactive at valid-at=1000 and active at valid-at=1200. No transaction is required for time-dependent answers to change. d/filter alone therefore cannot make a cache keyed only by database/relationship versions coherent. Existing EACL relation stamps are changed by writes; see modules/eacl-datomic/src/eacl/datomic/schema.clj:65 and backend.clj:293. + +A cache needs valid-time scope: exact valid-at, or a provably stable interval/epoch bounded by relevant transitions. Negative results must stop being reused when a future relationship becomes active. Metadata maps must also be scoped to their source database basis or maintained with correct version tracking. A stale incomplete sparse map would incorrectly treat an unseen qualified transaction as permanent. The measured cache is tied to one frozen basis. + +## Interpretation + +High confidence: metadata filtering is semantically viable; unchanged-EAV reassertion does not replace the original assertion tx; same-tx retract/reassert is rejected; clock changes need explicit cache scope. Moderate confidence for performance generalization: cached metadata is competitive with inline validity for small warm reads in this distribution, while per-candidate entity lookup is much slower on full scans. The earlier 111 ms first-page result with 999,980 expired predecessors is not representative of this evenly distributed 5% fixture. Local expired-prefix density, not merely global percentage, governs how many candidates a page skips. + +No cold-cache, one-transaction-per-relationship, mixed concurrent writes, full authorization graph, or production cache coherence implementation was benchmarked. + +## Reproduction and evidence + +Scripts: benchmark.clj, rescheduling.clj, followup.clj, report.clj. Raw observations and summaries are adjacent EDN files. Load the benchmark script in a dedicated nREPL using the core classpath, call seed! with 1000000, build-sparse!, verify!, warm-shared!, run-latencies! for page (7 500 1000) and point (7 2000 2000), and run-full-counts! (7 1000) and (1 1200). Run rescheduling/run-all! for mem and dev prefixes; it deletes its own tiny databases. Cleanup the benchmark database using its saved URI. + +Official reference: https://docs.datomic.com/reference/filters.html (filtering transaction attributes) and https://docs.datomic.com/transactions/transaction-data-reference.html (redundant assertions). diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling-dev.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling-dev.edn new file mode 100644 index 00000000..b926e86f --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling-dev.edn @@ -0,0 +1,124 @@ +{:uri-prefix "datomic:dev://localhost:4334/", + :java "26.0.2", + :cases + [{:mode :reassert, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :gap nil, + :after + {:basis-t 1006, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :new-transaction-until 200, + :same-assertion-tx true, + :relationship-changes []} + {:mode :retract-then-assert, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :error + {:class "datomic.impl.Exceptions$IllegalArgumentExceptionInfo", + :message + ":db.error/datoms-conflict Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true]}\n", + :data + {:cognitect.anomalies/category :cognitect.anomalies/incorrect, + :cognitect.anomalies/message + "Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true]}\n", + :d1 + [17592186045418 :probe/f [:user 17592186045420 :resource 17592186045419] 13194139534318 false], + :d2 + [17592186045418 :probe/f [:user 17592186045420 :resource 17592186045419] 13194139534318 true], + :db/error :db.error/datoms-conflict, + :tempids {"datomic.tx" 13194139534318}}}, + :after + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}} + {:mode :assert-then-retract, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :error + {:class "datomic.impl.Exceptions$IllegalArgumentExceptionInfo", + :message + ":db.error/datoms-conflict Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false]}\n", + :data + {:cognitect.anomalies/category :cognitect.anomalies/incorrect, + :cognitect.anomalies/message + "Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false]}\n", + :d1 + [17592186045418 :probe/f [:user 17592186045420 :resource 17592186045419] 13194139534318 true], + :d2 + [17592186045418 :probe/f [:user 17592186045420 :resource 17592186045419] 13194139534318 false], + :db/error :db.error/datoms-conflict, + :tempids {"datomic.tx" 13194139534318}}}, + :after + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}} + {:mode :two-transactions, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :gap + {:basis-t 1006, + :forward-present false, + :reverse-present false, + :forward-tx nil, + :reverse-tx nil, + :until nil, + :visible-at-150 false}, + :after + {:basis-t 1007, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534319, + :reverse-tx 13194139534319, + :until 200, + :visible-at-150 true}, + :new-transaction-until 200, + :same-assertion-tx false, + :relationship-changes + [{:e 17592186045418, + :a 73, + :v [:user 17592186045420 :resource 17592186045419], + :tx 13194139534319, + :added true} + {:e 17592186045419, + :a 74, + :v [:resource 17592186045420 :user 17592186045418], + :tx 13194139534319, + :added true}]}]} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling-mem.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling-mem.edn new file mode 100644 index 00000000..12345d50 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling-mem.edn @@ -0,0 +1,138 @@ +{:uri-prefix "datomic:mem://", + :java "26.0.2", + :cases + [{:mode :reassert, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :gap nil, + :after + {:basis-t 1006, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :new-transaction-until 200, + :same-assertion-tx true, + :relationship-changes []} + {:mode :retract-then-assert, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :error + {:class "datomic.impl.Exceptions$IllegalArgumentExceptionInfo", + :message + ":db.error/datoms-conflict Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true]}\n", + :data + {:cognitect.anomalies/category :cognitect.anomalies/incorrect, + :cognitect.anomalies/message + "Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true]}\n", + :d1 + [17592186045418 + :probe/f + [:user 17592186045420 :resource 17592186045419] + 13194139534318 + false], + :d2 + [17592186045418 + :probe/f + [:user 17592186045420 :resource 17592186045419] + 13194139534318 + true], + :db/error :db.error/datoms-conflict}}, + :after + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}} + {:mode :assert-then-retract, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :error + {:class "datomic.impl.Exceptions$IllegalArgumentExceptionInfo", + :message + ":db.error/datoms-conflict Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false]}\n", + :data + {:cognitect.anomalies/category :cognitect.anomalies/incorrect, + :cognitect.anomalies/message + "Two datoms in the same transaction conflict\n{:d1\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n true],\n :d2\n [17592186045418\n :probe/f\n [:user 17592186045420 :resource 17592186045419]\n 13194139534318\n false]}\n", + :d1 + [17592186045418 + :probe/f + [:user 17592186045420 :resource 17592186045419] + 13194139534318 + true], + :d2 + [17592186045418 + :probe/f + [:user 17592186045420 :resource 17592186045419] + 13194139534318 + false], + :db/error :db.error/datoms-conflict}}, + :after + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}} + {:mode :two-transactions, + :before + {:basis-t 1005, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534317, + :reverse-tx 13194139534317, + :until 100, + :visible-at-150 false}, + :gap + {:basis-t 1006, + :forward-present false, + :reverse-present false, + :forward-tx nil, + :reverse-tx nil, + :until nil, + :visible-at-150 false}, + :after + {:basis-t 1007, + :forward-present true, + :reverse-present true, + :forward-tx 13194139534319, + :reverse-tx 13194139534319, + :until 200, + :visible-at-150 true}, + :new-transaction-until 200, + :same-assertion-tx false, + :relationship-changes + [{:e 17592186045418, + :a 73, + :v [:user 17592186045420 :resource 17592186045419], + :tx 13194139534319, + :added true} + {:e 17592186045419, + :a 74, + :v [:resource 17592186045420 :user 17592186045418], + :tx 13194139534319, + :added true}]}]} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling.clj b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling.clj new file mode 100644 index 00000000..0debcf01 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/rescheduling.clj @@ -0,0 +1,97 @@ +(ns v9-txmeta-rescheduling + (:require [datomic.api :as d] + [clojure.pprint :as pp])) + +(def tuple-types [:db.type/keyword :db.type/ref :db.type/keyword :db.type/ref]) +(def schema + [{:db/ident :probe/f :db/valueType :db.type/tuple :db/tupleTypes tuple-types + :db/cardinality :db.cardinality/many :db/index true} + {:db/ident :probe/r :db/valueType :db.type/tuple :db/tupleTypes tuple-types + :db/cardinality :db.cardinality/many :db/index true} + {:db/ident :probe/until :db/valueType :db.type/long :db/cardinality :db.cardinality/one} + {:db/ident :probe/id :db/valueType :db.type/string :db/cardinality :db.cardinality/one + :db/unique :db.unique/identity}]) + +(defn root-cause [e] + (loop [x e] + (if-let [cause (.getCause x)] (recur cause) + {:class (.getName (class x)) :message (.getMessage x) :data (ex-data x)}))) + +(defn run-case! [uri-prefix mode] + (let [uri (str uri-prefix "v9-txmeta-reschedule-" (random-uuid)) + _ (d/create-database uri) + conn (d/connect uri)] + (try + @(d/transact conn schema) + @(d/transact conn [{:probe/id "s"} {:probe/id "r"} {:probe/id "rel"}]) + (let [db (d/db conn) + s (d/entid db [:probe/id "s"]) + r (d/entid db [:probe/id "r"]) + rel (d/entid db [:probe/id "rel"]) + f [:user rel :resource r] + rev [:resource rel :user s] + adds [[:db/add s :probe/f f] [:db/add r :probe/r rev]] + retracts [[:db/retract s :probe/f f] [:db/retract r :probe/r rev]] + fa (d/entid db :probe/f) + ra (d/entid db :probe/r) + meta200 {:db/id "datomic.tx" :probe/until 200} + read-state + (fn [db] + (let [fv (first (d/datoms db :eavt s :probe/f)) + rv (first (d/datoms db :eavt r :probe/r)) + tx (:tx fv) + until (when tx (:probe/until (d/entity db tx))) + filtered (d/filter db (fn [raw datom] + (or (not (#{fa ra} (:a datom))) + (let [end (:probe/until (d/entity raw (:tx datom)))] + (or (nil? end) (< 150 end))))))] + {:basis-t (d/basis-t db) + :forward-present (some? fv) :reverse-present (some? rv) + :forward-tx (:tx fv) :reverse-tx (:tx rv) + :until until + :visible-at-150 (boolean (seq (d/datoms filtered :eavt s :probe/f f)))})) + first-report @(d/transact conn (conj adds {:db/id "datomic.tx" :probe/until 100})) + before (read-state (:db-after first-report))] + (try + (let [gap (when (= mode :two-transactions) + (read-state (:db-after @(d/transact conn retracts)))) + data (case mode + :reassert (conj adds meta200) + :retract-then-assert (into [meta200] (concat retracts adds)) + :assert-then-retract (into [meta200] (concat adds retracts)) + :two-transactions (conj adds meta200)) + report @(d/transact conn data) + after (read-state (:db-after report))] + {:mode mode :before before :gap gap :after after + :new-transaction-until (:probe/until (d/entity (:db-after report) (d/t->tx (d/basis-t (:db-after report))))) + :same-assertion-tx (= (:forward-tx before) (:forward-tx after)) + :relationship-changes (mapv (fn [dt] {:e (:e dt) :a (:a dt) :v (:v dt) :tx (:tx dt) :added (:added dt)}) + (filter #(#{fa ra} (:a %)) (:tx-data report)))}) + (catch Exception e {:mode mode :before before :error (root-cause e) + :after (read-state (d/db conn))}))) + (finally + (d/release conn) + (d/delete-database uri))))) + +(defn run-all! [uri-prefix output-path] + (let [result {:uri-prefix uri-prefix :java (System/getProperty "java.version") + :cases (mapv #(run-case! uri-prefix %) + [:reassert :retract-then-assert :assert-then-retract :two-transactions])}] + (doseq [{:keys [mode before after gap error relationship-changes new-transaction-until]} (:cases result)] + (assert (= 100 (:until before))) + (case mode + :reassert + (do (assert (nil? error)) (assert (= 200 new-transaction-until)) + (assert (> (:basis-t after) (:basis-t before))) + (assert (= (:forward-tx before) (:forward-tx after))) + (assert (= 100 (:until after))) (assert (empty? relationship-changes))) + (:retract-then-assert :assert-then-retract) + (do (assert (= :db.error/datoms-conflict (get-in error [:data :db/error]))) + (assert (= before after))) + :two-transactions + (do (assert (nil? error)) (assert (false? (:forward-present gap))) + (assert (false? (:reverse-present gap))) (assert (:visible-at-150 after)) + (assert (= 200 (:until after))) + (assert (not= (:forward-tx before) (:forward-tx after)))))) + (spit output-path (with-out-str (pp/pprint result))) + result)) diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/shared-cache-warmup.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/shared-cache-warmup.edn new file mode 100644 index 00000000..f96d8273 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/shared-cache-warmup.edn @@ -0,0 +1 @@ +{:count 950000, :elapsed-ms 180.397458, :cached-transactions 1500} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/sparse-build.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/sparse-build.edn new file mode 100644 index 00000000..132924dc --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/sparse-build.edn @@ -0,0 +1,3 @@ +{:elapsed-ms 2.619125, + :metadata-datoms 1000, + :qualified-transactions 1000} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/verification.edn b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/verification.edn new file mode 100644 index 00000000..3a5f21a2 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/evidence/txmeta-2026-09-04/verification.edn @@ -0,0 +1,4 @@ +{:physical-counts + #:bench{:f4 1000000, :r4 1000000, :f8 1000000, :r8 1000000}, + :page-and-point-assertions 2016, + :status :passed} diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/proposal.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/proposal.md new file mode 100644 index 00000000..86583566 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/proposal.md @@ -0,0 +1,99 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](review-2026-09-04.md). The original artifact follows unchanged. + +## Why + +EACL needs scheduled, expiring, and Caveat-qualified relationships without +giving up the two-datom endpoint representation introduced by v7. The current +four-slot relationship ABI cannot carry these qualifiers, and adding Caveats +after validity would otherwise force another persisted storage break. + +## What Changes + +- **BREAKING**: Replace v7 relationship endpoint values with one fixed + eight-component v9 layout: + - forward: + `[subject-type relation-eid resource-type resource-eid caveat-eid caveat-context-eid valid-from-ms valid-until-ms]` + - reverse: + `[resource-type relation-eid subject-type subject-eid caveat-eid caveat-context-eid valid-from-ms valid-until-ms]` +- Preserve the owning endpoint eid plus the first four components as the + logical relationship identity; a four-component prefix is unique only + within one endpoint. + Caveat definition, relationship-bound Caveat context, and validity are + qualifiers and do not create distinct relationships. +- Keep exactly two authoritative relationship datoms. Do not restore a + first-class entity for every relationship. +- Store a Caveat definition as a shared schema entity in component five. Store + non-empty relationship-bound Caveat context in a sparse, immutable, + singly-owned context entity referenced by component six. Empty bound context + uses `nil` in component six. +- Put all Caveat components before validity components, while keeping every + qualifier after the opposite endpoint. This preserves one endpoint-ordered + candidate stream per graph hop and fixes the Phase 2 Caveat positions before Phase 1 + validity ships. +- Deliver v9 in two implementation phases: + - **Phase 1 — fixed v9 storage and native validity**: install the eight-slot + ABI, require Caveat slots to be `nil`, add trusted valid-time evaluation, + temporal cache/cursor proofs, and sparse expiration collection. + - **Phase 2 — SpiceDB-compatible Caveats**: parse and persist Caveat + definitions, validate Caveat-bearing relations and writes, store + relationship-bound context, evaluate Caveats with request context, expose + conditional permission results, and make caches/cursors context-complete. +- Match SpiceDB relationship identity semantics: one stored relationship may + exist for a subject/relation/resource identity regardless of Caveat or + expiration. `:create` conflicts with any existing qualifier variant, + `:touch` replaces qualifiers atomically, and `:delete` does not require the + qualifiers. +- **BREAKING**: Provide no v7-to-v9 relationship migration or dual-read path. + A v9 client accepts a fresh/rebuilt relationship store only and fails startup + when v7 relationship datoms remain. Fence Phase 2 activation with a + persisted semantic capability epoch so Phase 1 readers and writers cannot + serve a Caveat-enabled store. + +## Capabilities + +### New Capabilities + +- `temporal-relationship-validity`: Native future, expiring, and bounded + relationship semantics, trusted valid-time snapshots, temporal proofs, and + non-authoritative expiration collection. +- `relationship-caveats`: SpiceDB-compatible Caveat definitions, sparse + relationship-bound context storage, write validation, request-context + precedence, conditional permission results, and Caveat evaluation. + +### Modified Capabilities + +- `converged-relationship-storage`: Replace four-slot v7 endpoint values with + fixed eight-slot v9 values while retaining two authoritative datoms and one + ordered relationship source per traversal direction. +- `public-authorization`: Add Caveat request context and an explicit + three-state permission result while keeping `can?` fail-closed. +- `dependency-validated-authorization-cache`: Add temporal stability and + complete Caveat definition, bound-context, request-context, evaluator, and + conditional-result dependencies. +- `cursor-dependency-validity`: Bind continuation to valid-time, Caveat context, + Caveat dependencies, and conditional result semantics. + +## Impact + +- Affects the shared relationship codec and engine, every backend adapter + (Datomic Pro, Datahike, Datalevin, and DataScript), schema parsing and + persistence, relationship mutation and reads, permission APIs, object + cleanup, integrity reports, caches, cursors, formal models, and release + documentation. +- Changes the persisted relationship storage ABI to version 9 and invalidates + old cache snapshots, cursor tokens, and populated databases containing v7 + relationship values. +- Ordinary and validity-only relationships remain exactly two authoritative + endpoint datoms; finite expiration adds one derived index datom. Caveated + relationships add no relationship entity; only a non-empty + relationship-bound context adds one sparse context entity and payload datom. +- Requires operators upgrading a populated database to export/re-transact + Relationships into a fresh v9 database or otherwise rebuild all relationship + data before running v9. +- Encountered authoritative corruption or evaluator failure aborts the affected + operation with a typed error. Dropping a faulty edge is unsafe under exclusion. +- One candidate stream does not imply one total database read or bounded page + latency: integrity validation and inactive-prefix scanning have explicit costs + and release gates. diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/review-2026-09-04.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/review-2026-09-04.md new file mode 100644 index 00000000..96e41361 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/review-2026-09-04.md @@ -0,0 +1,102 @@ +# Disposition and findings — 2026-09-04 + +**Status: deprecated, superseded, unimplemented.** The [replacement v9 proposal](../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md) owns the implementation plan. Do not implement the eight-slot scheduled-validity proposal or sync its deltas into main specs. No completed implementation is being archived here. + +The original proposal, design, tasks, and six delta specs are retained byte-for-byte after their deprecation notices. Their original SHA-256 hashes are recorded in [the preservation manifest](evidence/original-artifact-sha256.json). OpenSpec artifact completeness means planning files exist; it does not mark this historical proposal implemented or active. + +## Decisions replacing the old design + +| Previous design | Replacement | Reason | +|---|---|---| +| Eight slots: identity, Caveat, context, start, end | Seven slots: identity, Caveat, context, end | Future activation is owned by consumers; an ordinary insertion invalidates the usual Relation dependencies. | +| Native scheduled activation | No `valid-from`; create when due | Removes future inactive edges, start-boundary tracking, interval validation, and rescheduling state. | +| Mandatory time-leading expiration index | Optional bounded scan-based collector | Efficient global collection is an operational optimization, not a prerequisite for correct expiry. | +| Two-sided temporal stability | Conservative forward-time expiry deadline plus evaluation-time lower bound | No future activation remains, but exclusion still requires deadlines on denials and continuations. | +| Historical integrity discussion could imply distrust of Datomic | Trust admitted atomic writes under the managed-writer contract | The engine provides atomicity and full-value set semantics; application-level identity and pair invariants remain the writer's responsibility. | + +Caveat definitions, immutable sparse context, typed encoding, conditional algebra, commit-time replacement guards, portable proofs, and rebuild-only deployment remain in scope. The original design's correctness findings remain useful unless explicitly superseded here. + +## What the latency measurements establish + +The original design records **111.445917 ms** median for a first effective page that advanced **1,000,000** candidates to return **20** results. That fixture placed **999,980 expired Relationships before every live result**. It establishes an adversarial scan bound, not expected latency for a workload with 5% temporal qualifiers. + +The subsequent [benchmark report](evidence/txmeta-2026-09-04/report.md) and adjacent scripts/raw EDN preserve the requested 5% experiment. The isolated local `:dev` database held the same million logical identities in four-slot and eight-slot layouts, each with forward and reverse values. Independent enumeration verified all four million endpoint datoms. At synthetic evaluation time 1000, 950,000 were active, 25,000 expired, and 25,000 not yet active; at 1200, 975,000 were active. + +| Warm read primitive | 20-result page median | Page p95 | Full scan median | +|---|---:|---:|---:| +| Four-slot raw control, no temporal filtering | 8.416 µs | 12.708 µs | 95.838 ms | +| Eight-slot inline bounds, manual filtering | 8.791 µs | 14.167 µs | 102.992 ms | +| Eight-slot inline bounds, `d/filter` | 10.000 µs | 15.708 µs | 129.471 ms | +| Four-slot, tx entity lookup per candidate | 34.959 µs | 48.166 µs | 860.894 ms | +| Four-slot, request-local tx bounds memo | 11.917 µs | 18.125 µs | 136.384 ms | +| Four-slot, shared tx bounds memo | 9.125 µs | 14.625 µs | 133.632 ms | +| Four-slot, complete sparse tx bounds map | 9.750 µs | 14.917 µs | 130.239 ms | + +These are warm single-threaded Peer index primitives, not public EACL authorization latency. They include per-request filtered-view construction but exclude separately reported shared-map preparation. There were 1,500 assertion transactions for one million Relationships: metadata sharing was unusually favorable. The sparse bounds map had 1,000 qualified transactions, not 50,000 independent schedules. Its absence-as-permanent interpretation is valid only for a complete map at the selected immutable basis. + +Instrumentation found 21–22 logical candidates through the twentieth result, but 34–35 actual `d/filter` predicate invocations due to read-ahead in those calls. Neither measure is a physical disk-read count. Four-slot point probes used exact full tuples; eight-slot probes used identity-prefix seeks, so their difference cannot be attributed only to tuple width. + +**Confidence: high** in the verified counts, equality assertions, and observed primitive behavior; **moderate** in the measured warm latency comparison; **unknown** for production, cold/concurrent behavior, actual seven-slot expiry-only performance, or physical byte savings. No benchmark of the replacement has been performed. The 5% experiment includes future activation and therefore is supporting evidence, not an expiry-only qualification run. + +## Why two endpoint tuples remain + +One forward value covers adjacency from a known subject; one reverse value covers adjacency from a known resource. Both include Caveat refs and expiry, so either traversal direction can test expiry without fetching the other half or a schedule. A single tuple can carry all qualifiers, but a single subject-owned value does not also provide resource-owned adjacency with the existing ordered access contract. + +Leading expiry belongs only in a separate collection index if one is later justified. Keeping the opposite endpoint before qualifiers preserves endpoint order and identity-prefix seeks. A trailing deadline cannot support a global deadline range over all Relationships. Initial collection therefore scans; this trades maintenance throughput for fewer maintained index values and simpler writes. It does not change authorization semantics. + +An optional future derived time-leading index can be backfilled without changing the endpoint tuple ABI. It is not part of the replacement's required storage or implementation tasks. + +## Transaction metadata and renewal + +The preserved [in-memory](evidence/txmeta-2026-09-04/rescheduling-mem.edn) and [:dev](evidence/txmeta-2026-09-04/rescheduling-dev.edn) results independently verify: + +1. Reasserting identical forward/reverse EAV values with new tx metadata commits a new transaction, but Datomic redundancy elimination preserves each Relationship datom's original assertion transaction. In the probe, basis-t changed 1005→1006 while assertion tx stayed 13194139534317; effective expiry stayed 100 rather than 200. +2. Retracting and asserting an identical EAV in the same transaction failed with `:db.error/datoms-conflict` in either input order. +3. Retraction and assertion in two transactions changed the assertion tx, but exposed a committed state with both endpoint halves absent. +4. A separate speculative `d/with` probe changed expiry/start metadata on the original transaction entity. Both sampled Relationships sharing that transaction changed interpretation in the new database value; the old immutable database retained its previous metadata. This follow-up was speculative, not a committed rescheduling test. +5. Speculative replacement of the inline tuple values changed both halves atomically without the identical-EAV conflict. + +There is no supported public Datomic switch identified here for forcing redundant application assertions to acquire a new assertion transaction. Special behavior of system attributes is not such a switch. See the [transaction data reference](https://docs.datomic.com/transactions/transaction-data-reference.html). + +Mutable original tx metadata is possible, but it changes every affected datom's interpretation and requires complete affected-Relation invalidation, basis-scoped metadata caches, and different transaction grouping for different deadlines. A schedule-ref would decouple that grouping but add indirection and an auxiliary lifecycle. Inline expiry avoids both costs and permits arbitrary deadlines in one transaction. + +`d/filter` is a read mechanism, not an independent temporal index or cache-coherence protocol. Its [official contract](https://docs.datomic.com/reference/filters.html) supports transaction-metadata predicates. The experiment establishes viability when metadata is cached, not superiority over inline expiry. + +## Expiry-only simplifies storage more than cache algebra + +At a fixed database basis and fixed Caveat inputs, expiry-only removes edges as evaluation time increases. A positive-only permission cannot gain results from that removal. However, EACL supports exclusion: with `read = viewer - banned`, permanent `viewer`, and `banned` expiring at 100, a denial at 90 becomes permission at 100 without any transaction. + +Therefore grants **and denials** need certified expiry horizons when their proof can change. Relation-version equality alone is insufficient. The same applies to intermediate denotations and live cursors: expiry of a ban can introduce an authorized identity before an already-emitted cursor boundary. A stable final union result does not make its expiring child denotation stable. + +The replacement uses a conservative certificate `[evaluated-at, reusable-before)`, with nil upper bound only when no future expiry can alter that artifact. It checks the selected evaluation time on every hit. Exact-time identity or no publication is the fallback. Positive-only denial reuse beyond expiry is a possible proven optimization, not a default assumption about arbitrary permission expressions. + +## Collection, versions, and retained history + +Clock passage performs no transaction and does not bump `:eacl/relation-version`. Tuple expiry checks and temporal certificates enforce it directly. A collector is optional and can be delayed or disabled. + +Actual collector deletion is a managed Relationship mutation. The replacement requires the same atomic pair/context removal, commit-time guards, and per-affected-Relation stamp as other deletions. The current Datomic writer uses the Relation stamp as a compare-and-set guard as well as a cache generation (`modules/eacl-datomic/src/eacl/datomic/impl.clj`, `tx-relation-version-stamp` and `optimistic-relationship-tx-data`). Omitting it would change the concurrency protocol. The correctly spelled persisted attribute is `:eacl/relation-version`. + +Deleting an already expired assertion may preserve effective authorization for evaluation times at/after the cutoff, but still changes stored reads, identity conflicts, source content, and potentially earlier-time interpretation. Avoiding invalidation on that basis needs a separate proof and is deferred. Index-only repair in a future derived index would not itself alter authoritative Relationship proof data. + +Datomic retraction removes current assertions and normally retains history. Collection is not excision and makes no promise to reclaim durable bytes. See [Datomic's index model](https://docs.datomic.com/indexes/index-model.html) and [excision](https://docs.datomic.com/operation/excision.html). Historical reconstruction needs an appropriate retained database basis; applying an earlier evaluation time to the post-collection current basis cannot recreate deleted assertions. + +## Integrity is an application invariant + +Datomic's atomic transactions and set uniqueness work as specified. They do not make two arbitrary attributes a logically inseparable pair or impose uniqueness on owner plus a prefix of a tuple. Distinct complete tuple values are distinct facts. A stale unguarded replacement plan can therefore leave multiple qualifier variants without a storage-engine defect. + +Managed EACL writers must preserve those application invariants atomically. Qualified managed sources need not re-read the reverse half of every forward edge solely to distrust storage. Unknown writers and explicit integrity/repair operations have a different proof obligation. If an actual authoritative fault or evaluator failure is encountered, abort the affected operation: dropping a malformed subtracting edge could grant access. These are distinct rules, and neither implies that Datomic randomly corrupts Relationships. + +## Datahike valid-time alternative + +Datahike's [experimental Valid Time feature](https://github.com/replikativ/datahike/blob/main/doc/valid_time.md) is relevant but is not a portable drop-in for EACL's new contract. Read-only inspection of the installed 0.8.1759 JAR found its valid-at implementation uses a filtered database with transaction coverage and per-fact winner logic. It has history/supersession semantics beyond expiry of current stored endpoint values, shares transaction-level bounds, and does not remove the need for time-aware cache proofs. + +No Datahike valid-time benchmark was run. The observed Datomic predicate timings must not be attributed to Datahike's implementation. The replacement neither depends on this experimental API nor changes EACL's pinned Datahike version. + +## Qualification still required + +- Benchmark real seven-slot expiry-only storage and public authorization; vary expiry proportion, local clustering, endpoint degree, direction, cache temperature, transaction diversity, and concurrent renewal/collection. +- Set numerical acceptance budgets before qualification; retain complete proof/payload/evaluator costs rather than reporting only one seek. +- Verify all backend ordering and guarded writer topologies. Datomic results alone do not certify DataScript, Datahike, or Datalevin. +- Pin a concrete SpiceDB reference release/commit and a type/coercion/encoding profile, then pass CLJ/CLJS and differential Caveat fixtures. No full CEL compatibility is claimed. + +The evidence directory includes the original measurement scripts and raw results, with [SHA-256 checksums](evidence/txmeta-2026-09-04/SHA256SUMS.json). The dedicated benchmark database was deleted and its processes stopped; [cleanup evidence](evidence/txmeta-2026-09-04/cleanup.edn) records that disposition. Reproduction requires a new isolated database and deliberate nREPL setup; it must not reuse a production URI. diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/converged-relationship-storage/spec.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/converged-relationship-storage/spec.md new file mode 100644 index 00000000..00956a9a --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/converged-relationship-storage/spec.md @@ -0,0 +1,503 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../../../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](../../review-2026-09-04.md). The original artifact follows unchanged. + +## MODIFIED Requirements + +### Requirement: Two-value endpoint representation + +Each EACL relationship SHALL be represented by exactly two cardinality-many, +indexed, authoritative endpoint values: a forward value on the subject entity +and a reverse value on the resource entity. The values SHALL use this fixed +eight-component v9 order: + +- forward: + `[subject-type relation-eid resource-type resource-eid caveat-eid caveat-context-eid valid-from-ms valid-until-ms]` +- reverse: + `[resource-type relation-eid subject-type subject-eid caveat-eid caveat-context-eid valid-from-ms valid-until-ms]` + +The owning endpoint eid plus the first four components SHALL identify the +logical relationship. The first four components alone SHALL be only an +endpoint-local key and MUST NOT be used as a global relationship identity. Components +five through eight SHALL be qualifiers and SHALL be `nil` when absent. Both +physical halves SHALL contain the same Caveat definition ref, Caveat context +ref, and validity bounds. + +A non-nil Caveat context ref SHALL require a non-nil Caveat definition ref. A +non-nil Caveat definition ref with a nil context ref SHALL represent a Caveat +with empty relationship-bound context. EACL SHALL store no entity whose sole +purpose is to represent every relationship. + +#### Scenario: Relationship creation + +- **WHEN** EACL creates a relationship between existing subject and resource endpoints +- **THEN** the backend stores exactly one forward endpoint datom and one reverse endpoint datom and stores no relationship entity + +#### Scenario: Permanent uncaveated relationship + +- **WHEN** EACL creates a relationship without a Caveat or validity bounds +- **THEN** it stores exactly one eight-slot forward value and one eight-slot + reverse value +- **AND** slots five through eight are `nil` +- **AND** it stores no relationship entity + +#### Scenario: Validity-only relationship + +- **WHEN** EACL creates a relationship with validity bounds and no Caveat +- **THEN** slots five and six are `nil` +- **AND** slots seven and eight contain the normalized validity bounds +- **AND** both endpoint halves contain identical qualifiers + +#### Scenario: Caveat with empty bound context + +- **WHEN** Phase 2 creates a relationship with a Caveat and no + relationship-bound context values +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six is `nil` +- **AND** no Caveat context entity is allocated + +#### Scenario: Caveat with bound context + +- **WHEN** Phase 2 creates a relationship with non-empty relationship-bound + Caveat context +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six contains the same sparse Caveat context eid in both halves +- **AND** the context payload is not duplicated inside the endpoint values + +#### Scenario: Context without Caveat + +- **WHEN** an update supplies or stored data contains a non-nil slot six with a + nil slot five +- **THEN** admitted writes reject it +- **AND** an affected operation rejects out-of-band authoritative corruption with a typed error + +#### Scenario: Duplicate physical value + +- **WHEN** the same complete endpoint value is added more than once in one database value +- **THEN** database set semantics retain one datom for that endpoint value + +#### Scenario: Same tuple value on two owners + +- **WHEN** two different subjects legitimately produce the same forward tuple value +- **THEN** their logical identities remain different because the owner eid is part of the key + +#### Scenario: Duplicate complete physical value + +- **WHEN** the same complete eight-slot endpoint value is added more than once + in one database value +- **THEN** database set semantics retain one datom for that endpoint value + +### Requirement: Ordered endpoint index access + +Every backend adapter SHALL implement candidate access for exact matching, adjacency, relationship +filtering, relation-in-use checks, and forward and reverse pagination using the single v9 endpoint attribute appropriate to the requested direction. +Pair, ownership, and content-proof validation MAY require additional reads. The first +three components SHALL remain the typed relation prefix, the fourth component +SHALL remain the opposite endpoint used for ordering and pagination, and all +four qualifier components SHALL remain trailing. + +Every tuple/vector seek boundary SHALL use the complete stored arity required by +the backend ordering contract. Normal v9 authorization and relationship reads +MUST NOT scan, union, merge, or prefer values from a v7 relationship attribute. + +#### Scenario: Forward endpoint scan + +- **WHEN** authorization or relationship pagination scans outward from a known + subject +- **THEN** the adapter performs one seek against that subject's v9 forward + endpoint values +- **AND** returns candidates ordered by resource eid before evaluating + qualifiers + +#### Scenario: Reverse endpoint scan + +- **WHEN** authorization or relationship pagination scans inward from a known + resource +- **THEN** the adapter performs one seek against that resource's v9 reverse + endpoint values +- **AND** returns candidates ordered by subject eid before evaluating + qualifiers + +#### Scenario: Exact logical relationship probe + +- **WHEN** EACL probes one known subject, relation, and resource +- **THEN** it seeks by the first four components using full eight-slot low and + high bounds +- **AND** obtains zero or one well-formed logical relationship +- **AND** treats multiple qualifier variants as corruption rather than choosing + a grant + +#### Scenario: Reverse-order continuation + +- **WHEN** a descending scan resumes from a relationship cursor +- **THEN** `rseek-datoms` or its backend equivalent starts from a full + eight-slot high bound; nil is not used as a high qualifier sentinel +- **AND** returns only values inside the requested endpoint prefix and ordering + boundary + +#### Scenario: Adjacent prefix is absent + +- **WHEN** no value exists for a requested prefix but an adjacent prefix exists +- **THEN** explicit entity, attribute, arity, and component guards prevent the + adjacent value from being returned + +#### Scenario: Legacy attributes remain installed but empty + +- **WHEN** a Datomic database still has immutable v7 attribute definitions but + no v7 relationship datoms +- **THEN** v9 reads never seek those attributes +- **AND** their schema presence adds no per-hop second seek + +### Requirement: Atomic pair mutation and repair + +Relationship mutation SHALL add, replace, or retract both v9 endpoint values in +one admitted transaction. Every planned create, touch, delete, or collection +SHALL validate at commit time the complete identity-group and auxiliary state +on which its plan depends and SHALL preserve `:create`, `:touch`, and `:delete` +semantics over the owner-qualified first-four-component logical identity. + +`:create` SHALL conflict with every stored temporal or Caveat state. +`:touch` SHALL create when absent and otherwise replace the exact old endpoint +pair, Caveat context entity, and expiration index with the requested canonical +state. `:delete` SHALL require only the logical relationship and SHALL remove +all discoverable physical variants so it can repair bounded corruption. +A writer topology unable to provide commit-time state validation SHALL reject +qualified writes with a typed unsupported-writer error rather than use only +plan-time checks. Exceeding the configured repair bound SHALL fail atomically. + +#### Scenario: Complete relationship conflict + +- **WHEN** `:create` targets a logical relationship whose complete forward and reverse values exist +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Racing creates of one relationship + +- **WHEN** two creates for one identity are planned against the same pre-write basis +- **THEN** commit-time validation permits exactly one creation +- **AND** the committed database contains exactly one canonical endpoint pair + +#### Scenario: Racing creates with different qualifiers + +- **WHEN** two `:create` transactions for one logical identity are planned + against the same pre-write value with different qualifiers +- **THEN** exactly one creation commits +- **AND** the other fails with `:eacl/relationship-conflict` +- **AND** one canonical forward/reverse pair remains + +#### Scenario: Stale concurrent touches + +- **WHEN** two different touches are planned from the same old qualifiers and commit serially +- **THEN** the second plan cannot add its replacement after its expected old state changed +- **AND** it replans or fails with a typed conflict rather than leaving two variants + +#### Scenario: Touch replaces all qualifiers + +- **WHEN** `:touch` changes the Caveat, relationship-bound context, or validity +- **THEN** EACL retracts the exact old forward and reverse values +- **AND** adds one matching new pair +- **AND** creates/retracts sparse context and expiration data atomically +- **AND** advances the affected relation mutation identity exactly once + +#### Scenario: Touch is unchanged + +- **WHEN** `:touch` supplies qualifiers canonically equal to the stored state +- **THEN** EACL may treat the operation as an idempotent no-op +- **AND** it does not allocate an unnecessary replacement context entity + +#### Scenario: Repeated and conflicting batch updates + +- **WHEN** one batch repeats an identical update for one logical relationship +- **THEN** the batch has the same outcome as one occurrence +- **WHEN** one batch contains semantically different updates for that identity +- **THEN** EACL rejects it with + `:eacl/invalid-relationship-update-batch` before transaction submission + +#### Scenario: Incomplete relationship repair + +- **WHEN** `:touch` targets missing, mismatched, or duplicated endpoint halves +- **THEN** EACL removes bounded discoverable variants and writes one matching + canonical pair + +#### Scenario: Unconditional deletion + +- **WHEN** `:delete` targets a complete, incomplete, corrupt, or absent pair +- **THEN** EACL removes every variant that is safely attributable within the configured repair bound +- **AND** it never retracts auxiliary data whose exclusive ownership has not been established + +#### Scenario: Delete omits qualifiers + +- **WHEN** `:delete` supplies the subject, relation, and resource but no Caveat, + context, or validity +- **THEN** EACL removes the matching logical relationship if present +- **AND** removes its singly-owned context and expiration-index data + +### Requirement: Endpoint deletion and integrity + +Every backend adapter SHALL treat peer eids, Caveat definition eids, and Caveat +context eids embedded in endpoint values according to its native value +semantics. EACL object cleanup SHALL explicitly remove every touching endpoint +pair and owned auxiliary data before endpoint retraction. + +The module SHALL expose an offline integrity report covering malformed arity, +dangling or mismatched halves, qualifier disagreement, owner-qualified identity +collisions, invalid Caveat/context combinations, missing or multiply-owned +context entities, malformed intervals, and stale or missing expiration-index +entries. An authoritative integrity fault encountered in an operation MUST +abort that operation with `:eacl/invalid-relationship-state`; it MUST NOT be modeled as +relationship absence, because absence on the subtracting side of exclusion can +grant access. Derived expiration-index mismatches remain diagnostics and SHALL +not fail authorization. + +#### Scenario: EACL object cleanup + +- **WHEN** `delete-object!` removes an endpoint +- **THEN** it removes local and peer endpoint values for every touching + relationship +- **AND** removes singly-owned Caveat context and expiration-index values +- **AND** publishes every affected relation mutation identity + +#### Scenario: Direct endpoint retraction + +- **WHEN** a consumer retracts an endpoint entity outside EACL +- **THEN** peer halves and context entities can remain as detectable corruption + because embedded refs are not relied upon for automatic cascading + +#### Scenario: Integrity scan + +- **WHEN** the offline integrity report scans the database +- **THEN** it reports each invalid pair, identity collision, Caveat/context + fault, interval fault, or auxiliary-index mismatch without mutating data + +#### Scenario: Corrupt relationship reaches authorization + +- **WHEN** authorization encounters an authoritative integrity fault on a candidate +- **THEN** the affected authorization operation fails with + `:eacl/invalid-relationship-state` +- **AND** it does not publish an answer or reusable continuation + +### Requirement: Database-visible proof coverage + +Unknown-writer relationship proofs SHALL commit to both complete eight-slot +endpoint values, the public identities of their endpoints, and all authoritative +relationship-bound Caveat context payloads. They SHALL NOT include the derived +expiration index. A proof SHALL separately validate structure; digest change +detection alone is not a validity certificate. A proof MUST change when any +answer-affecting relationship qualifier or referenced context changes out of band. + +Caveat definitions SHALL be covered by the schema proof and generation. + +#### Scenario: One half changes out of band + +- **WHEN** one endpoint half changes for a relation in a cached request's + dependency closure +- **THEN** the next content-proof validation rejects the previous answer + +#### Scenario: Context payload changes out of band + +- **WHEN** a referenced context entity retains its eid but its payload changes +- **THEN** the database-visible content proof changes +- **AND** the prior answer is not reused + +#### Scenario: Qualifier ref changes out of band + +- **WHEN** either Caveat ref or either validity bound changes without an + admitted relation-version update +- **THEN** the content proof changes and prior reuse is rejected + +#### Scenario: Endpoint identity changes out of band + +- **WHEN** an endpoint's public identity changes while its eid remains stable +- **THEN** the content proof changes and prior authorization results are not + reused + +### Requirement: Shared logical storage layer + +The core workspace SHALL provide backend-neutral pure functions for fixed-arity +v9 endpoint construction, decoding, logical-identity extraction, Caveat/context +qualifier validation, peer-half construction, exact retractions, interval +validation, and prefix validation wherever all backends share behavior. +Backend-specific index, transaction, clock, schema, and history operations SHALL +remain inside adapters. + +#### Scenario: Equivalent relationship + +- **WHEN** all supported adapters encode the same relationship and qualifiers +- **THEN** their forward and reverse values have identical eight-component + order +- **AND** decode to the same endpoints, Caveat ref, context ref, and validity + +#### Scenario: Permanent normalization + +- **WHEN** a relationship omits every qualifier +- **THEN** the shared codec emits four explicit trailing `nil` values rather + than a shorter value + +#### Scenario: Backend-specific database access + +- **WHEN** an adapter reads or writes endpoint values +- **THEN** it uses its native APIs without introducing backend dependencies + into the shared codec + +### Requirement: Cross-runtime validation + +The v9 endpoint-pair adapter SHALL satisfy the shared backend contract and each +backend's storage, pagination, consistency, mutation, cache-proof, cleanup, +integrity, temporal, and Caveat tests. DataScript SHALL satisfy the same logical +contract on JVM and ClojureScript runtimes. + +#### Scenario: JVM suite + +- **WHEN** the JVM backend tests run +- **THEN** direct and recursive authorization, lookup, count, relationship read/write, proof, deletion, and integrity behavior pass + +#### Scenario: JVM backend suites + +- **WHEN** Datomic Pro, Datahike, Datalevin, and DataScript JVM suites run +- **THEN** direct and recursive authorization, lookup, count, relationship + reads/writes, qualifier evaluation, cache proof, cleanup, collection, and + integrity behavior pass + +#### Scenario: ClojureScript suite + +- **WHEN** the compiled DataScript ClojureScript suite runs under Node +- **THEN** fixed eight-slot ordering, seeks, pagination, validity, Caveat + evaluation, mutation, and corruption handling match JVM semantics + +#### Scenario: Single-source traversal benchmark + +- **WHEN** v9 direct checks, adjacency, arrows, lookup, and pagination are + benchmarked against v7 +- **THEN** every logical relation hop obtains candidates from one authoritative + endpoint stream +- **AND** total reads, integrity-proof work, candidates advanced, tuple bytes, + and active-Caveat overhead are measured without a hidden + v7 fallback + +## ADDED Requirements + +### Requirement: One logical relationship regardless of qualifiers + +Logical relationship identity SHALL consist of the endpoint owner eid plus the +first four endpoint components. Conflict keys and batch deduplication MUST NOT +omit the owner. Caveat definition, Caveat context, `valid-from`, and `valid-until` +SHALL NOT create distinct relationships. At most one stored relationship SHALL +exist for a logical identity. + +This rule SHALL match SpiceDB behavior: relationships differing only in Caveat, +Caveat context, or expiration cannot coexist. + +#### Scenario: Different Caveat is not a second relationship + +- **WHEN** a relationship already exists and a caller attempts `:create` with + the same logical identity and a different Caveat +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Different context is not a second relationship + +- **WHEN** a relationship already exists and a caller attempts `:create` with + different relationship-bound context +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Different validity is not a second relationship + +- **WHEN** a relationship already exists and a caller attempts `:create` with + different validity bounds +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Out-of-band variants coexist + +- **WHEN** multiple complete endpoint values share the same owner and first four components +- **THEN** integrity reports a logical-identity collision +- **AND** an affected operation fails with `:eacl/invalid-relationship-state` +- **AND** no qualifier precedence rule silently selects one variant + +### Requirement: Rebuild-only v9 compatibility boundary + +The v9 adapter SHALL use only the eight-slot endpoint-pair layout for +authorization, mutation, reads, cleanup, integrity, and proof generation. EACL +SHALL NOT provide an automatic v7-to-v9 relationship migration, dual-read +compatibility mode, mixed-format authorization path, or per-relation fallback. + +A v9 client SHALL refuse startup when v7 relationship datoms remain or when the +persisted relationship-storage version is incompatible. It SHALL provide typed +rebuild/reseed guidance. Legacy Datomic schema definitions MAY remain inert. Every current-basis +selection and mutation commit SHALL also validate a persisted semantic +capability epoch so a long-lived Phase 1 client cannot serve or write after +Phase 2 activation. Activation SHALL drain or fence old clients before serving +Caveated current views; a capability epoch read from a retained old immutable +pin is insufficient. Without an enforceable fence, activation SHALL require a +coordinated stop/upgrade/activate cutover. + +#### Scenario: Populated v7 database + +- **WHEN** a v9 client opens a database containing any v7 forward or reverse + relationship datom +- **THEN** startup fails with `:eacl/storage-version` +- **AND** no authorization request interprets the old data +- **AND** the error directs the operator to rebuild or reseed Relationships + +#### Scenario: Fresh or rebuilt database + +- **WHEN** a database contains no old relationship datoms and has the required + v9 schema +- **THEN** EACL stamps or validates relationship storage version 9 +- **AND** every subsequently admitted relationship uses only v9 values + +#### Scenario: Mixed relationship data + +- **WHEN** both v7 and v9 relationship datoms are detected +- **THEN** EACL fails closed at startup rather than merging the graphs + +#### Scenario: Live Phase 1 client during Phase 2 activation + +- **WHEN** another writer atomically enables the Phase 2 Caveat capability epoch +- **THEN** a Phase 1 client fails its next current-basis selection or mutation +- **AND** it cannot treat a Caveated relationship as absent + +#### Scenario: Old cache or cursor + +- **WHEN** a cache artifact or cursor was produced for the old relationship ABI +- **THEN** v9 rejects it through the cache, engine, adapter, or token ABI + boundary + +### Requirement: Finite expiration index has one fixed physical layout + +Each relationship with finite `valid-until` SHALL add one derived indexed value +owned by its subject with order +`[valid-until-ms subject-type relation-eid resource-type resource-eid caveat-eid caveat-context-eid valid-from-ms]`. +Permanent and start-only relationships SHALL add none. Together with its owner, +the value SHALL reconstruct the exact authoritative endpoint pair. It SHALL not +participate in authorization proofs. + +#### Scenario: Expiration entry round trip + +- **WHEN** a collector reads one expiration-index datom +- **THEN** it reconstructs exact full-arity forward and reverse values +- **AND** commit-time guards prevent it from deleting a replacement pair + +#### Scenario: Expiration index is stale + +- **WHEN** the derived entry is missing or stale +- **THEN** authorization still follows the authoritative endpoint values +- **AND** maintenance diagnostics report the mismatch + +### Requirement: Candidate-stream work is explicitly bounded + +One endpoint seek SHALL mean one ordered candidate source, not constant work or +one total database read. Effective reads, counts, and authorization SHALL charge +every advanced inactive/corrupt candidate, lookahead item, validation read, +payload byte, and evaluator step to existing request limits. Exhaustion SHALL be +a typed failure and MUST NOT be returned as a successful empty/truncated result. + +#### Scenario: Clustered inactive prefix + +- **GIVEN** an endpoint has a long prefix of future or expired relationships +- **WHEN** an effective page seeks the first 20 active results +- **THEN** every inactive candidate advanced is charged to the request +- **AND** reaching the limit fails without asserting that the stream ended + +#### Scenario: Integrity validation costs reads + +- **WHEN** an unknown-writer source lacks a reusable basis integrity certificate +- **THEN** pair and auxiliary validation may perform reads beyond the one candidate stream +- **AND** telemetry reports those reads separately diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/cursor-dependency-validity/spec.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/cursor-dependency-validity/spec.md new file mode 100644 index 00000000..e5200074 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/cursor-dependency-validity/spec.md @@ -0,0 +1,142 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../../../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](../../review-2026-09-04.md). The original artifact follows unchanged. + +## ADDED Requirements + +### Requirement: Temporal continuations bind valid-at and stability horizon + +Every validity-aware continuation SHALL authenticate its temporal mode, +selected `valid-at`, and full temporal stability interval. A continuation +created from an explicit EACL snapshot SHALL use pinned mode. A client-targeted +query SHALL use live mode unless it explicitly selects a pinned snapshot. +Token expiry and snapshot-retention limits SHALL be checked independently from +relationship valid-time stability. + +Pinned resumption SHALL preserve the exact original valid-at and validate the +ordinary database dependencies. Live resumption SHALL capture one new trusted +valid-at and reuse retained state only when that value lies inside its certified +interval. Crossing either bound SHALL return a typed temporal-restart error and +no resumed page. The caller starts a new query; EACL MUST NOT silently reuse the +old position and skip newly active earlier candidates. + +Certificates SHALL cover retained frontier, skipped candidates, negative +evidence, lookahead, and residual state. Certifying only emitted items is +insufficient. Uncertified temporal reuse SHALL fall back to exact valid-at +identity or fail with a restart requirement. + +#### Scenario: Resume before temporal boundary + +- **WHEN** a live cursor resumes with a new valid-at inside its complete certified interval and equivalent database dependencies +- **THEN** EACL may reuse its continuation state at that new valid-at + +#### Scenario: Resume at temporal boundary + +- **WHEN** a live cursor's newly selected valid-at reaches or leaves either interval bound +- **THEN** EACL returns a typed temporal-restart error and no page +- **AND** a restarted query does not silently continue from the old position + +#### Scenario: Explicit administrative valid-at + +- **WHEN** a pinned cursor was issued at valid-at 90 with interval ending at 100 and resumes when wall-clock time is 110 +- **THEN** its temporal certificate remains valid at 90 +- **AND** separate token expiry, retention, and database-proof checks still apply + +#### Scenario: Unrelated database churn + +- **WHEN** unrelated transactions occur while valid-time and dependency certificates remain valid +- **THEN** those transactions do not by themselves invalidate continuation + +#### Scenario: Skipped future edge activates before the cursor position + +- **WHEN** a live page skipped a future relationship before its last emitted identity +- **THEN** the continuation horizon ends no later than that activation +- **AND** resumption after activation requires a new query rather than omitting the newly active identity + +### Requirement: Caveat request context is authenticated continuation scope + +A Caveat-aware cursor SHALL authenticate the complete canonical request context, +the Caveat compatibility profile/evaluator identity, and the conditional result +semantics needed to reproduce its page. Context supplied on resumption SHALL +match the authenticated scope or cause a typed mismatch or restart. + +Relationship-bound context remains database dependency data: the cursor proof +SHALL cover each relevant context eid/payload and Caveat definition through the +selected snapshot proof. + +#### Scenario: Request context changes between pages + +- **WHEN** a caller resumes a cursor with different Caveat request context +- **THEN** EACL rejects the continuation or starts a separately scoped query +- **AND** does not reuse the prior traversal frontier + +#### Scenario: Bound context changes between pages + +- **WHEN** `:touch` replaces a relationship's Caveat context before resumption +- **THEN** relation and content proof validation prevent old continuation reuse + +#### Scenario: Caveat definition changes + +- **WHEN** schema replacement changes a referenced Caveat before resumption +- **THEN** schema validation rejects or recovers the continuation according to + policy + +#### Scenario: Evaluator identity changes + +- **WHEN** the Caveat evaluator compatibility fingerprint changes +- **THEN** old Caveat-aware cursors are incompatible + +### Requirement: Conditional lookup state is stable across pages + +Caveat-aware lookup cursors SHALL preserve whether each returned membership is +definite or conditional and SHALL authenticate retained missing-context or +residual-condition data. A continuation MUST NOT turn a conditional candidate +into a definite grant or omit a definite candidate because condition state was +lost. + +#### Scenario: Conditional candidate crosses page boundary + +- **WHEN** a conditional lookup candidate is the last item on one page or first + item on the next +- **THEN** its permissionship and missing-context fields remain reproducible + +#### Scenario: Definite and conditional candidates share an object id + +- **WHEN** multiple permission paths reach one object with different condition + states +- **THEN** pagination applies the permission algebra before emitting one + correctly classified result +- **AND** does not emit duplicate object identities + +#### Scenario: Boolean compatibility lookup + +- **WHEN** a compatibility lookup omits conditional results +- **THEN** its cursor scope records that result policy +- **AND** it cannot resume through a Caveat-aware result policy + +### Requirement: v9 cursors commit to fixed eight-slot relationship ordering + +Relationship-read and authorization cursors SHALL identify the v9 endpoint ABI +and the ordering boundary derived from endpoint component four. Trailing Caveat, +context, and validity qualifiers SHALL be authenticated where needed for exact +stored-relationship enumeration, while effective authorization pagination +remains ordered by the opposite endpoint identity. + +#### Scenario: Old cursor reaches v9 + +- **WHEN** a cursor was minted for a four-slot or seven-slot experimental + relationship ABI +- **THEN** the v9 client rejects it as incompatible + +#### Scenario: Qualifiers change at the boundary relationship + +- **WHEN** `:touch` changes qualifiers on a relationship used as a stored-read + continuation boundary +- **THEN** dependency and cursor validation prevent the old physical boundary + from being mistaken for its replacement + +#### Scenario: Full-arity vector bound + +- **WHEN** DataScript resumes a stored relationship scan +- **THEN** its comparator boundary uses the complete eight-element vector shape +- **AND** qualifier values cannot shift the endpoint ordering contract diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/dependency-validated-authorization-cache/spec.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/dependency-validated-authorization-cache/spec.md new file mode 100644 index 00000000..8c47fb63 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/dependency-validated-authorization-cache/spec.md @@ -0,0 +1,203 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../../../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](../../review-2026-09-04.md). The original artifact follows unchanged. + +## ADDED Requirements + +### Requirement: Temporal cache reuse is bounded by certified valid-time stability + +Every completed answer, reusable denotation, projection, and continuation that +depends on relationship validity SHALL include a conservative valid-time +stability interval containing the `valid-at` used for computation. Reuse SHALL +require both the existing complete database dependency proof and membership of +the selected `valid-at` in that certified interval. + +Including exact `valid-at` in a non-reusable key or bypassing publication is an +acceptable conservative fallback. An entry MUST NOT remain reusable merely +because the database basis and relation versions are unchanged when time can +change the answer. + +#### Scenario: Time changes without a transaction + +- **WHEN** a future relationship reaches `valid-from` or an active relationship + reaches `valid-until` without a database transaction +- **THEN** an entry whose stability interval ended at that boundary is not + reused + +#### Scenario: Database dependency changes inside a stable interval + +- **WHEN** selected `valid-at` remains inside the entry's stability interval but + a relation in the complete dependency closure changes +- **THEN** ordinary dependency validation rejects the entry + +#### Scenario: Permanent graph + +- **WHEN** every answer-affecting relationship is permanent and all database + dependency proofs remain valid +- **THEN** temporal stability MAY be unbounded + +#### Scenario: Incomplete temporal proof + +- **WHEN** the engine cannot prove a safe interval for a cacheable value +- **THEN** it computes the selected-snapshot result +- **AND** does not publish or lift it across valid-time instants + +### Requirement: Caveat cache identity is context-complete + +For every cache entry or subproblem influenced by a possible Caveat path, +lookup scope, dependency certificates, and authenticated value data together +SHALL distinguish: + +- the complete canonical request context admitted for the operation; +- each relevant Caveat definition and schema generation; +- each relationship Caveat context eid and authoritative payload; +- the Caveat compatibility profile and evaluator fingerprint; +- permissionship, residual condition, and missing-context result data; and +- the ordinary complete relation dependency proof. + +The lookup key SHALL be computable before evaluation. Permissionship, residual +condition, and missing fields SHALL be authenticated value data, not inputs +that require knowing the answer before a lookup. Definition and bound-context +content SHALL be dependency-certificate data. + +A relation-version proof alone SHALL NOT authorize reuse after context payload, +Caveat definition, request context, or evaluator semantics change. + +#### Scenario: Same graph and different request context + +- **WHEN** two requests select the same database and valid-time but differ in a + context value visible to a possible Caveat path +- **THEN** their semantic cache identities differ + +#### Scenario: Bound context overrides request context + +- **WHEN** relationship-bound context fixes a value also present in request + context +- **THEN** lookup identity includes the admitted request context and dependency + validation covers the authoritative bound values +- **AND** a caller-supplied replacement cannot alias the bound result + +#### Scenario: Caveat definition changes + +- **WHEN** schema replacement changes a referenced Caveat definition +- **THEN** the changed schema generation rejects entries computed under the old + definition + +#### Scenario: Caveat context changes out of band + +- **WHEN** a context payload changes while its eid and relation version remain + unchanged +- **THEN** database-visible proof validation rejects the prior entry + +#### Scenario: Evaluator profile changes + +- **WHEN** the Caveat compatibility profile or evaluator fingerprint changes +- **THEN** entries produced by the old evaluator are cache misses + +### Requirement: Conditional results cannot alias definite results + +The completed-answer and subproblem value formats SHALL distinguish +`:has-permission`, `:no-permission`, and `:conditional-permission`. +Conditional entries SHALL authenticate their residual condition and missing +context fields where retained. Cache lookup MUST NOT substitute one +permissionship state for another. + +#### Scenario: Conditional value is replayed as a grant + +- **WHEN** an external provider returns a validly shaped conditional value under + a key for a definite grant +- **THEN** complete key/value authentication rejects it + +#### Scenario: Missing field set changes + +- **WHEN** two conditional evaluations require different context fields +- **THEN** their retained result identities differ + +#### Scenario: Boolean can? uses a conditional cache entry + +- **WHEN** `can?` obtains a valid cached conditional permissionship +- **THEN** it returns false rather than treating the entry as a grant + +### Requirement: Caveat algebra caching preserves possible paths + +Caveat-aware cache proofs SHALL cover all positive, negative, and conditional +paths that can affect the result. Runtime short-circuiting MAY retain a smaller +witness only when the proof establishes that omitted paths cannot alter the +permissionship or residual condition. + +#### Scenario: Unconditional union witness + +- **WHEN** one union branch grants unconditionally and another is conditional +- **THEN** a cached has-permission result need not retain the irrelevant + conditional branch as a result dependency +- **AND** its ordinary static relationship dependency closure remains complete + +#### Scenario: Conditional union + +- **WHEN** every possible granting branch is false or conditional +- **THEN** a cached conditional result commits to all residual alternatives + needed to reproduce it + +#### Scenario: Conditional exclusion + +- **WHEN** a true positive branch is reduced by a conditional subtracting branch +- **THEN** the cached result remains conditional +- **AND** commits to the subtracting residual condition + +#### Scenario: Missing proof + +- **WHEN** EACL cannot prove that a cached residual condition is complete +- **THEN** it evaluates without reusable Caveat result publication + +### Requirement: Temporal and Caveat ABI changes invalidate portable caches + +The authorization ABI used by exact and managed keys, exported cache snapshots, +subproblem values, and restored entries SHALL change for the v9 relationship +representation and Phase 2 Caveat semantics. Restoring an artifact whose ABI +does not commit to eight-slot qualifiers, valid-time, request context, and +conditional result semantics SHALL fail closed. + +#### Scenario: Restore old cache snapshot + +- **WHEN** a v9 client restores a cache snapshot produced by the old + relationship ABI +- **THEN** it rejects the snapshot before any entry influences authorization + +#### Scenario: Restore Phase 1 artifact in Phase 2 + +- **WHEN** Phase 2 enables Caveats and encounters a Phase 1 cache artifact whose + ABI cannot encode conditional semantics +- **THEN** it rejects or treats the artifact as a miss + +#### Scenario: Shared provider contains old entries + +- **WHEN** a shared provider returns an authenticated entry carrying an old + authorization ABI +- **THEN** the v9 client treats it as a miss + + +### Requirement: Temporal certificates belong to the exact reusable artifact + +A reusable answer, subproblem denotation, and continuation SHALL each carry a +certificate for its own contents. A stable final answer does not certify that +an intermediate denotation or traversal frontier is stable. Certificates SHALL +include both lower and upper bounds so administrative evaluation can move in +either direction. Authoritative faults SHALL NOT be cached as ordinary denials. + +#### Scenario: Stable union and unstable subproblem + +- **GIVEN** a permanent owner path makes a union always true while another relation activates later +- **WHEN** the engine caches the union answer and the other relation's denotation +- **THEN** the answer may be unbounded but the denotation certificate ends at activation +- **AND** another permission cannot reuse the stale denotation beyond that boundary + +#### Scenario: Historical query precedes certificate + +- **WHEN** an administrative valid-at is earlier than the cached interval's lower bound +- **THEN** the entry is a miss even if the database is unchanged + +#### Scenario: Maintenance-only expiration repair + +- **WHEN** only a derived expiration-index value changes +- **THEN** that change alone does not alter the authorization content certificate +- **AND** collection/reconciliation diagnostics remain independently correct diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/public-authorization/spec.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/public-authorization/spec.md new file mode 100644 index 00000000..be07348f --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/public-authorization/spec.md @@ -0,0 +1,188 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../../../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](../../review-2026-09-04.md). The original artifact follows unchanged. + +## ADDED Requirements + +### Requirement: Caveat-aware permission checks accept bounded request context + +EACL SHALL expose a Caveat-aware permission-check operation that accepts an +optional canonical request-context map in addition to the subject, permission, +resource, consistency, and execution controls. Request context SHALL be part of +the selected authorization request and SHALL remain fixed for every subproblem +in that operation. + +The operation SHALL reject malformed, non-canonicalizable, type-invalid, or +over-limit context before evaluator execution or cache publication. + +#### Scenario: Check without request context + +- **WHEN** a caller checks a permission without request context +- **THEN** EACL evaluates uncaveated paths normally +- **AND** evaluates Caveated paths using only relationship-bound values + +#### Scenario: Check with request context + +- **WHEN** a caller supplies valid request context +- **THEN** every Caveat path in that check observes the same canonical request + context + +#### Scenario: Context changes during execution + +- **WHEN** caller-owned mutable input changes after request admission +- **THEN** the in-flight check continues using the captured canonical context +- **AND** authorization does not observe the mutation + +#### Scenario: Invalid request context + +- **WHEN** request context violates type, depth, entry-count, collection, or + encoded-size limits +- **THEN** EACL rejects the request with a typed context error +- **AND** no fallback uncontextualized check runs + +### Requirement: Permission checks expose three-state permissionship + +A successful Caveat-aware check operation SHALL return exactly one permissionship +state. Typed evaluation, execution-limit, clock, and authoritative integrity +failures are errors, not a fourth permissionship state: + +- `:has-permission`; +- `:no-permission`; or +- `:conditional-permission`. + +A conditional response SHALL include the canonical set of missing context field +names needed to determine the result. It MAY include bounded diagnostic +information or a residual condition, but that information MUST NOT be required +to safely interpret permissionship. + +#### Scenario: Definite grant + +- **WHEN** the graph and Caveats determine that access exists +- **THEN** the result is `:has-permission` +- **AND** missing context is empty + +#### Scenario: Definite denial + +- **WHEN** the graph and Caveats determine that access does not exist +- **THEN** the result is `:no-permission` +- **AND** missing context is empty + +#### Scenario: Missing context can change the result + +- **WHEN** one or more missing Caveat parameters can still change the final + authorization result +- **THEN** the result is `:conditional-permission` +- **AND** the response names the missing fields + +#### Scenario: Missing context is irrelevant + +- **WHEN** an unconditional path determines access despite another conditional + path +- **THEN** the result is `:has-permission` +- **AND** irrelevant missing fields are not reported as making the result + conditional + +### Requirement: Boolean can? remains fail-closed + +The existing Boolean `can?` convenience operation SHALL return true only when +the Caveat-aware permissionship is `:has-permission`. It SHALL return false for +`:no-permission` and `:conditional-permission`. + +`can?` SHALL propagate typed execution or integrity failures; it MUST NOT +convert a failed subproblem into false membership inside the permission graph. + +A caller requiring missing-context diagnostics MUST use the Caveat-aware result +operation rather than infer them from `can?`. + +#### Scenario: Conditional result through can? + +- **WHEN** the Caveat-aware result is `:conditional-permission` +- **THEN** `can?` returns false + +#### Scenario: Definite result through can? + +- **WHEN** the Caveat-aware result is `:has-permission` +- **THEN** `can?` returns true +- **WHEN** the result is `:no-permission` +- **THEN** `can?` returns false + +### Requirement: Caveat-aware lookup preserves result state + +Authorization lookups that can traverse Caveated relationships SHALL accept the +same bounded request context and SHALL distinguish definitely authorized from +conditionally authorized subjects or resources. A result reachable only under +a residual Caveat MUST NOT be presented as definitely authorized. + +Pagination and count surfaces SHALL document whether they return definite +results only or structured definite/conditional totals; they MUST NOT silently +collapse conditional results into grants. + +#### Scenario: Definite lookup result + +- **WHEN** a resource is reachable through a definite authorization path +- **THEN** lookup marks it `:has-permission` + +#### Scenario: Conditional lookup result + +- **WHEN** a resource is reachable only if missing Caveat context satisfies a + residual condition +- **THEN** lookup marks it `:conditional-permission` +- **AND** includes the missing field names applicable to that result + +#### Scenario: Boolean-only lookup compatibility + +- **WHEN** an existing compatibility lookup returns only definitely authorized + resources +- **THEN** it omits conditional results +- **AND** documentation directs callers to the Caveat-aware lookup for + conditional candidates + +### Requirement: Snapshot and speculative boundaries capture Caveat request state + +An EACL snapshot SHALL continue to represent database basis, schema, and +valid-time, while Caveat request context SHALL belong to one authorization +request rather than become persistent snapshot state. `eacl/with` and +`eacl/with-schema` SHALL not silently copy caller request context into stored +relationships. + +Relationship-bound context SHALL enter only through an explicit relationship +write/update qualifier and SHALL be persisted separately from request context. + +#### Scenario: Reuse one snapshot with different request context + +- **WHEN** a caller checks the same immutable EACL snapshot with two different + request-context maps +- **THEN** each check has a distinct semantic request identity +- **AND** the snapshot's database basis and valid-time remain unchanged + +#### Scenario: Prospective relationship with bound context + +- **WHEN** a caller uses `eacl/tx-relationship` and `eacl/with` to add a + Caveated relationship with bound context +- **THEN** the speculative snapshot contains the persisted qualifier state +- **AND** later permission checks may add separate request context + +#### Scenario: Request context is not persisted + +- **WHEN** a permission check supplies request context +- **THEN** EACL does not transact or attach it to any relationship + + +### Requirement: Effective result limits do not hide scan exhaustion + +Effective lookup and count SHALL charge inactive candidates and validation work +to request limits. A work-limit failure SHALL NOT be returned as a successful +empty page, end-of-stream, exact count, or ordinary denial. Temporal restarts +SHALL be explicit errors and SHALL emit no resumed page under the old cursor. + +#### Scenario: Active result follows a large inactive run + +- **WHEN** scanning reaches its work limit before finding or excluding further active results +- **THEN** the operation returns a typed limit error +- **AND** no successful exhaustion marker or continuation is published + +#### Scenario: Temporal restart required + +- **WHEN** live resumption crosses the retained state's certified interval +- **THEN** the caller receives a typed restart requirement +- **AND** can start a fresh query that includes newly active earlier identities diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/relationship-caveats/spec.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/relationship-caveats/spec.md new file mode 100644 index 00000000..7aeed46b --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/relationship-caveats/spec.md @@ -0,0 +1,605 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../../../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](../../review-2026-09-04.md). The original artifact follows unchanged. + +## Purpose + +Define Phase 2 SpiceDB-compatible Caveat behavior for EACL relationships, +including schema definitions, sparse relationship-bound context, request +context, conditional permission results, and deterministic evaluation. + +## ADDED Requirements + +### Requirement: Caveat definitions are typed schema declarations + +EACL SHALL parse, validate, persist, read, compare, and replace SpiceDB-style +top-level Caveat definitions. A Caveat definition SHALL have a stable name, +typed parameters, and a bounded CEL expression that produces a boolean or a +partial result when required context is absent. + +EACL SHALL publish a versioned Caveat compatibility profile. Syntax, parameter +types, functions, and operators outside that profile MUST be rejected at schema +write rather than approximated. + +#### Scenario: Valid Caveat definition + +- **WHEN** a schema declares a Caveat whose parameters and expression are + supported by the active compatibility profile +- **THEN** EACL persists one canonical Caveat schema entity +- **AND** schema reads reproduce an equivalent declaration +- **AND** all supported runtimes compile the same canonical typed form + +#### Scenario: Unknown Caveat parameter + +- **WHEN** a Caveat expression references a name absent from its parameter list +- **THEN** schema validation fails with a typed Caveat error +- **AND** no schema replacement commits + +#### Scenario: Non-boolean expression + +- **WHEN** a Caveat expression cannot produce a boolean result +- **THEN** schema validation rejects it + +#### Scenario: Unsupported CEL construct + +- **WHEN** a Caveat uses syntax, a type, a function, or an overload outside the + declared EACL compatibility profile +- **THEN** schema validation rejects it explicitly +- **AND** does not silently change its meaning + +#### Scenario: Caveat definition changes + +- **WHEN** a supported schema write changes a Caveat's parameters or expression +- **THEN** the schema generation changes +- **AND** cached plans and answers from the old definition cannot be reused + +### Requirement: Relation subject branches declare allowed Caveats + +A relation type reference SHALL be able to name one allowed Caveat using +SpiceDB-compatible `with ` syntax. A logical relationship may attach +zero or one Caveat, and the selected Caveat SHALL be valid for the exact +resource relation, subject type, wildcard state, and optional subject relation. + +A schema MAY include both uncaveated and caveated branches for the same subject +form to make the Caveat optional. When only a caveated branch matches, the +relationship write SHALL require that Caveat. + +#### Scenario: Optional Caveat branch + +- **GIVEN** a relation allows both `user` and `user with ip_allowlist` +- **WHEN** a relationship is written for a user with no Caveat +- **THEN** the write is valid +- **WHEN** the same relation is written with `ip_allowlist` +- **THEN** the Caveated write is also valid + +#### Scenario: Required Caveat branch + +- **GIVEN** the only matching branch is `user with ip_allowlist` +- **WHEN** a relationship is written without a Caveat +- **THEN** EACL rejects it with a typed required-Caveat error + +#### Scenario: Caveat is not allowed + +- **WHEN** a relationship names a Caveat not allowed by its matching relation + branch +- **THEN** EACL rejects the write before transaction submission + +#### Scenario: Missing Caveat definition + +- **WHEN** a relation branch references an undefined Caveat +- **THEN** schema validation fails before persistence + +#### Scenario: More than one Caveat on one relationship + +- **WHEN** a relationship update attempts to attach multiple Caveats +- **THEN** EACL rejects it +- **AND** callers must combine the condition inside one Caveat definition + +### Requirement: Caveat qualifiers use tuple slots five and six + +For a Caveated relationship, endpoint slot five SHALL contain the internal eid +of the selected schema-level Caveat definition. Endpoint slot six SHALL be +either `nil` or the eid of one relationship-specific Caveat context entity. +Both endpoint halves SHALL contain identical values. + +A relationship without a Caveat SHALL contain `nil` in both slots. A context ref +without a Caveat ref SHALL be an authoritative integrity fault that aborts an +affected operation; it SHALL NOT be converted to absent membership. + +#### Scenario: Uncaveated relationship + +- **WHEN** a relationship has no Caveat +- **THEN** both endpoint halves contain `nil` in slots five and six +- **AND** no Caveat context entity is created + +#### Scenario: Caveat with empty bound context + +- **WHEN** a relationship attaches a Caveat with no relationship-bound values +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six is `nil` +- **AND** request context may supply the Caveat parameters + +#### Scenario: Caveat with non-empty bound context + +- **WHEN** a relationship attaches a Caveat with one or more bound values +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six contains one context eid shared by both endpoint halves + +#### Scenario: Mismatched Caveat qualifiers + +- **WHEN** the endpoint halves contain different Caveat or context refs +- **THEN** integrity reporting identifies the mismatch +- **AND** an affected authorization operation fails with + `:eacl/invalid-relationship-state` + +### Requirement: Relationship-bound Caveat context is sparse and immutable + +Non-empty relationship-bound Caveat context SHALL live in one internal, +singly-owned entity referenced by endpoint slot six. The entity SHALL contain +one canonical, type-preserving, bounded context-map payload and SHALL have no +public object ID. + +An empty context SHALL use a nil tuple ref and no entity. Context entities SHALL +not be deduplicated across relationships. Once referenced by a committed +relationship, a context entity SHALL be immutable; changing context SHALL +replace the entity and both endpoint refs atomically. + +#### Scenario: Sparse context storage + +- **WHEN** a Caveated relationship binds no parameter values +- **THEN** it allocates no relationship-specific context datom + +#### Scenario: Non-empty context storage + +- **WHEN** a Caveated relationship binds a non-empty context map +- **THEN** EACL stores one canonical payload datom on one context entity +- **AND** arbitrary context bytes are not duplicated in the two endpoint tuples + +#### Scenario: Context changes through touch + +- **WHEN** `:touch` changes relationship-bound context +- **THEN** EACL creates a replacement immutable context entity +- **AND** replaces both endpoint refs and retracts the old singly-owned entity + in the same admitted transaction + +#### Scenario: Context entity is shared out of band + +- **WHEN** integrity inspection finds one context eid referenced by more than + one logical relationship +- **THEN** it reports an ownership violation +- **AND** repair does not guess which relationship owns it + +#### Scenario: Context payload changes in place out of band + +- **WHEN** a context payload is mutated without replacing the endpoint ref +- **THEN** database-visible content proofs detect the change +- **AND** prior cached results are not reused + +### Requirement: Relationship writes validate Caveat context + +Relationship-bound context SHALL be validated against the selected Caveat's +declared parameter names and types before transaction submission. Context input +SHALL use a canonical JSON-like value model compatible with the declared +SpiceDB profile and EACL's portable CLJ/CLJS encoding. + +EACL SHALL enforce configured depth, entry-count, string/byte, collection, and +encoded-size limits without truncation. + +#### Scenario: Partially bound context + +- **WHEN** relationship context supplies a valid subset of Caveat parameters +- **THEN** the write succeeds +- **AND** unsupplied parameters remain available for request-time context + +#### Scenario: Unknown bound key + +- **WHEN** relationship context includes a key not declared by the Caveat +- **THEN** EACL rejects the write with a typed context error + +#### Scenario: Bound value has wrong type + +- **WHEN** a relationship-bound value cannot inhabit the declared parameter type +- **THEN** EACL rejects the write + +#### Scenario: Oversized bound context + +- **WHEN** bound context exceeds any configured structural or encoded-size limit +- **THEN** EACL rejects it before allocating a context entity +- **AND** does not truncate it into a potentially granting value + +### Requirement: Caveat and validity do not change relationship identity + +EACL SHALL permit at most one stored relationship for a +subject/relation/resource identity regardless of Caveat, Caveat context, or +validity. Caveat qualifiers SHALL be mutable properties of that relationship, +matching SpiceDB behavior. + +`:create` SHALL conflict with any existing qualifier state. `:touch` SHALL +replace Caveat, context, and validity atomically. `:delete` SHALL not require +the caller to supply Caveat or context. + +#### Scenario: Duplicate differs only by Caveat + +- **WHEN** a caller creates the same logical relationship with another Caveat +- **THEN** EACL reports `:eacl/relationship-conflict` +- **AND** does not store a second relationship + +#### Scenario: Duplicate differs only by context + +- **WHEN** a caller creates the same logical relationship with different bound + context +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Touch replaces Caveat + +- **WHEN** `:touch` changes an uncaveated relationship to a Caveated one or + changes the selected Caveat +- **THEN** one canonical relationship remains with the requested qualifiers + +#### Scenario: Delete omits Caveat + +- **WHEN** a caller deletes a Caveated relationship by logical identity +- **THEN** EACL deletes it without requiring Caveat name or context +- **AND** retracts its singly-owned context entity + +### Requirement: Request context merges with bound context using SpiceDB precedence + +Caveat-aware authorization and lookup requests SHALL accept a bounded canonical +request-context map. EACL SHALL merge request context with +relationship-bound context so bound values take precedence for duplicate keys. + +The merge SHALL be shallow by parameter name: a bound map value replaces +the whole request parameter, rather than recursively merging nested maps. +Structural/size admission precedes the merge; Caveat-specific type conversion +SHALL apply to the effective merged values before evaluation. A caller MUST NOT override a value fixed on the relationship. + +#### Scenario: Request supplies missing value + +- **GIVEN** a relationship binds `cidr` but not `user_ip` +- **WHEN** the request supplies a valid `user_ip` +- **THEN** the evaluator receives both values + +#### Scenario: Bound value overrides request value + +- **GIVEN** a relationship binds `expected_aud` +- **WHEN** the request supplies a different `expected_aud` +- **THEN** the evaluator receives the relationship-bound value + +#### Scenario: Shadowed request value has wrong Caveat type + +- **GIVEN** a relationship binds a valid value for a Caveat parameter +- **WHEN** a structurally valid request supplies a value of another type for that same parameter +- **THEN** Caveat type validation uses the bound value and does not reject the shadowed value as the effective parameter + +#### Scenario: Bound map replaces request map + +- **GIVEN** a relationship binds parameter `settings` to a map without key `admin` +- **WHEN** request context supplies a `settings` map containing `admin` +- **THEN** the effective `settings` is exactly the bound map +- **AND** the request cannot add keys through recursive merging + +#### Scenario: Request value has wrong type + +- **WHEN** request context supplies an invalid type for a visible Caveat + parameter +- **THEN** EACL returns a typed invalid-context request error +- **AND** does not treat the edge as true + +#### Scenario: Oversized request context + +- **WHEN** request context exceeds a configured limit +- **THEN** EACL rejects the request before cache-key construction or evaluation + +### Requirement: Caveat evaluation returns definite or conditional membership + +For a temporally active, structurally valid Caveated relationship, EACL SHALL +evaluate the selected Caveat using the merged context. Evaluation SHALL produce +one of: + +- true: the relationship may participate in the graph; +- false: the relationship is absent from the effective graph; +- partial: required context is missing, so the relationship contributes a + conditional residual expression and missing parameter names. + +Evaluation errors, malformed definitions, unsupported values, or missing +referenced entities SHALL abort the affected operation with a typed error. +They SHALL NOT become false/absent operands or successful no-permission +results inside permission algebra. Only a valid false evaluation or a valid +inactive interval contributes definite absence. + +#### Scenario: Caveat evaluates true + +- **WHEN** every required parameter is known and the Caveat evaluates true +- **THEN** the relationship participates subject to all other qualifiers + +#### Scenario: Caveat evaluates false + +- **WHEN** every required parameter is known and the Caveat evaluates false +- **THEN** the relationship does not participate + +#### Scenario: Context is missing + +- **WHEN** the result depends on one or more absent parameters +- **THEN** the relationship produces a conditional result +- **AND** identifies the missing parameter names + +#### Scenario: Inactive relationship is Caveated + +- **WHEN** validity makes a Caveated relationship inactive +- **THEN** the engine rejects it before loading bound context or invoking the + Caveat evaluator + +#### Scenario: Evaluator error + +- **WHEN** evaluator execution fails or exceeds a configured bound +- **THEN** EACL returns a typed evaluation failure +- **AND** publishes neither a successful permissionship nor a continuation + +### Requirement: Conditional conditions compose through permission algebra + +The engine SHALL preserve conditional residual conditions until permission +evaluation is determined. Union, intersection, exclusion, arrows, recursion, +and subject-relation traversal SHALL combine definite and conditional branches +without collapsing missing context to false prematurely. + +An unconditional witness MAY determine a positive union despite another +conditional branch. A definite subtracting witness MAY determine an exclusion +denial. When missing context can still change the final answer, the result SHALL +remain conditional and report the complete minimal or conservative missing +field set. + +#### Scenario: Unconditional union witness + +- **GIVEN** a permission is `owner + viewer` +- **WHEN** `owner` is definitely true and `viewer` is conditional +- **THEN** the permission is has-permission + +#### Scenario: Conditional union + +- **WHEN** every possible granting branch is false or conditional and at least + one conditional branch could grant +- **THEN** the permission is conditional-permission + +#### Scenario: Conditional intersection + +- **WHEN** one required intersection branch is conditional and no branch is + definitely false +- **THEN** the permission is conditional-permission + +#### Scenario: Definite false intersection + +- **WHEN** one required intersection branch is definitely false +- **THEN** the permission is no-permission even if another branch is + conditional + +#### Scenario: Conditional exclusion + +- **GIVEN** a permission is `viewer - banned` +- **WHEN** `viewer` is true and `banned` is conditional +- **THEN** the result remains conditional because missing context can determine + whether access is subtracted + +#### Scenario: Arrow contains conditional edge + +- **WHEN** an arrow path depends on a conditional relationship +- **THEN** the resulting subject/resource membership retains that condition + +### Requirement: Public Caveat-aware results match SpiceDB permissionship + +The Caveat-aware permission API SHALL return one of +`:has-permission`, `:no-permission`, or `:conditional-permission`. +A conditional response SHALL include the context field names still required to +determine the answer and MAY include a bounded diagnostic residual condition. + +The existing Boolean `can?` API SHALL return true only for +`:has-permission`. It SHALL return false for both no-permission and +conditional-permission so missing context never grants access. + +Lookup operations SHALL distinguish definitely authorized and conditionally +authorized results. Continuations SHALL preserve that result semantics. + +#### Scenario: Complete true check + +- **WHEN** Caveat evaluation and graph traversal determine access +- **THEN** the Caveat-aware API returns `:has-permission` +- **AND** `can?` returns true + +#### Scenario: Complete false check + +- **WHEN** graph traversal determines access is absent +- **THEN** the Caveat-aware API returns `:no-permission` +- **AND** `can?` returns false + +#### Scenario: Incomplete check + +- **WHEN** missing context prevents a definite decision +- **THEN** the Caveat-aware API returns `:conditional-permission` +- **AND** returns the missing field names +- **AND** `can?` returns false + +#### Scenario: Conditional lookup result + +- **WHEN** a lookup candidate is reachable only under a residual Caveat +- **THEN** the lookup result marks that candidate conditional rather than + silently including it as definitely authorized + +### Requirement: Caveat dependencies are cache- and cursor-complete + +Any cache entry, subproblem, continuation, explanation, or cursor influenced by +Caveats SHALL distinguish the canonical request context, Caveat definition and +schema generation, relationship context eid and payload, evaluator +profile/fingerprint, residual condition, missing fields, and result kind. + +A relation-version proof alone SHALL NOT authorize reuse after a context payload +or evaluator dependency changes. Cache/context identity SHALL be bounded before +hashing or canonicalization. + +#### Scenario: Request context changes + +- **WHEN** two requests differ in a context value visible to any possible + Caveat path +- **THEN** they cannot collide in cache or cursor identity + +#### Scenario: Caveat definition changes + +- **WHEN** schema replacement changes a referenced Caveat +- **THEN** old plans, answers, and continuations cannot be reused + +#### Scenario: Context payload changes out of band + +- **WHEN** a context entity's payload changes without a relation-version update +- **THEN** database-visible proof validation rejects prior results + +#### Scenario: Conditional result is replayed as definite + +- **WHEN** an external cache attempts to substitute a conditional value for a + definite value or vice versa +- **THEN** complete authenticated entry identity rejects the substitution + +### Requirement: Caveat faults and orphaning fail closed + +A missing Caveat definition, missing context entity, malformed payload, invalid +type, mismatched endpoint qualifier, evaluator incompatibility, or unsupported +profile SHALL never unconditionalize a relationship. + +Committed schema replacement SHALL reject removal of a Caveat definition still +referenced by stored relationships with no inert-orphan exception in v9. Referenced parameter removals and type +changes SHALL be rejected when they would invalidate stored bound context or +relation branches. Commit-time schema guards SHALL serialize these checks +with relationship writes. Integrity reports SHALL identify +orphaned and malformed Caveat data. + +#### Scenario: Caveat definition is missing + +- **WHEN** a relationship references a Caveat entity absent from the selected + schema +- **THEN** the affected operation fails with a typed authoritative-state error +- **AND** integrity reports the orphan + +#### Scenario: Context entity is missing + +- **WHEN** slot six references an absent context entity +- **THEN** the affected operation fails with a typed authoritative-state error +- **AND** the missing entity is neither empty context nor false membership + +#### Scenario: Referenced Caveat removal + +- **WHEN** schema replacement would remove a Caveat used by stored + relationships +- **THEN** the committed write fails with a typed in-use error + +#### Scenario: Unknown writer creates invalid Caveat data + +- **WHEN** an out-of-band writer creates malformed Caveat qualifiers +- **THEN** authorization fails closed +- **AND** proof and integrity paths expose the change + +### Requirement: Relationship entities remain a future assertion boundary + +Caveat definitions and sparse context entities SHALL NOT require EACL to reify +every relationship. First-class relationship or grant-assertion entities MAY be +introduced only when the domain requires independent assertion identity, such +as multiple grantors, independently revocable grants, multiple simultaneous +intervals, provenance, approvals, or stable grant IDs. + +#### Scenario: Ordinary Caveated relationship + +- **WHEN** one Caveat with optional context qualifies one logical relationship +- **THEN** the endpoint pair remains the authoritative relationship +- **AND** the context entity is qualifier data, not a relationship vertex + +#### Scenario: Multiple independent grants are requested + +- **WHEN** a future feature requires two independently revocable grants for one + subject/relation/resource identity +- **THEN** that feature receives a separate storage-model proposal +- **AND** is not simulated by duplicate v9 qualifier variants + +### Requirement: Cross-runtime Caveat conformance + +The JVM and ClojureScript implementations SHALL consume one canonical typed +Caveat representation and produce equivalent definite, false, partial, +missing-context, and error results. Phase 2 SHALL include differential fixtures +against the declared SpiceDB compatibility profile. + +#### Scenario: Shared conformance corpus + +- **WHEN** the same Caveat, relationship context, and request context are + evaluated on every supported runtime +- **THEN** the permissionship and missing-context results are identical + +#### Scenario: SpiceDB reference fixture + +- **WHEN** a fixture uses syntax and values inside the declared compatibility + profile +- **THEN** EACL and the pinned SpiceDB reference produce equivalent observable + results + +#### Scenario: Runtime divergence + +- **WHEN** differential testing finds a semantic mismatch +- **THEN** Phase 2 release is blocked until EACL rejects the construct or the + runtimes agree + + +### Requirement: Caveat value encoding is explicitly typed and portable + +Before Phase 2 activation, EACL SHALL freeze a versioned supported type/coercion +profile and an injective typed encoding for every admitted CEL value. The +secure-format v1 envelope SHALL NOT be mistaken for that value model: native +doubles and integers beyond the JS safe range are not directly supported. +Unsupported values SHALL be rejected without rounding, overflow, or conflating +the types exposed to `any`. The reference SpiceDB release or commit SHALL be +recorded with the differential corpus. + +#### Scenario: Large integer and double encoding + +- **WHEN** the profile admits a signed/unsigned 64-bit integer or a double +- **THEN** CLJ and CLJS preserve the exact declared value and its type in canonical bytes +- **AND** numeric strings follow the declared conversion rule rather than silently rounding through a JS number + +#### Scenario: Encoding is not yet qualified + +- **WHEN** a profile type lacks a shared encoding or conformance result +- **THEN** Phase 2 admission for that type remains disabled + +### Requirement: Authoritative faults never erase subtracting evidence + +An encountered authoritative integrity or evaluation failure SHALL abort the +entire affected check, lookup, count, explanation, or batch operation. EACL +MUST NOT turn it into false or absent membership. `can?` SHALL propagate the +typed error. No successful reusable result SHALL be published from the failed +operation. This is separate from conditional permission caused by missing +request context. + +#### Scenario: Corrupt subtracting edge + +- **GIVEN** permission is `viewer - banned`, `viewer` is true, and `banned` has missing context storage, mismatched halves, or duplicate variants +- **WHEN** the operation encounters the fault +- **THEN** it fails with a typed error +- **AND** it cannot grant by dropping `banned` + +#### Scenario: Subtracting evaluator fails + +- **GIVEN** permission is `viewer - banned` and the `banned` Caveat exceeds its evaluation limit +- **WHEN** the evaluator reports failure +- **THEN** the whole operation fails rather than treating `banned` as false + +### Requirement: Recursive residual evaluation terminates within declared limits + +Recursive Caveat evaluation SHALL use canonical bounded residual identity and +fixed-point processing. Repeated visits to equivalent conditional states MUST +NOT grow residual formulas indefinitely. Unsupported recursive constructs SHALL +be rejected at schema admission. Work-limit exhaustion SHALL be a typed failure. + +#### Scenario: Conditional cycle + +- **WHEN** a supported recursive relation revisits the same residual state +- **THEN** evaluation converges without unbounded expression unfolding +- **AND** the shared conformance corpus verifies permissionship and missing fields + +#### Scenario: Same Caveat with different bound values + +- **WHEN** two paths attach one Caveat definition with different bound contexts +- **THEN** their residual identities preserve those bindings +- **AND** simplification cannot equate the paths merely by Caveat name diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/temporal-relationship-validity/spec.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/temporal-relationship-validity/spec.md new file mode 100644 index 00000000..f11730ee --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/specs/temporal-relationship-validity/spec.md @@ -0,0 +1,346 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../../../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](../../review-2026-09-04.md). The original artifact follows unchanged. + +## Purpose + +Define native valid-time semantics for permanent, future, expiring, and bounded +EACL relationships without requiring scheduler writes at activation or expiry. + +## ADDED Requirements + +### Requirement: Half-open relationship validity + +Every relationship SHALL have an optional valid-time interval represented by +inclusive `valid-from` and exclusive `valid-until` bounds. EACL SHALL normalize +both bounds to exact UTC epoch-millisecond integers in the inclusive portable range +`[-9007199254740991, 9007199254740991]` and SHALL interpret omitted +bounds as unbounded. A relationship is temporally active at `t` exactly when +`valid-from <= t < valid-until`, after ignoring an omitted inequality. + +EACL SHALL reject zero-width and reversed bounded intervals before transaction +submission. + +#### Scenario: Permanent relationship + +- **WHEN** both validity bounds are absent +- **THEN** the relationship is temporally active at every representable + `valid-at` instant + +#### Scenario: Future relationship + +- **WHEN** `valid-from` is later than the selected `valid-at` +- **THEN** the relationship is stored but has no authorization effect + +#### Scenario: Expiring relationship + +- **WHEN** `valid-at` is exactly equal to `valid-until` +- **THEN** the relationship is inactive + +#### Scenario: Adjacent intervals + +- **WHEN** one relationship interval ends at the exact instant another interval + begins +- **THEN** the half-open definition produces neither an overlap nor an + ambiguous boundary + +#### Scenario: Invalid interval + +- **WHEN** a relationship update supplies `valid-from >= valid-until` +- **THEN** EACL rejects it with a typed invalid-validity error +- **AND** writes no endpoint or auxiliary data + +### Requirement: Trusted valid-time snapshots + +Every top-level authorization, lookup, count, relationship-read, explanation, +and batch operation SHALL evaluate against one EACL-created snapshot containing +both one immutable database basis and one captured `valid-at` instant. When a +client creates the snapshot without an explicit administrative time, EACL SHALL +obtain `valid-at` from the client's configured trusted clock exactly once. + +A client MAY reuse its immutable database pin for minimize-latency consistency, +but each top-level operation targeting the client SHALL pair that pin with a +freshly captured `valid-at`. Passing an explicit EACL snapshot SHALL freeze both +the database basis and its captured `valid-at`. + +Speculative snapshots produced by `eacl/with` or `eacl/with-schema` SHALL retain +their parent's captured `valid-at` unless the caller explicitly creates a new +administrative temporal view through an EACL API. + +#### Scenario: Clock crosses a boundary during evaluation + +- **WHEN** wall-clock time passes a relationship boundary while one + authorization operation is running +- **THEN** every subproblem in that operation uses the same captured + `valid-at` +- **AND** the operation returns a deterministic snapshot result + +#### Scenario: Batch evaluation + +- **WHEN** one batch contains multiple permission checks +- **THEN** all checks use the same database basis and `valid-at` + +#### Scenario: Minimize-latency pin is reused + +- **WHEN** two client-targeted operations reuse the same immutable database pin + at different wall-clock instants +- **THEN** each operation captures its own `valid-at` +- **AND** the second operation does not inherit the first operation's time + +#### Scenario: Explicit snapshot is retained + +- **WHEN** a caller evaluates the same explicit EACL snapshot more than once +- **THEN** every evaluation uses the snapshot's original database basis and + `valid-at` + +#### Scenario: Prospective relationship transaction + +- **WHEN** a caller applies a scheduled relationship with `eacl/with` +- **THEN** the speculative snapshot contains the stored assertion +- **AND** authorization evaluates it at the snapshot's unchanged `valid-at` + +#### Scenario: Administrative valid-time view + +- **WHEN** an authorized caller asks EACL to evaluate an EACL snapshot at an + explicit representable instant +- **THEN** EACL returns a new immutable temporal view +- **AND** does not accept an arbitrary native database value as the basis + +### Requirement: Scheduled activation and expiration require no boundary write + +Authorization SHALL derive relationship activity from stored validity bounds +and the selected `valid-at`. Correct activation and expiration MUST NOT depend +on a scheduler, timer, transaction at the boundary, listener callback, or +garbage-collection pass. + +#### Scenario: Scheduled grant activates + +- **GIVEN** a relationship was written on 3 September with `valid-from` on + 6 September +- **WHEN** a snapshot is selected before the start +- **THEN** the relationship is inactive +- **WHEN** a snapshot is selected at or after the start and before the end +- **THEN** the relationship is active without another database transaction + +#### Scenario: Grant expires + +- **WHEN** a snapshot is selected at or after a finite `valid-until` +- **THEN** the relationship is inactive even when its physical tuple remains in + the database + +#### Scenario: Delayed collector + +- **WHEN** the expiration collector is stopped or delayed +- **THEN** expired relationships remain non-granting + +### Requirement: Temporal qualifiers participate in all permission algebra + +Validity filtering SHALL occur at the relationship-leaf boundary before an edge +contributes to union, intersection, exclusion, arrow traversal, recursion, +lookup, count, or expansion. Inactive positive and negative relationships SHALL +both be absent from the selected valid-time graph. + +#### Scenario: Positive grant expires + +- **WHEN** the only granting relationship expires +- **THEN** a permission that was true becomes false at the expiration boundary + +#### Scenario: Exclusion expires + +- **GIVEN** a permission is `viewer - banned` +- **WHEN** the only active `banned` relationship expires while `viewer` + remains active +- **THEN** the permission changes from false to true without a database write + +#### Scenario: Future arrow edge + +- **WHEN** an intermediate relationship used by an arrow has not reached its + `valid-from` +- **THEN** traversal does not follow that edge + +#### Scenario: Temporal lookup and count + +- **WHEN** a lookup or count runs at a selected `valid-at` +- **THEN** its result contains exactly the resources or subjects authorized in + that temporal graph + +### Requirement: Relationship APIs preserve stored and effective state + +Relationship writes SHALL accept optional validity bounds independently of the +logical relationship identity. `read-relationships` SHALL be able to return the +stored normalized bounds and SHALL provide an option to select stored +assertions, effective-at assertions, or both with an explicit status. + +A future or expired assertion SHALL continue to exist for `:create` conflict, +`:touch`, `:delete`, audit, and integrity purposes even when it is absent from +the effective authorization graph. + +#### Scenario: Read scheduled assertion + +- **WHEN** a stored-view relationship read matches a future assertion +- **THEN** it returns the assertion and normalized bounds with status + `:scheduled` + +#### Scenario: Read active assertion + +- **WHEN** an effective-at read matches a relationship active at the selected + `valid-at` +- **THEN** it returns the assertion with status `:active` + +#### Scenario: Read expired assertion + +- **WHEN** a stored-view read matches an expired assertion +- **THEN** it can return status `:expired` +- **AND** an effective-at-only read omits it + +#### Scenario: Create after expiry but before collection + +- **WHEN** `:create` targets the same logical relationship as an expired + assertion still stored +- **THEN** it reports a relationship conflict +- **AND** the caller can use `:touch` to reschedule it + +### Requirement: Temporal stability is explicit authorization metadata + +Every internal authorization result that may enter a reusable cache or +continuation SHALL carry a conservative temporal stability interval containing +the selected `valid-at`. Within that interval, and only while all ordinary +dependency and structural-integrity proofs remain valid, the result SHALL be invariant under the passage +of time. + +The interval for a composed permission SHALL be no wider than the intersection +of the intervals required by the witnesses and counter-witnesses used to prove +that result. An implementation MAY conservatively choose a narrower interval +or disable temporal reuse, but MUST NOT certify reuse across a possible +validity boundary. + +#### Scenario: Cached denial before activation + +- **WHEN** a denial is computed before a future granting relationship starts +- **THEN** its temporal stability ends no later than that start boundary +- **AND** it is not reused at or after the boundary + +#### Scenario: Cached grant before expiration + +- **WHEN** a grant depends on a relationship with finite `valid-until` +- **THEN** its temporal stability ends no later than that expiration +- **AND** it is not reused at or after the boundary + +#### Scenario: Permanent dependency graph + +- **WHEN** every relationship capable of affecting a result is permanent +- **THEN** its temporal stability MAY be unbounded + +#### Scenario: Conservative fallback + +- **WHEN** the engine cannot establish a complete temporal stability proof +- **THEN** it evaluates correctly but does not publish the result for + cross-time reuse + +### Requirement: Clock configuration fails safely + +The client clock SHALL be deterministic for one invocation, return an exact +epoch-millisecond value inside EACL's cross-runtime safe range, and be sampled +only through the EACL temporal snapshot boundary. Invalid clock values SHALL +fail the request. EACL documentation SHALL require synchronized production +clocks when multiple peers authorize against one shared database. + +#### Scenario: Invalid clock result + +- **WHEN** the configured clock returns a non-integer, out-of-range, or + decreasing value relative to the clock contract for that client +- **THEN** snapshot acquisition fails with a typed clock error +- **AND** authorization does not fall back to an unqualified graph + +#### Scenario: Explicit test clock + +- **WHEN** a test client uses a deterministic injected clock +- **THEN** boundary behavior can be tested without sleeping or scheduling + transactions + +#### Scenario: Multiple peers + +- **WHEN** multiple EACL peers use one shared database +- **THEN** operational documentation states the permitted clock-skew model and + the consequences near validity boundaries; any configured uncertainty policy + evaluates the whole permission over the interval or returns a typed failure + +### Requirement: Expiration collection is sparse and non-authoritative + +A relationship with finite `valid-until` SHALL have one EACL-owned sparse +expiration-index value using the physical layout fixed by the converged +relationship-storage specification that can locate the exact qualified relationship without +scanning every endpoint tuple. Permanent and start-only relationships SHALL +have no expiration-index value. + +Collection SHALL delete only assertions whose exclusive end is older than the +configured retention cutoff. It SHALL retract the exact endpoint pair, the +exact expiration-index value, and any singly-owned Caveat context entity in one +admitted transaction. Authorization MUST NOT consult this index when deciding +whether a relationship is active. + +#### Scenario: Finite expiration is indexed + +- **WHEN** a relationship is created or touched with finite `valid-until` +- **THEN** exactly one matching sparse expiration-index value exists + +#### Scenario: Permanent relationship has no temporal index overhead + +- **WHEN** a relationship has no finite `valid-until` +- **THEN** no expiration-index datom is stored for it + +#### Scenario: Collection races with rescheduling + +- **WHEN** a collector reads an expired entry and a concurrent `:touch` + replaces that relationship with different qualifiers +- **THEN** exact-value guards prevent the stale collection plan from deleting + the replacement +- **AND** the collector retries or reports a benign race + +#### Scenario: Expiration entry reconstructs exact pair + +- **WHEN** collection reads an expiration entry +- **THEN** its owner and eight components reconstruct the exact forward and reverse values +- **AND** current-state guards reject stale collection plans + +#### Scenario: Missing index entry + +- **WHEN** an expired relationship lacks its derived expiration-index value +- **THEN** authorization still denies it according to the authoritative tuple +- **AND** integrity reporting identifies the index mismatch + +### Requirement: Historical and valid time remain distinct + +EACL SHALL distinguish transaction-time selection from relationship valid-time +selection. On backends supporting historical database values, a temporal +authorization question SHALL be modeled as a database basis plus a separate +`valid-at` instant. Documentation SHALL state the retention limits for +reconstructing past authorization after physical collection. + +#### Scenario: Bitemporal question + +- **WHEN** an audit selects the database as known at transaction time `T` and + evaluates relationships at valid time `V` +- **THEN** EACL keeps `T` and `V` as distinct authenticated inputs + +#### Scenario: Physical collection and history + +- **WHEN** an expired assertion has been physically collected +- **THEN** current authorization remains unchanged +- **AND** past reconstruction is available only to the extent guaranteed by the + selected backend's retained history + + +### Requirement: Clock uncertainty cannot drop negative evidence + +A configured skew or uncertainty policy MUST NOT make an individual relationship +inactive early as a generic fail-closed rule. EACL SHALL grant only if the whole +permission is invariantly granting throughout the selected uncertainty interval; +otherwise it SHALL return a typed clock-uncertainty failure. + +#### Scenario: Exclusion ends inside uncertainty window + +- **GIVEN** permission is `viewer - banned` and `banned` expires inside the clock uncertainty interval +- **WHEN** the result differs across that interval +- **THEN** EACL does not grant by expiring `banned` at the early edge +- **AND** returns the documented typed uncertainty failure diff --git a/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/tasks.md b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/tasks.md new file mode 100644 index 00000000..aefe0321 --- /dev/null +++ b/openspec/history/superseded/2026-09-03-add-v9-qualified-relationship-tuples/tasks.md @@ -0,0 +1,140 @@ +> **DEPRECATED / SUPERSEDED — 2026-09-04.** This unimplemented proposal is retained for historical review only. Its requirements and unchecked tasks are withdrawn from the implementation plan; do not apply or sync these deltas into the main specifications. Use the [replacement v9 proposal](../2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md). +> +> The replacement uses **seven-slot Caveat + expiry-only endpoint tuples**, removes scheduled activation and the mandatory expiration index, and retains time-aware cache/cursor checks. See the [review findings and preserved REPL evidence](review-2026-09-04.md). The original artifact follows unchanged. + +## 1. Freeze the v9 contracts + +- [ ] 1.1 Add relationship storage version 9 and bump engine, adapter, cache, subproblem, and cursor ABI identifiers; verify old artifacts and old storage stamps are rejected in contract tests. +- [ ] 1.2 Add shared constants for the two v9 attribute names, value arity eight, endpoint-local identity arity four plus the owning endpoint eid, and qualifier positions; verify every backend imports the shared definitions rather than duplicating slot numbers. +- [ ] 1.3 Document the canonical order `[endpoint identity ×4, caveat, caveat-context, valid-from, valid-until]`; verify schema docs, API docs, tests, and secure-format fixtures use the same order. +- [ ] 1.4 Add startup qualification that rejects any v7 relationship datom or mixed v7/v9 data; verify a populated v7 database fails before serving while a fresh/rebuilt v9 database opens. +- [ ] 1.5 Remove any proposed dual-read, automatic migration, or per-relation fallback path from v9 code and docs; verify instrumentation observes no v7 relationship seek during v9 authorization. + +- [ ] 1.6 Persist a separate semantic capability epoch and fence Phase 2 activation at current-basis selection and mutation commit, draining old pinned clients or using a coordinated cutover; verify long-lived Phase 1 clients, cache/token restore, and speculative operations cannot bypass the epoch. + +## 2. Phase 1 — install fixed eight-slot storage + +- [ ] 2.1 Replace Datomic forward/reverse relationship schema attributes with fixed eight-component heterogeneous tuples and verify installed `:db/tupleTypes` match the OpenSpec exactly. +- [ ] 2.2 Replace Datahike forward/reverse relationship schema attributes with the same eight-component types and verify physical-schema qualification rejects drift. +- [ ] 2.3 Replace Datalevin relationship schema and write-policy stamp rules for the v9 attributes and verify admitted writes advance only the affected relation generation. +- [ ] 2.4 Replace DataScript relationship attributes with fixed eight-element vectors and verify JVM/Node schema tests use complete arity. +- [ ] 2.5 Update clean database bootstrap and storage-version stamping for v9 and verify no v7 attribute is read as an authoritative relationship source. +- [ ] 2.6 Add Phase 1 admission rules requiring slots five and six to be nil; verify encountered out-of-band non-nil Caveat qualifiers fail the affected operation with a typed error until Phase 2 is enabled. + +## 3. Phase 1 — replace the endpoint codec and scans + +- [ ] 3.1 Replace four-slot constructors, decoders, validators, peer-half construction, and retractions with eight-slot equivalents; verify round-trip, symmetry, nil normalization, and malformed-arity tests. +- [ ] 3.2 Add owner-qualified logical-identity extraction and qualifier extraction; verify Caveat/context/validity changes do not alter identity. +- [ ] 3.3 Update forward and reverse endpoint scans to page by component four while carrying components five through eight; verify ascending and descending order across adjacent relation prefixes. +- [ ] 3.4 Implement full-eight-slot direction-specific low/high bounds for exact identity probes and vector seeks; verify nil-low, descending-high, negative/extreme timestamps, whole-identity exclusion, and zero/one/multiple qualifier variants. +- [ ] 3.5 Update direct membership to obtain qualified candidates from one endpoint seek and verify permanent positive, permanent negative, future, active, and expired checks use one authoritative candidate stream, with separately measured integrity/proof reads and duplicate-group lookahead. +- [ ] 3.6 Update relationship filters, reads, counts, relation-in-use checks, and schema-orphan probes for v9 values; verify future and expired assertions remain visible to stored-data safety checks. +- [ ] 3.7 Update secure digests, canonical records, content proofs, and benchmark fixtures to include all eight components; verify each qualifier component changes the digest. + +## 4. Phase 1 — make mutation and cleanup identity-aware + +- [ ] 4.1 Change `:create` conflict detection to serialize on the owning endpoint plus the first four components at commit time; verify concurrent creates with different validity bounds yield exactly one winner on each writer topology. +- [ ] 4.2 Implement `:touch` as canonical old-pair replacement and verify an unchanged touch is idempotent, stale concurrent touch/delete plans replan or fail, and a changed interval leaves one pair. +- [ ] 4.3 Implement `:delete` by logical identity without qualifier input and verify it removes future, active, expired, partial, and duplicate corrupt variants without touching adjacent identities. +- [ ] 4.4 Update batch normalization so identical updates deduplicate and semantically different updates for one logical identity fail before submission; verify typed error data. +- [ ] 4.5 Update object deletion to remove both v9 halves and owned auxiliary values; verify self-relationships, missing endpoints, and one-sided corruption. +- [ ] 4.6 Update integrity reports for arity, peer symmetry, owner-qualified identity uniqueness, qualifier agreement, and invalid slot-five/slot-six combinations; verify encountered authoritative faults abort the operation, including when used by an exclusion. +- [ ] 4.7 Ensure every effective relationship/qualifier mutation advances each affected relation generation once per admitted transaction; verify unrelated relation generations remain unchanged. + +## 5. Phase 1 — implement native validity + +- [ ] 5.1 Add public relationship validity input and normalize supported instant forms to exact UTC epoch-millisecond integers; verify omitted, start-only, end-only, bounded, zero-width, reversed, and out-of-range cases. +- [ ] 5.2 Preserve the public three-field logical `Relationship` value while carrying qualifiers in update options; verify permanent writes emit four trailing nils. +- [ ] 5.3 Add one trusted clock sample to each top-level authorization view and verify one operation cannot observe two wall-clock instants. +- [ ] 5.4 Capture a fresh valid-time for each client-targeted operation even when reusing a minimize-latency database pin; verify explicit snapshots freeze both basis and time. +- [ ] 5.5 Preserve valid-time through `eacl/with`, `eacl/with-schema`, and chained speculative snapshots; verify prospective scheduled grants are deterministic. +- [ ] 5.6 Add an explicit EACL administrative temporal-view API without accepting caller-owned native database values; verify transaction time and valid time remain distinct. +- [ ] 5.7 Apply `[valid-from, valid-until)` filtering before every direct, union, intersection, exclusion, arrow, recursion, lookup, count, and expansion contribution; verify exact boundary matrices. +- [ ] 5.8 Add a regression where expiry of a subtracting relationship changes `viewer - banned` from no-permission to has-permission without a database write. +- [ ] 5.9 Extend `read-relationships` with stored/effective status for permanent, scheduled, active, and expired relationships; verify create conflicts still see inactive stored assertions. + +## 6. Phase 1 — temporal cache, cursor, and collection + +- [ ] 6.1 Extend leaf results with conservative temporal stability intervals; verify future-start, active-expiry, already-expired, and permanent certificates. +- [ ] 6.2 Compose temporal stability through every permission operator, including negative proofs and exclusions; verify no result is reused across a possible boundary. +- [ ] 6.3 Gate exact and managed cache reuse on ordinary dependency proofs plus valid-time interval membership; verify unchanged relation versions cannot carry a result across time. +- [ ] 6.4 Implement a safe exact-valid-at key or no-publication fallback when temporal proof is incomplete; verify cached and uncached semantics agree. +- [ ] 6.5 Authenticate temporal mode, selected valid-time, and both stability bounds in continuations; verify pinned resumption ignores wall-clock validity crossings, live crossings require explicit restart, and skipped candidates/frontier/lookahead are certified. +- [ ] 6.6 Add the specified eight-slot subject-owned expiration-index value for each finite `valid-until` and none for permanent/start-only relationships; verify create, touch, delete, cleanup, and speculative parity. +- [ ] 6.7 Implement bounded exact-value expiration collection after a retention cutoff; verify authorization never consults the index and a stale collector cannot delete a rescheduled relationship. +- [ ] 6.8 Add expiration-index reconciliation and metrics; verify missing/stale derived entries affect diagnostics or collection only, never authorization. + +## 7. Phase 1 — rebuild-only release qualification + +- [ ] 7.1 Write export/re-transact/reseed guidance for populated v7 installations and verify it contains no automatic conversion or mixed-runtime promise. +- [ ] 7.2 Add startup tests for fresh, rebuilt, schema-only-legacy, populated-v7, mixed, and wrong-stamp databases; verify failures occur before request service. +- [ ] 7.3 Benchmark v7 versus Phase 1 v9 direct checks, negative checks, adjacency, arrows, lookup, count, and pagination; report candidate-stream seeks, pair/ownership/content validation, advanced datoms, and total request work separately. +- [ ] 7.4 Benchmark permanent-heavy, 1%-temporal, and temporal-dense million-relationship datasets on Datomic Pro; include adversarial expired/future prefixes and suffixes, both scan directions, endpoint-degree distributions, exact-page exhaustion, and default-limit failures; report latency, segment density, peer-cache pressure, and scan throughput. +- [ ] 7.5 Benchmark durable Datahike/Datalevin size and write amplification plus DataScript JVM/Node allocation and ordering; verify accepted release thresholds. +- [ ] 7.6 Run the CI-equivalent JVM suite through nREPL, the DataScript ClojureScript build last, heavy storage benchmarks explicitly, and `bin/formal source-closure`; verify Phase 1 gates are clean. + +- [ ] 7.7 Record numerical latency, storage, allocation, and scan-work acceptance budgets before paired qualification; distinguish raw index probes from complete public authorization and warm-peer evidence from cold/storage evidence. + +## 8. Phase 2 — add Caveat schema and canonical IR + +- [ ] 8.1 Extend the SpiceDB parser with top-level Caveat definitions and verify source positions, comments, duplicate names, parameters, and expression boundaries. +- [ ] 8.2 Define a versioned EACL SpiceDB Caveat compatibility profile and canonical typed IR; pin the reference SpiceDB release/commit and define every admitted type/coercion/function; verify a typed encoding for doubles and exact int/uint through the existing secure-format envelope, including stable CLJ/CLJS bytes. +- [ ] 8.3 Implement static name, type, overload, return-type, recursion/complexity, and size validation; verify unsupported CEL constructs fail schema writes explicitly. +- [ ] 8.4 Persist Caveat definitions as schema entities and include them in read, compare, replacement, generation, and content-proof logic; verify changing a definition invalidates derived plans. +- [ ] 8.5 Extend relation type references to allow `with caveat` and verify optional versus required Caveat branches for direct, wildcard, and subject-relation subjects. +- [ ] 8.6 Add schema replacement guards for Caveat definitions referenced by relation branches or stored relationships; verify removal and incompatible parameter/type changes cannot invalidate stored context or relation branches, including races with relationship writes. +- [ ] 8.7 Build a shared Caveat schema conformance corpus from the declared SpiceDB profile and verify every supported runtime accepts/rejects the same definitions. + +## 9. Phase 2 — enable tuple Caveat and context qualifiers + +- [ ] 9.1 Enable slot five as the schema-level Caveat definition eid and verify a relationship may contain zero or one Caveat allowed by its exact relation branch. +- [ ] 9.2 Add `:eacl.caveat-context/payload` for sparse internal context entities and verify an empty bound context uses nil slot six and allocates no entity. +- [ ] 9.3 Canonically encode and type-check non-empty relationship-bound context into one payload datom; verify round-trip for every supported Caveat parameter type and configured limit. +- [ ] 9.4 Enforce immutable, singly-owned, non-deduplicated context lifecycle; verify touch replaces context and delete retracts it atomically. +- [ ] 9.5 Reject context-without-Caveat, disallowed Caveats, unknown context keys, wrong types, oversize values, and multiple Caveats; verify no partial endpoint/context data commits. +- [ ] 9.6 Extend relationship reads to return Caveat name and bound context while preserving logical identity; verify empty and non-empty context representations. +- [ ] 9.7 Update `:create`, `:touch`, and `:delete` tests to match SpiceDB identity semantics; verify Caveat/context differences never create parallel relationships. +- [ ] 9.8 Extend cleanup, integrity, relation/Caveat in-use checks, and unknown-writer proofs over slots five/six and context payloads; verify missing/shared context raises typed authoritative-state errors and in-place payload changes invalidate prior content proofs. + +## 10. Phase 2 — implement Caveat evaluation and public results + +- [ ] 10.1 Add bounded canonical request-context admission to Caveat-aware permission and lookup requests; verify caller mutation after admission cannot alter an in-flight request. +- [ ] 10.2 Implement relationship-bound-over-request context precedence and parameter type checking; verify shallow parameter replacement, Caveat type checks after precedence, and rejection of nested-map request injection. +- [ ] 10.3 Implement deterministic bounded CEL compilation/evaluation for the declared compatibility profile; verify true, false, partial, error, and resource-limit outcomes. +- [ ] 10.4 Return residual conditions and canonical missing field names for partial evaluation; verify missing context is not prematurely collapsed to false inside the engine. +- [ ] 10.5 Compose definite and conditional memberships through union, intersection, exclusion, arrows, subject relations, and recursion; verify SpiceDB-derived truth tables, bound-context-sensitive residual identity, conditional cycles, terminating fixed points, and typed work-limit failures. +- [ ] 10.6 Add a public three-state check returning `:has-permission`, `:no-permission`, or `:conditional-permission`; verify conditional responses include missing fields. +- [ ] 10.7 Keep `can?` fail-closed by returning true only for has-permission; verify conditional results return false while typed evaluation/integrity failures propagate and cannot become false subtracting operands. +- [ ] 10.8 Add Caveat-aware lookup results and explicit definite-only compatibility behavior; verify conditional candidates are classified, not silently granted. +- [ ] 10.9 Ensure temporal inactivity is checked before context entity reads and evaluator invocation; verify expired/future Caveated edges perform no Caveat evaluation. + +## 11. Phase 2 — complete cache, cursor, and adversarial safety + +- [ ] 11.1 Separate pre-evaluation lookup scope, dependency certificates, and authenticated value data for request context, definitions, bound payloads, evaluator identity, permissionship, residuals, and missing fields; verify each answer-affecting change causes a miss. +- [ ] 11.2 Authenticate conditional result values and prevent conditional/definite substitution by shared cache providers; verify adversarial replay tests. +- [ ] 11.3 Extend static dependency closure and runtime witnesses for all possible Caveat paths; verify an unvisited conditional branch cannot make cached authorization unsound. +- [ ] 11.4 Authenticate request context, evaluator identity, and conditional lookup policy in cursors; verify context changes between pages require mismatch/restart. +- [ ] 11.5 Cover relationship-bound context payloads in unknown-writer proofs and verify in-place out-of-band mutation invalidates prior answers. +- [ ] 11.6 Add fuzz/property tests for nested context, numeric boundaries, timestamps, durations, IP addresses, lists/maps, residual algebra, and cache canonicalization. +- [ ] 11.7 Add regression fixtures inspired by published SpiceDB Caveat cache/conditional-result advisories and verify EACL cannot upgrade a conditional or malformed path to a grant. +- [ ] 11.8 Differentially execute the supported corpus against a pinned SpiceDB reference and every EACL runtime; block release on semantic divergence. + +## 12. Documentation, performance, formalization, and OpenSpec gates + +- [ ] 12.1 Update schema, relationship write/read, Caveat context, valid-time, permission result, lookup, cache, cursor, cleanup, and operations documentation; verify examples cover a 6–9 September grant with an IP Caveat. +- [ ] 12.2 Document that bound context overrides request context and that Caveat/context/validity do not create duplicate relationships; verify examples use touch to replace qualifiers. +- [ ] 12.3 Document the eight-slot ceiling and the future first-class grant-assertion boundary; verify no docs imply support for multiple qualifier variants. +- [ ] 12.4 Benchmark uncaveated, Caveated-empty-context, Caveated-bound-context, true, false, and conditional paths across backends/runtimes; report context point-read and evaluator overhead. +- [ ] 12.5 Update formal models or executable decision inventories for validity, conditional permissionship, context precedence, and owner-qualified identity uniqueness; verify generated artifacts and refinement tests are clean. +- [ ] 12.6 Run Phase 2 JVM and ClojureScript suites using the project-prescribed nREPL workflow, heavy Caveat/storage benchmarks explicitly, and `bin/formal source-closure`; verify all release gates pass. +- [ ] 12.7 Run strict OpenSpec validation and verify proposal, design, tasks, and all six capability delta specs are accepted. + + +## 13. Adversarial regression gates from the 2026-09-04 review + +- [ ] 13.1 Exercise corrupt/missing/shared context, duplicate qualifiers, malformed validity, and unsupported Phase 1 qualifiers under `viewer - banned`; verify typed whole-operation failure and no successful cache/page publication. +- [ ] 13.2 Verify owner-qualified identity with equal tuple prefixes on different subjects/resources; exercise concurrent creates/touches/deletes, unknown-writer qualification, bounded repair, and ambiguous context ownership on every supported writer topology. +- [ ] 13.3 Verify a stable union answer does not widen an unstable child denotation's certificate; test backward administrative valid-time, pinned cursor resumption, and newly active candidates before a live cursor position. +- [ ] 13.4 Verify expiration-index-only repair changes maintenance diagnostics without changing authorization content proofs; round-trip the physical index and race collection with rescheduling. +- [ ] 13.5 Verify clock uncertainty cannot grant by expiring a subtracting edge early; explicit temporal views still use exact selected time. +- [ ] 13.6 Verify every admitted Caveat type, shallow context merge, type conversion after precedence, reference/profile pin, recursive residual termination, and conditional/error distinction on CLJ and Node before Phase 2 activation. diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/.openspec.yaml b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/.openspec.yaml new file mode 100644 index 00000000..1d9aeef9 --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-04 diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/design.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/design.md new file mode 100644 index 00000000..351fe66d --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/design.md @@ -0,0 +1,274 @@ +## Context + +See [proposal.md](proposal.md) for scope and the [historical review](../2026-09-03-add-v9-qualified-relationship-tuples/review-2026-09-04.md) for measured evidence and rejected alternatives. The v8 engine currently uses four-component v7 Relationship tuples. Its endpoint-local ordering, logical identity, paired mutation, Relation stamps, proofs, and cursors all depend on that representation. + +The previous eight-slot proposal was not implemented. This change replaces it directly; it is not an upgrade from a released eight-slot v9. The permission representation remains separate from the Relationship storage version. + +## Goals / Non-Goals + +**Goals:** retain covering forward/reverse adjacency, keep qualifiers out of logical identity, make expiry independent of timely writes, and preserve sound authorization/cache/pagination behavior across all supported adapters. Reserve Caveat positions in the first release and deliver a qualified Caveat evaluator subsequently. + +**Non-Goals:** scheduled activation, recurring schedules, transaction-metadata validity, schedule-ref entities, multiple independent grants per logical edge, multiple Caveats per edge, general bitemporal storage, an automatic migration, or a mandatory global expiration index. The remaining eighth Datomic tuple position is not assigned by this proposal. + +## Decisions + +### 1. Freeze one seven-component endpoint ABI + +```clojure +;; Stored on subject-eid: +[subject-type relation-eid resource-type resource-eid + caveat-eid caveat-context-eid valid-until-ms] + +;; Stored on resource-eid: +[resource-type relation-eid subject-type subject-eid + caveat-eid caveat-context-eid valid-until-ms] +``` + +Persisted attributes: + +```clojure +:eacl.v9.relationship/subject-type+relation+resource-type+resource+caveat+caveat-context+valid-until +:eacl.v9.relationship/resource-type+relation+subject-type+subject+caveat+caveat-context+valid-until +``` + +Datomic Pro, Datahike, and Datalevin tuple declarations use: + +```clojure +[:db.type/keyword :db.type/ref :db.type/keyword :db.type/ref + :db.type/ref :db.type/ref :db.type/long] +``` + +DataScript stores the same fixed-length ordinary vector with equivalent validation. All optional slots remain present. A permanent uncaveated Relationship has three trailing nils. Slot six without slot five is invalid. There is exactly one authoritative tuple on each endpoint; no separate expiration datom is required. + +Owner plus components one through four identifies an endpoint-local Relationship. The logical cross-direction identity includes both resolved endpoints, Relation, and the existing wildcard/subject-relation codec state. Never use the first four tuple components without the owner as a global key. Qualifiers can change; they cannot create a second assertion identity. + +Two endpoint values serve two adjacency access patterns. Putting all qualifiers in a single value is possible and is exactly what each half does. Removing the reverse half would remove the existing resource-owned covering index. Both halves carry the deadline so reverse traversal needs no forward lookup just to determine expiry. + +A two-slot Caveat representation avoids a binding entity for Caveats with empty bound context. Seven components suffice for the requested semantics. Neither storing a start bound that is always nil nor reserving an eighth semantic field is needed. + +### 2. Keep qualifiers after the opposite endpoint + +Within an endpoint's attribute, the first three components define the typed Relation prefix and component four orders the opposite endpoints. A hop uses one authoritative candidate stream. Caveat/context/expiry fields arrive with that candidate; non-caveated expiry testing adds a scalar comparison, not a separate entity lookup. + +Logical-identity probes now use prefix/group access rather than assuming callers know the full tuple. Seek/rseek bounds use the full seven-component shape and backend-certified low/high qualifier sentinels. Nil is a low sentinel, not a descending upper sentinel. Test negative timestamps, extrema, and inclusive/exclusive continuation over an entire identity group. Stop on entity, attribute, arity, and prefix boundaries. + +Let `D` be the stored degree of the selected adjacency and `k` the candidates advanced to obtain a page. Cost includes index positioning plus processing `k` candidates and any proof/context/evaluator work. It is not bounded by the output page size. A trailing expiry field cannot skip an arbitrary expired prefix. Nor does a low global expiring percentage bound the local percentage within a queried prefix. + +The previous million-edge 111.445917 ms probe deliberately advanced a million candidates to return twenty. The evenly distributed 5% probe instead saw small warm pages around 9–15 microseconds for cached/inline variants, with materially different full-scan costs. These establish different workloads, not a contradiction or a seven-slot service-level target. + +Leading expiry would order adjacency by time instead of opposite endpoint and break existing pagination. A separate time-leading maintenance index could support global expiry ranges, but it would add maintained data. Initial collection uses bounded scans; a later measured need can justify an independently versioned derived index without changing this ABI. + +### 3. Define expiry as one exclusive end + +A normalized finite deadline is an exact UTC epoch-millisecond integer in `[-9007199254740991, 9007199254740991]`. Nil is unbounded; maximum integer is still a finite exclusive deadline. Reject fractional, out-of-range, or precision-losing input rather than round it. + +For a well-formed stored edge in the selected database: + +```clojure +(or (nil? valid-until-ms) + (< evaluated-at-ms valid-until-ms)) +``` + +There is no `valid-from` field or hidden activation derived from assertion transaction time. Reject start-bound/schedule inputs explicitly. A Relationship is eligible immediately when present in the selected committed or speculative database, subject to expiry, Caveats, and the permission expression. Consumer scheduling means deciding when to transact its creation; ordinary consistency determines when a reader selects a basis containing it. + +An absolute expiry is not extended when a consumer's delayed creation job finally runs. Admitted already-expired Relationships remain stored and immediately inactive; they retain ordinary identity/conflict semantics until replaced or deleted. Applications wanting to renew use `:touch` with an explicit new deadline. This makes import, replay, and delayed submission deterministic and avoids an implicit lease-from-commit interpretation. + +Filter expiry at the edge boundary in both directions, before context loading/CEL evaluation and before the edge enters union, intersection, exclusion, arrows, recursion, lookup, or count. “Expired” means absent as evidence, not “authorization must deny”: removing negative evidence may grant. + +### 4. Preserve guarded identity mutation + +`:create` conflicts with any stored state of the identity, including an expired assertion. `:touch` creates when absent or replaces the exact old pair and changed bound context atomically. `:delete` requires identity only. A semantically unchanged touch can be a no-op and must not allocate a new context just to force an assertion transaction. + +Resolve aliases before batch deduplication. Identical repeated updates collapse; conflicting updates of one resolved identity fail admission. Different identities can have different expiry deadlines and Caveats in the same transaction. + +Guard current identity/pair/context state at commit, using the backend's transaction-time guard or a Relation-generation CAS with replanning. A serialized stale plan is not safe: two touches retracting the same old tuple and adding different replacements can create two qualifier variants. Datomic set uniqueness is on the entire value, not its prefix. Unsupported writer topologies reject the write; they do not weaken to plan-time checking. The same guard contract applies to public transaction helpers and speculative execution. + +Each actual Relationship mutation stamps every affected Relation once per transaction. The Datomic persisted attribute is `:eacl/relation-version`; its existing writer turns ordinary stamps into CAS guards. Renewal changes the tuple, so no redundant-assertion override is needed. A guarded exact replacement is one transaction and has no committed absence gap. + +### 5. Share immutable expressions and keep bound context sparse + +Separate a named Caveat definition from the expression it executes. The named definition is the schema identity used by `with ` and tuple slot five. It references an immutable, interned expression entity. Multiple names with identical expression content can therefore share a program without conflating their schema permissions or lifecycle. The conceptual representation is: + +```clojure +;; Named definition; expression-eid is an ordinary entity reference. +{:db/id caveat-eid + :eacl.caveat/name canonical-name + :eacl.caveat/expression expression-eid} + +;; Shared, immutable expression; payload is a bounded canonical encoding. +{:db/id expression-eid + :eacl.caveat-expression/key expression-key + :eacl.caveat-expression/payload canonical-expression-payload} + +;; Decoded payload shape, not a native nested-map datom: +{:format :eacl.caveat-expression/v1 + :source exact-cel-source + :parameters canonical-typed-parameter-declarations + :profile semantic-profile-id} +``` + +Freeze physical attribute types and portable encoding before implementation. The expression key is a versioned digest of the entire canonical payload, not source text alone. Canonical identity preserves exact source text and parameter names/types under a deterministic declaration order; it includes the semantic profile. Different whitespace or logically equivalent formulas need not deduplicate. Caveat name, Relationship identity, expiry, and bound/request values do not enter expression identity. No semantic-equivalence solver is required. + +Schema writes validate first, then atomically intern the full payload and reference its eid. Concurrent admissions of identical content must resolve to one expression entity in the selected database. A digest is an index key: compare full canonical content before reuse and reject a collision instead of merging unequal definitions. Implement equivalent interning guards on every backend, including speculative views. An eid is database-local and is not sufficient to identify content across sources, recreated databases, or divergent speculative branches. + +A supported definition edit references a new or existing immutable expression and changes the schema generation in the same guarded transaction. It never edits a published expression payload in place. The named-definition reference and complete expression content participate in the selected snapshot's schema proof. Unknown-writer qualification must detect out-of-band reference/payload changes; an old content key or claimed immutability cannot replace that proof. Retained old database views resolve their own definitions and expressions. + +Initially retain unreferenced expression entities for reuse; do not add shared-expression garbage collection to Relationship collection. Removing a named definition remains subject to existing in-use guards. An eventual expression collector would need a separate guarded proof of no remaining schema references. A program cache entry is disposable process state and does not establish database ownership. + +A non-empty bound context uses one internal entity: + +```clojure +{:db/id context-eid + :eacl.caveat-context/payload canonical-typed-context} +``` + +The entity has no public object identity, belongs to exactly one logical Relationship, and is immutable after publication. Both endpoint halves reference it. Empty bound context uses nil and allocates no entity. Changed context creates a replacement and retracts the old owned payload atomically with both refs. Expiry-only changes can retain the unchanged context ref. Context is not deduplicated across Relationships, avoiding shared deletion/refcount semantics. + +Managed object deletion and collector deletion remove both endpoint halves and owned context. Do not rely on automatic cascading or inverse-reference indexing of refs embedded in tuple values. Shared Caveat definitions and expression entities are never owned by a Relationship or collected with it. + +A trusted managed source does not need a new opposite-half read for every traversal candidate simply because the tuple is wider. The admitted-writer and source-certification contracts establish structural invariants. Unknown-writer content proofs still cover both complete halves, endpoint public identity, and referenced context payload; a digest detects changes but does not establish structural validity. A source that cannot establish required integrity fails qualification or performs the necessary scope validation. Count those reads explicitly. + +An actually encountered invalid shape, duplicate variant, missing required authoritative payload, or evaluator fault aborts the operation. Do not erase failed evidence and continue through exclusion. Bounded repair can remove discoverable variants; ambiguous context ownership is reported/quarantined, never guessed. + +### 6. Deliver Caveats with a qualified portable value model + +Phase 2 supports zero or one allowed Caveat per Relationship, validated against the exact subject-type/wildcard/subject-relation branch. Preserve top-level `caveat` definitions and `with ` grammar. Both bare and Caveated branches allow optional attachment; only a matching Caveated branch makes attachment required. + +Capture structurally bounded request context once. Effective context is a shallow parameter-name merge where bound values replace request values. Type conversion applies to the resulting effective parameter, so a shadowed wrong-typed request value is not treated as the parameter. Bound maps replace whole request maps; do not recursively merge them. Unknown bound keys are invalid. Unknown request keys are not expression variables and remain in conservative request identity. + +Use **[exoscale/cel-parser](https://github.com/exoscale/cel-parser)**, Clojure coordinate `com.exoscale/cel-parser`, for JVM CEL parsing and interpretation. EACL owns the Caveat/schema grammar, admitted type/profile validation, context conversion, partial-result adapter, permission algebra, and result/error mapping. Preserve a canonical portable typed representation and deterministic bounded evaluation. No ambient clock, I/O, mutable globals, or hidden network calls belong in the evaluator. Any caller-supplied time used by a Caveat is explicit request context and does not replace the trusted expiry clock. + +Successful results are has-permission, no-permission, or conditional-permission with missing fields/residuals. Errors and limits are separate typed failures. Boolean `can?` returns true only for has-permission, false for the other two successful states, and propagates errors. Internal algebra never turns conditional or failed subtracting evidence into false prematurely. + +Residual identity preserves Caveat definitions and bound values, supports bounded simplification/sharing, and terminates under supported recursion. Equivalent conditional cycles cannot grow formulas without bound. Reject unsupported recursive constructs at schema admission. + +The existing secure-format envelope is not a complete CEL value model. Freeze a typed encoding and explicit type/coercion/function profile before Phase 2 activation. Any admitted int64, uint64, double, timestamp, bytes, map/list, or other value must have exact declared semantics on CLJ and CLJS; reject unsupported types rather than quietly narrow them. Record a concrete SpiceDB release/commit with the differential corpus. Reference compatibility remains limited to the tested published profile. + +Committed schema changes reject removal of Caveats still referenced by stored Relationships, including expired ones. Reject parameter/type/branch changes that invalidate stored context. Serialize schema guards with Relationship writes. Existing speculative Relation `:retain-inert` behavior does not authorize orphaning a Caveat: retained Relationship data must keep any referenced Caveat definition valid until explicitly removed. This is an intentional stricter rule for Caveat lifecycle. + +#### Lazy compilation and client program reuse + +Resolve `tuple caveat-eid -> named definition -> expression-eid/content` through the selected snapshot's validated schema catalog. Once traversal reaches a well-formed, expiry-active Caveated edge that actually needs evaluation, obtain or build its client-local program. Client construction, ordinary schema reads, stored Relationship enumeration, expired candidates, and unvisited paths do not eagerly compile their expressions. Schema-write admission still performs bounded syntax/name/type/profile validation before commit; this is separate from populating a client's runtime program cache. + +Use `exoscale.cel.parser/make-program` for the reusable parsed program and `eval-for` with fresh effective bindings for each evaluation. Here compilation means constructing the parsed executable representation and EACL's validated evaluation plan, not JVM bytecode generation. `parse-eval` is not the repeated traversal path. Programs and any reusable subexpression plans contain no request/Relationship bindings, residual state, or authorization results. + +The bounded client cache key is the complete canonical expression identity plus an evaluator fingerprint covering library/build version, adapter/plan format, numeric conversion, partial/error semantics, admitted options/macros/functions, and extension implementations. A digest lookup verifies canonical content equality. A snapshot-scoped eid-to-content mapping makes repeated lookup cheap, but a bare eid or Caveat name cannot be the program key. Equal content may reuse a program across names or snapshots once each snapshot independently resolves and validates that content. Unrelated schema-generation changes need not discard a pure expression program; answer/plan/cursor proof validation still observes the schema change. + +Establish canonical content identity and any digest-collision comparison when admitting or refreshing the validated catalog/cache mapping, then retain a client-local handle to that identity. Warm traversal can index by this qualified handle without recanonicalizing, rehashing, or comparing the entire source for every edge. Revalidate the mapping under the ordinary selected-view proof rules; compact handle lookup cannot hide a changed definition or payload. + +Coalesce concurrent misses per key into one in-flight build. Publish only successfully built immutable programs, remove failed in-flight entries, bound resident program count/weight and concurrent build work, and allow eviction followed by equivalent recompilation. Per-caller cancellation must not corrupt shared state or cancel another caller's usable build. Verify concurrent evaluations with different bindings; fresh visitors in upstream source are evidence about construction, not a concurrency certification. Cache misses and compile work count against execution limits and produce typed failures, never a denial. + +The extra expression entity is schema-scale indirection, not a new per-Relationship entity or an extra adjacency seek. A cold catalog/program path can need definition and expression reads plus compilation. With the selected schema catalog and program resident, repeated candidates require an in-memory lookup and evaluation with their own bindings; non-empty bound context can still need its own payload read. Qualify those conditions and count reads explicitly. Expression interning reduces repeated compilation and stored duplicate source; it does not eliminate candidate scanning, evaluator work, proof validation, or context reads. + +#### cel-parser integration and qualification + +Pin an exact `com.exoscale/cel-parser` artifact/version or immutable source build and resolved dependency set before qualification. The source review used commit `66ac2edbe9c196498d9f925ea4bf664ea0c166a2`; its [VERSION](https://github.com/exoscale/cel-parser/blob/66ac2edbe9c196498d9f925ea4bf664ea0c166a2/VERSION) contains `0.1.8`. This identifies inspected source, not an installed or qualified release. Verify JDK/transitive compatibility in nREPL. A smaller library is not evidence of lower end-to-end latency or integration effort. + +The inspected [parser API](https://github.com/exoscale/cel-parser/blob/66ac2edbe9c196498d9f925ea4bf664ea0c166a2/src/exoscale/cel/parser.clj) separates program construction and evaluation. Its [ANTLR implementation](https://github.com/exoscale/cel-parser/blob/66ac2edbe9c196498d9f925ea4bf664ea0c166a2/src/exoscale/cel/antlr.clj) constructs a Java parse tree; parsing is not a static CEL type checker. The EACL adapter must validate names, admitted overloads, and Boolean result typing before schema commit and provide the portable typed representation. Do not weaken admission to “parses successfully” or delay malformed-schema discovery until traversal. + +By default `eval-for` returns errors as values; [error unwrapping](https://github.com/exoscale/cel-parser/blob/66ac2edbe9c196498d9f925ea4bf664ea0c166a2/src/exoscale/cel/expr.clj) produces an exception object. EACL must recognize typed error results explicitly or use the verified throwing option and map exceptions. Never interpret arbitrary truthy Clojure values as grants. Only a typed Boolean true is definite true. EACL's missing-context layer must preserve checked expression/binding identity, decisive Boolean branches, residuals, and missing fields; it cannot classify all missing-variable errors as conditional. The inspected API supplies no established unknown/residual protocol, and its [unknowns test file](https://github.com/exoscale/cel-parser/blob/66ac2edbe9c196498d9f925ea4bf664ea0c166a2/test/exoscale/cel/generated/unknowns_test.clj) contains no cases. Partial evaluation is an implementation gate, not an assumed library feature. + +The inspected [visitor](https://github.com/exoscale/cel-parser/blob/66ac2edbe9c196498d9f925ea4bf664ea0c166a2/src/exoscale/cel/visitor.clj) reparses macro bodies inside collection iterations for `map`, `all`, `exists`, `exists_one`, and `filter`. Caching only the outer `make-program` result therefore does not establish parse-free warm evaluation. For admitted macros, qualify reusable body plans through the library integration; otherwise reject those constructs in the published profile before activation. Instrument parser invocations for nested macros as well as top-level builds. No per-element source reparsing may be hidden in a warm-cache benchmark. + +Freeze explicit parse/tree, input, collection, regex, and aggregate request-work limits, with verified enforcement in the chosen library integration. Do not invent a library budget API or assume a future timeout stops underlying work. Extend the bounded integration or reject a construct whose required limits cannot be enforced. The [documented differences](https://github.com/exoscale/cel-parser/blob/66ac2edbe9c196498d9f925ea4bf664ea0c166a2/README.md) and value conversion behavior require differential tests, including namespaces/map keys, numeric conversion, overload errors, and any SpiceDB-specific extension. Unqualified behavior is rejected explicitly. + +**ClojureScript remains a release requirement.** This implementation uses JVM/ANTLR classes. Its source is not directly a CLJS evaluator. Keep those imports and programs behind the JVM boundary; persist only the portable canonical representation. Specify and qualify the CLJS path against the same profile, JVM adapter, and pinned SpiceDB corpus before Phase 2 activation. Do not silently drop CLJS Caveats or introduce remote evaluation. No CEL runtime benchmark or conformance result is established by this source review. + +### 7. Capture one trusted evaluation time + +The selected authorization view is `(immutable database basis, evaluated-at-ms)`. A client-targeted operation captures one fresh trusted time even when consistency reuses a database pin. All subproblems and checks within one batch use that same time. An explicitly retained EACL snapshot freezes both values; `eacl/with` and `eacl/with-schema` preserve its time while deriving a new database value. + +A causal consistency token constrains database selection; it does not alone prove a current clock instant. Native `d/filter`/`d/with` values remain outside the public source boundary. This change does not introduce a caller-controlled time override on ordinary authorization. Deterministic client clocks support tests; retained historical EACL snapshots expose their fixed evaluation time and remain subject to backend retention. + +Validate clock values and the configured nondecreasing client-clock contract. A regression fails current snapshot acquisition rather than reviving expired Relationships silently. Multiple peers need the documented trusted-clock/skew contract. Expiring each edge early is not a generic safe skew policy because early removal of a ban grants. No new uncertainty-window evaluator is required in this release; if one is offered later, it must prove whole-permission invariance or fail explicitly. + +Snapshot results describe the captured time, even if wall time crosses expiry during execution. They are not a promise that a grant remains valid at some later application side effect. + +### 8. Add deadlines to reusable artifacts, not timers to the graph + +Expiry-only removes edges over increasing time at a fixed basis and fixed Caveat input. Positive-only expressions therefore cannot gain members, but EACL's exclusion breaks that conclusion for general permissions: + +```text +read = viewer - banned +viewer: permanent +banned: valid-until 100 +read at 90 = no-permission +read at 100 = has-permission +``` + +No Relation write occurs at 100. Cache coherence must check time as well as database dependencies. + +The initial conservative certificate is: + +```clojure +{:evaluated-at-ms t0 + :reusable-before-ms deadline-or-nil} +``` + +Reuse requires `t0 <= selected-time` and either nil deadline or `selected-time < deadline`, plus all existing source/branch, causal, schema, Relation, object-identity, context, and authenticated-entry checks. Earlier frozen snapshots need exact-time identity or a separately proved interval; never reuse a forward certificate backwards. Provider TTL and asynchronous eviction are optimizations only; compare the deadline before every accepted hit. + +Each certificate proves the stability of its own artifact: Boolean/three-state answer, denotation, projection, residual, count, page, or retained frontier. Derive a conservative deadline from all expiry-sensitive witnesses/counter-witnesses needed to establish that artifact. The minimum future expiry over a complete relevant evidence set is safe; using only returned edges is not. Static Relation dependency closure remains complete even when a smaller temporal witness is proven sufficient. Already-expired edges cannot reactivate through time alone. Missing completeness proof means exact-time reuse or no publication, never a fabricated nil deadline. + +A permanent owner can keep `owner + viewer` true while the `viewer` child expires. An unbounded certificate for the final Boolean must not widen the child's deadline. Cached denials under exclusion and conditional missing-field sets need the same reasoning. A proven positive-only denial optimization is allowed later, but first-release correctness cannot depend on inferring monotonicity from the absence of a direct `-` in the top-level expression. + +Keep three concepts distinct: pre-evaluation lookup scope contains complete canonical request context and evaluator/ABI identity; dependency proofs cover named definitions, referenced expression content, bound context, and Relation state; the authenticated value contains permissionship, residual/missing fields, and temporal certificate. Do not require knowing the answer to calculate a lookup key. The separate program cache stores executable structure only: a program hit is not proof that any authorization result or residual can be reused. + +### 9. Pin snapshot cursors; restart live cursors at unsafe expiry + +A cursor authenticates temporal mode, captured evaluation time, its own deadline, source/schema/Relation proofs, result policy, ordering, context, and evaluator/ABI identity. + +- An explicit snapshot cursor preserves the same database basis and time. Wall time beyond a Relationship expiry does not change its frozen graph. Token lifetime and history availability remain independent checks. +- A live client cursor captures a fresh time and validates its complete retained-state certificate. Crossing the deadline or moving before the certificate start returns a typed restart requirement and no resumed page. The caller starts a new query. + +This remains necessary without scheduled activation. If an object before the cursor position was excluded by an expiring ban, it can become authorized after that ban expires. Continuing from the old position would omit it. Certify skipped candidates, negative evidence, lookahead, residuals, and traversal frontier, not merely emitted results. If proof is incomplete, a pinned query or explicit restart is the safe route. + +### 10. Make collection optional bounded maintenance + +The initial maintenance operation scans authoritative forward tuples with a stable keyset over a selected database basis. It accepts a trusted cutoff and retention policy, charges scanned candidates as well as deletions, and supports bounded batches/progress. Select finite expiries at or before the cutoff; cutoff cannot be later than the trusted collection time. Permanent Relationships are never selected. + +For each candidate, reconstruct the exact reverse value and revalidate the current pair/owned context in the admitted transaction. A changed or renewed identity causes retry/skip with an explicit race outcome, not deletion of its replacement. Each actual deletion retracts both halves plus singly-owned context and stamps every affected Relation once. No stamp is needed for scanning or a no-op batch. The collector must use the normal writer concurrency protocol, not raw stale retractions. + +A run over a frozen scan basis does not promise to collect rows concurrently added behind its cursor; later passes cover them. Mark completion relative to that scan basis, not “there are no expired Relationships now.” Define limits/progress separately from authorization pagination. Recovery from an unavailable scan basis restarts a maintenance pass. + +No background job is required for authorization correctness. Collection improves stored scan density and bounds retained current facts according to operational policy. It can change stored reads and CREATE conflicts even when effective authorization at/after cutoff is unchanged. It therefore conservatively invalidates the affected Relation proof. Special GC-preserving answer proofs are deferred. + +A derived expiry index is deferred until measured maintenance demand justifies it. It could later use a subject-owned time-leading permutation of the seven fields and AVET range access; it would be non-authoritative, independently rebuildable, and absent from authorization content certificates. This release does not add its schema, writer maintenance, reconciliation, or configuration surface. + +Retraction is not excision. Datomic normally retains retracted facts in history; this operation does not promise durable byte reclamation. A post-collection current basis evaluated at an earlier time does not restore the deleted Relationship. Historical answers require an appropriate retained old basis and authoritative context/schema history; no stronger cross-backend history contract is introduced. + +### 11. Freeze compatibility and phase activation + +Relationship storage version 9 means the seven-slot layout and its specific format fingerprint. Tuple arity alone is insufficient: reject old four-slot and discarded eight-slot layouts, and any experimental seven-slot format with different component meanings. Inert legacy schema definitions may remain installed, but populated incompatible attributes/mixed data reject startup. No dual-read or automatic conversion is shipped. + +Phase 1 enables expiry and requires both Caveat slots nil. Phase 2 enables the qualified Caveat profile under a persisted semantic capability epoch without changing tuple positions. Cache/cursor/engine fingerprints include phase semantics. + +All active current-serving readers and writers must be fenced at activation. A long-lived client cannot check a new epoch by reading its old minimize-latency pin. Use a coordinated stop/upgrade/activate cutover unless an independently enforceable deployment fence exists. Retained Phase 1 snapshots can remain explicit historical views, never current Phase 2 service. Check capability compatibility on selected views and mutation commits in addition to startup. + +## Risks / Trade-offs + +- Wider permanent tuples → qualify segment density, durable bytes, allocation, and scan throughput; two datoms do not imply unchanged space. +- Expired clusters → charge all candidate work, return typed resource-limit errors, and measure cleanup lag. A global 5% estimate is not a bound on a particular endpoint. +- Scan-based collection at large scale → expose bounded progress and measure throughput; a derived index remains a later optimization. +- Conservative deadlines reducing cache reuse → measure completed answers and intermediates separately; optimize only with complete temporal proofs. +- Exclusion, recursion, and Caveat partials → differential/model tests must cover negative and conditional paths, not only expiring positive grants. +- Unknown writers or schema races → use explicit source qualification and commit-time guards; never paper over failed evidence as false membership. +- Cross-runtime CEL divergence → admission rejects unqualified constructs and Phase 2 stays disabled until its pinned corpus passes. +- Minimal CEL library surface → qualify static checking, partial evaluation, macro-body reuse, and enforceable budgets; program caching alone supplies none of these contracts. +- Shared expressions → guard interning and schema references, retain unused immutable entities initially, and bound the client program cache independently of database retention. +- No migration path → publish rebuild instructions and validate a fresh database before switching traffic. + +## Migration Plan + +1. Keep the preceding proposal deprecated; do not apply its deltas as an intermediate release. +2. Publish the Relationship storage/API and cache/cursor compatibility breaks. +3. Export logical Relationships and required application/schema data; create a fresh v9 database and load through the supported writer. This is operator rebuild guidance, not conversion tooling. +4. Verify the seven-slot format, one canonical pair per identity, context ownership, storage/capability markers, and absence of populated incompatible attributes. +5. Qualify Phase 1 on every backend/runtime and switch traffic only after application authorization checks pass. +6. Implement/qualify Caveats and perform the fenced Phase 2 activation described above. +7. Rollback uses the previous database/backup with the matching previous EACL version. There is no in-place tuple downgrade. + +## Qualification and remaining implementation gates + +The proposal fixes the ABI, expression ownership and lazy program reuse, semantic boundaries, collection strategy, and cache/cursor correctness obligations. The exact cel-parser build and adapter configuration, canonical expression encoding and interning guards, bounded compatibility profile, type/partial/budget integration and qualified CLJS execution path, public option/error naming consistent with current APIs, and numerical release budgets must be recorded during their explicit implementation tasks before the corresponding feature is activated or benchmark accepted. They do not justify claiming implementation or compatibility today. + +Use nREPL for Clojure semantic checks, in-memory Datomic for mutation/certificate counterexamples, and an isolated local `:dev` database with at least one million independently verified logical identities for real latency. Benchmark the actual seven-slot implementation at 0%, 1%, and 5% finite-expiry distributions, distinguish active/unexpired from expired fractions, vary expired clustering and endpoint degree independently, include one-transaction-per-Relationship metadata alternatives only if compared, and measure both directions, cold/warm states, concurrent renewal/GC, and complete public authorization paths. Do not transplant the old four/eight-slot numbers as acceptance thresholds. diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md new file mode 100644 index 00000000..6aa8fb1a --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/proposal.md @@ -0,0 +1,42 @@ +## Why + +EACL needs Caveats and expiring Relationships without making every Relationship a separate entity or requiring a transaction exactly at expiry. The preceding eight-slot scheduling proposal added future activation, interval bookkeeping, and a mandatory collection index that are unnecessary when consumers can create Relationships when they should begin. + +This proposal supersedes [2026-09-03-add-v9-qualified-relationship-tuples](../2026-09-03-add-v9-qualified-relationship-tuples/proposal.md), which remains deprecated and unimplemented. Its [review and REPL evidence](../2026-09-03-add-v9-qualified-relationship-tuples/review-2026-09-04.md) inform this replacement; the old deltas must not be applied first. + +## What Changes + +- **BREAKING:** replace the four-slot v7 persisted Relationship representation used by the v8 engine with exactly two authoritative seven-slot endpoint values: forward `[subject-type relation-eid resource-type resource-eid caveat-eid caveat-context-eid valid-until-ms]`, and its reverse counterpart. Owner plus the first four components remains identity; the three trailing values are qualifiers. +- Support optional, exclusive `valid-until` in both directions. A stored Relationship is expiry-active when the bound is absent or the selected evaluation time is earlier than the bound. Expiration needs no write, callback, cache eviction job, or collection pass. +- Exclude `valid-from`, schedule entities, transaction-metadata validity, and native backend valid-time integration. Consumers perform future creation as an ordinary guarded Relationship write; delayed insertion is acceptable to this contract. No background activation service is supplied. +- Preserve `:create` conflicts against all stored qualifier states, atomic qualifier replacement through `:touch`, and identity-only `:delete`. Relationships with different deadlines can share a transaction. Renewing a Relationship changes both tuple values and its affected relation mutation identity. +- Retain the Caveat design: shared typed schema definitions, zero or one Caveat per Relationship, sparse immutable context payloads only for non-empty bound context, bound-over-request precedence, bounded deterministic evaluation, conditional permissionship, and a pinned, tested SpiceDB compatibility profile. +- Use [exoscale/cel-parser](https://github.com/exoscale/cel-parser) (`com.exoscale/cel-parser`) for JVM CEL parsing and interpretation behind the EACL adapter. Pin its version and semantic profile; qualify EACL's type validation, partial results, execution limits, and ClojureScript path explicitly rather than assuming the library supplies them. +- Intern immutable expression entities containing canonical source, typed parameter declarations, and profile identity. Named Caveat definitions reference these shared entities; Relationship tuple slots remain unchanged. Equivalence means equal canonical expression content, not proof that different CEL formulas are logically equivalent. +- Lazily build programs in the EACL client when traversal first evaluates an expiry-active Caveated edge, then reuse them across identical expressions and different bindings. Keep the bounded program cache separate from authorization results, with concurrent miss coordination and complete expression/evaluator identity. Qualify warm macro execution as well as top-level program reuse. +- Deliver the seven-slot ABI and expiry first, reserving Caveat slots as nil; enable Caveats in a second phase without another tuple change. Fence obsolete clients at semantic capability activation. +- Add expiry deadlines to cached grants, denials, residuals, and continuations. Expiring negative evidence can grant permission under exclusion; expiry-only does not make version-only caches coherent. Snapshot cursors freeze evaluation time; live cursors restart when their complete temporal proof expires. +- Make expired-Relationship collection optional, bounded maintenance over the authoritative forward tuples. Do not require a third per-Relationship expiration-index datom. Collector deletions use the normal atomic pair/context lifecycle and bump `:eacl/relation-version` for every affected Relation; passing time does not. +- **BREAKING:** accept only fresh/rebuilt v9 stores, reject incompatible or mixed Relationship data and old cache/cursor artifacts, and provide rebuild guidance without automatic migration or dual reads. + +## Capabilities + +### New Capabilities + +- `expiring-relationships`: expiry-only semantics, trusted evaluation-time snapshots, consumer-owned activation, stored versus expiry-active reads, renewal, optional guarded collection, and history limits. +- `relationship-caveats`: typed named Caveat declarations, shared immutable expression entities, lazy client program compilation, immutable bound context, request context, conditional algebra, encoding/profile conformance, and lifecycle safety. + +### Modified Capabilities + +- `converged-relationship-storage`: fixed seven-slot ABI across all backends, identity guards, ordered scans, atomic pair mutations, proofs, and rebuild boundary. +- `public-authorization`: evaluation-time and Caveat request boundaries, three-state checks, definite/conditional enumeration, and explicit limit/restart errors. +- `dependency-validated-authorization-cache`: expiry certificates for each reusable artifact, complete Caveat identity/dependencies, and ABI fencing. +- `cursor-dependency-validity`: pinned/live time modes, expiry-safe continuation, context authentication, and seven-slot ordering. + +## Impact + +Changes span the shared Relationship codec/planner, all four backend schemas and adapters, public snapshot/read/write/check/lookup/count APIs, schema grammar and safety checks, cache/proof/cursor formats, bounded maintenance, formal models, documentation, and CLJ/CLJS conformance suites. The permission representation is not redesigned by the Relationship storage version bump. + +Phase 2 introduces the pinned `com.exoscale/cel-parser` JVM dependency and its tested transitive dependency set, including its Java/ANTLR boundary. Parsed programs remain rebuildable client runtime state; they do not become the persisted or portable Caveat format. Schema admission still rejects invalid definitions before commit, independently of lazy runtime compilation. Library size alone does not establish lower total integration cost: static validation, conditional evaluation, macro-body reuse, execution limits, and cross-runtime behavior remain explicit qualification work. + +The retained million-Relationship experiments establish Datomic filtering and mutation behavior, not seven-slot production performance. Before release, benchmark the actual expiry-only implementation with 0%, 1%, and 5% expiring workloads plus concentrated expired prefixes, publish workload and transaction diversity, and establish numerical latency/space budgets before qualification. No CEL/SpiceDB compatibility or performance target is declared satisfied by this proposal. diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/converged-relationship-storage/spec.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/converged-relationship-storage/spec.md new file mode 100644 index 00000000..646770fb --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/converged-relationship-storage/spec.md @@ -0,0 +1,520 @@ +## MODIFIED Requirements + +### Requirement: Two-value endpoint representation + +Each EACL relationship SHALL be represented by exactly two cardinality-many, +indexed, authoritative endpoint values: a forward value on the subject entity +and a reverse value on the resource entity. The values SHALL use this fixed +seven-component v9 order: + +- forward: + `[subject-type relation-eid resource-type resource-eid caveat-eid caveat-context-eid valid-until-ms]` +- reverse: + `[resource-type relation-eid subject-type subject-eid caveat-eid caveat-context-eid valid-until-ms]` + +The owning endpoint eid plus the first four components SHALL identify the +logical relationship. The first four components alone SHALL be only an +endpoint-local key and MUST NOT be used as a global relationship identity. Components +five through seven SHALL be qualifiers and SHALL be `nil` when absent. Both +physical halves SHALL contain the same Caveat definition ref, Caveat context +ref, and expiry. + +A non-nil Caveat context ref SHALL require a non-nil Caveat definition ref. A +non-nil Caveat definition ref with a nil context ref SHALL represent a Caveat +with empty relationship-bound context. EACL SHALL store no entity whose sole +purpose is to represent every relationship. + +#### Scenario: Relationship creation + +- **WHEN** EACL creates a relationship between existing subject and resource endpoints +- **THEN** the backend stores exactly one forward endpoint datom and one reverse endpoint datom and stores no relationship entity + +#### Scenario: Permanent uncaveated relationship + +- **WHEN** EACL creates a relationship without a Caveat or expiry +- **THEN** it stores exactly one seven-slot forward value and one seven-slot + reverse value +- **AND** slots five through seven are `nil` +- **AND** it stores no relationship entity + +#### Scenario: Expiry-only relationship + +- **WHEN** EACL creates a relationship with expiry and no Caveat +- **THEN** slots five and six are `nil` +- **AND** slot seven contains the normalized expiry +- **AND** both endpoint halves contain identical qualifiers + +#### Scenario: Caveat with empty bound context + +- **WHEN** Phase 2 creates a relationship with a Caveat and no + relationship-bound context values +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six is `nil` +- **AND** no Caveat context entity is allocated + +#### Scenario: Caveat with bound context + +- **WHEN** Phase 2 creates a relationship with non-empty relationship-bound + Caveat context +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six contains the same sparse Caveat context eid in both halves +- **AND** the context payload is not duplicated inside the endpoint values + +#### Scenario: Context without Caveat + +- **WHEN** an update supplies or stored data contains a non-nil slot six with a + nil slot five +- **THEN** admitted writes reject it +- **AND** an affected operation rejects out-of-band authoritative corruption with a typed error + +#### Scenario: Duplicate physical value + +- **WHEN** the same complete endpoint value is added more than once in one database value +- **THEN** database set semantics retain one datom for that endpoint value + +#### Scenario: Same tuple value on two owners + +- **WHEN** two different subjects legitimately produce the same forward tuple value +- **THEN** their logical identities remain different because the owner eid is part of the key + +#### Scenario: Duplicate complete physical value + +- **WHEN** the same complete seven-slot endpoint value is added more than once + in one database value +- **THEN** database set semantics retain one datom for that endpoint value + +### Requirement: Ordered endpoint index access + +Every backend adapter SHALL implement candidate access for exact matching, adjacency, relationship +filtering, relation-in-use checks, and forward and reverse pagination using the single v9 endpoint attribute appropriate to the requested direction. +Pair, ownership, and content-proof validation MAY require additional reads. The first +three components SHALL remain the typed relation prefix, the fourth component +SHALL remain the opposite endpoint used for ordering and pagination, and all +three qualifier components SHALL remain trailing. + +Every tuple/vector seek boundary SHALL use the complete stored arity required by +the backend ordering contract. Normal v9 authorization and relationship reads +MUST NOT scan, union, merge, or prefer values from a v7 relationship attribute. + +#### Scenario: Forward endpoint scan + +- **WHEN** authorization or relationship pagination scans outward from a known + subject +- **THEN** the adapter performs one seek against that subject's v9 forward + endpoint values +- **AND** returns candidates ordered by resource eid before evaluating + qualifiers + +#### Scenario: Reverse endpoint scan + +- **WHEN** authorization or relationship pagination scans inward from a known + resource +- **THEN** the adapter performs one seek against that resource's v9 reverse + endpoint values +- **AND** returns candidates ordered by subject eid before evaluating + qualifiers + +#### Scenario: Exact logical relationship probe + +- **WHEN** EACL probes one known subject, relation, and resource +- **THEN** it seeks by the first four components using full seven-slot low and + high bounds +- **AND** obtains zero or one well-formed logical relationship +- **AND** treats multiple qualifier variants as corruption rather than choosing + a grant + +#### Scenario: Reverse-order continuation + +- **WHEN** a descending scan resumes from a relationship cursor +- **THEN** `rseek-datoms` or its backend equivalent starts from a full + seven-slot high bound; nil is not used as a high qualifier sentinel +- **AND** returns only values inside the requested endpoint prefix and ordering + boundary + +#### Scenario: Adjacent prefix is absent + +- **WHEN** no value exists for a requested prefix but an adjacent prefix exists +- **THEN** explicit entity, attribute, arity, and component guards prevent the + adjacent value from being returned + +#### Scenario: Legacy attributes remain installed but empty + +- **WHEN** a Datomic database still has immutable v7 attribute definitions but + no v7 relationship datoms +- **THEN** v9 reads never seek those attributes +- **AND** their schema presence adds no per-hop second seek + +### Requirement: Atomic pair mutation and repair + +Relationship mutation SHALL add, replace, or retract both v9 endpoint values in +one admitted transaction. Every planned create, touch, delete, or collection +SHALL validate at commit time the complete identity-group and auxiliary state +on which its plan depends and SHALL preserve `:create`, `:touch`, and `:delete` +semantics over the owner-qualified first-four-component logical identity. + +`:create` SHALL conflict with every stored temporal or Caveat state. +`:touch` SHALL create when absent and otherwise replace the exact old endpoint +pair and changed Caveat context entity with the requested canonical +state. `:delete` SHALL require only the logical relationship and SHALL remove +all discoverable physical variants so it can repair bounded corruption. +A writer topology unable to provide commit-time state validation SHALL reject +Relationship writes with a typed unsupported-writer error rather than use only +plan-time checks. Exceeding the configured repair bound SHALL fail atomically. + +#### Scenario: Complete relationship conflict + +- **WHEN** `:create` targets a logical relationship whose complete forward and reverse values exist +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Racing creates of one relationship + +- **WHEN** two creates for one identity are planned against the same pre-write basis +- **THEN** commit-time validation permits exactly one creation +- **AND** the committed database contains exactly one canonical endpoint pair + +#### Scenario: Racing creates with different qualifiers + +- **WHEN** two `:create` transactions for one logical identity are planned + against the same pre-write value with different qualifiers +- **THEN** exactly one creation commits +- **AND** the other fails with `:eacl/relationship-conflict` +- **AND** one canonical forward/reverse pair remains + +#### Scenario: Stale concurrent touches + +- **WHEN** two different touches are planned from the same old qualifiers and commit serially +- **THEN** the second plan cannot add its replacement after its expected old state changed +- **AND** it replans or fails with a typed conflict rather than leaving two variants + +#### Scenario: Touch replaces all qualifiers + +- **WHEN** `:touch` changes the Caveat, relationship-bound context, or expiry +- **THEN** EACL retracts the exact old forward and reverse values +- **AND** adds one matching new pair +- **AND** creates/retracts changed sparse context data atomically +- **AND** advances the affected relation mutation identity exactly once + +#### Scenario: Touch is unchanged + +- **WHEN** `:touch` supplies qualifiers canonically equal to the stored state +- **THEN** EACL may treat the operation as an idempotent no-op +- **AND** it does not allocate an unnecessary replacement context entity + +#### Scenario: Repeated and conflicting batch updates + +- **WHEN** one batch repeats an identical update for one logical relationship +- **THEN** the batch has the same outcome as one occurrence +- **AND** `:create` still conflicts with an identity that existed before the batch +- **WHEN** one batch contains semantically different updates for that identity +- **THEN** EACL rejects it with + `:eacl/invalid-relationship-update-batch` before transaction submission + +#### Scenario: Incomplete relationship repair + +- **WHEN** `:touch` targets missing, mismatched, or duplicated endpoint halves +- **THEN** EACL removes bounded discoverable variants and writes one matching + canonical pair + +#### Scenario: Unconditional deletion + +- **WHEN** `:delete` targets a complete, incomplete, corrupt, or absent pair +- **THEN** EACL removes every variant that is safely attributable within the configured repair bound +- **AND** it never retracts auxiliary data whose exclusive ownership has not been established + +#### Scenario: Delete omits qualifiers + +- **WHEN** `:delete` supplies the subject, relation, and resource but no Caveat, + context, or expiry +- **THEN** EACL removes the matching logical relationship if present +- **AND** removes its singly-owned context data + +### Requirement: Endpoint deletion and integrity + +Every backend adapter SHALL treat peer eids, Caveat definition eids, and Caveat +context eids embedded in endpoint values according to its native value +semantics. EACL object cleanup SHALL explicitly remove every touching endpoint +pair and owned auxiliary data before endpoint retraction. + +The module SHALL expose an offline integrity report covering malformed arity, +dangling or mismatched halves, qualifier disagreement, owner-qualified identity +collisions, invalid Caveat/context combinations, missing or multiply-owned +context entities, missing or malformed referenced Caveat expressions, and malformed expiry. An authoritative integrity fault encountered in an operation MUST +abort that operation with `:eacl/invalid-relationship-state`; it MUST NOT be modeled as +relationship absence, because absence on the subtracting side of exclusion can +grant access. + +#### Scenario: EACL object cleanup + +- **WHEN** `delete-object!` removes an endpoint +- **THEN** it removes local and peer endpoint values for every touching + relationship +- **AND** removes singly-owned Caveat context values +- **AND** retains shared Caveat definitions and expression entities +- **AND** publishes every affected relation mutation identity + +#### Scenario: Direct endpoint retraction + +- **WHEN** a consumer retracts an endpoint entity outside EACL +- **THEN** peer halves and context entities can remain as detectable corruption + because embedded refs are not relied upon for automatic cascading + +#### Scenario: Integrity scan + +- **WHEN** the offline integrity report scans the database +- **THEN** it reports each invalid pair, identity collision, Caveat/context + fault or expiry fault without mutating data + +#### Scenario: Corrupt relationship reaches authorization + +- **WHEN** authorization encounters an authoritative integrity fault on a candidate +- **THEN** the affected authorization operation fails with + `:eacl/invalid-relationship-state` +- **AND** it does not publish an answer or reusable continuation + +### Requirement: Database-visible proof coverage + +Unknown-writer relationship proofs SHALL commit to both complete seven-slot +endpoint values, the public identities of their endpoints, and all authoritative +relationship-bound Caveat context payloads. They SHALL cover only authoritative graph data, not optional future derived maintenance indexes. A proof SHALL separately validate structure; digest change +detection alone is not a structural-integrity certificate. A proof MUST change when any +answer-affecting relationship qualifier or referenced context changes out of band. + +Caveat definitions, their expression references, and complete canonical expression payloads SHALL be covered by the schema proof and generation. Managed expression immutability or a stored digest SHALL NOT replace selected-view content validation for unknown-writer sources. Expression entities SHALL remain shared schema data; they SHALL NOT change the seven-slot endpoint ABI or become Relationship-owned cleanup data. + +#### Scenario: One half changes out of band + +- **WHEN** one endpoint half changes for a relation in a cached request's + dependency closure +- **THEN** the next content-proof validation rejects the previous answer + +#### Scenario: Context payload changes out of band + +- **WHEN** a referenced context entity retains its eid but its payload changes +- **THEN** the database-visible content proof changes +- **AND** the prior answer is not reused + +#### Scenario: Qualifier ref changes out of band + +- **WHEN** a Caveat ref or expiry changes without an + admitted relation-version update +- **THEN** the content proof changes and prior reuse is rejected + +#### Scenario: Endpoint identity changes out of band + +- **WHEN** an endpoint's public identity changes while its eid remains stable +- **THEN** the content proof changes and prior authorization results are not + reused + +#### Scenario: Shared expression changes out of band + +- **WHEN** a named Caveat's expression reference or payload changes without the admitted schema-generation update +- **THEN** the selected schema content proof detects the change and rejects prior affected authorization reuse +- **AND** a cached program cannot hide that changed authoritative input + +### Requirement: Shared logical storage layer + +The core workspace SHALL provide backend-neutral pure functions for fixed-arity +v9 endpoint construction, decoding, logical-identity extraction, Caveat/context +qualifier validation, peer-half construction, exact retractions, expiry +validation, and prefix validation wherever all backends share behavior. +Backend-specific index, transaction, clock, schema, and history operations SHALL +remain inside adapters. + +#### Scenario: Equivalent relationship + +- **WHEN** all supported adapters encode the same relationship and qualifiers +- **THEN** their forward and reverse values have identical seven-component + order +- **AND** decode to the same endpoints, Caveat ref, context ref, and expiry + +#### Scenario: Permanent normalization + +- **WHEN** a relationship omits every qualifier +- **THEN** the shared codec emits three explicit trailing `nil` values rather + than a shorter value + +#### Scenario: Backend-specific database access + +- **WHEN** an adapter reads or writes endpoint values +- **THEN** it uses its native APIs without introducing backend dependencies + into the shared codec + +### Requirement: Cross-runtime validation + +The v9 endpoint-pair adapter SHALL satisfy the shared backend contract and each +backend's storage, pagination, consistency, mutation, cache-proof, cleanup, +integrity, temporal, and Caveat tests. DataScript SHALL satisfy the same logical +contract on JVM and ClojureScript runtimes. + +#### Scenario: JVM suite + +- **WHEN** the JVM backend tests run +- **THEN** direct and recursive authorization, lookup, count, relationship read/write, proof, deletion, and integrity behavior pass + +#### Scenario: JVM backend suites + +- **WHEN** Datomic Pro, Datahike, Datalevin, and DataScript JVM suites run +- **THEN** direct and recursive authorization, lookup, count, relationship + reads/writes, qualifier evaluation, cache proof, cleanup, collection, and + integrity behavior pass + +#### Scenario: ClojureScript suite + +- **WHEN** the compiled DataScript ClojureScript suite runs under Node +- **THEN** fixed seven-slot ordering, seeks, pagination, expiry, Caveat + evaluation, mutation, and corruption handling match JVM semantics + +#### Scenario: Single-source traversal benchmark + +- **WHEN** v9 direct checks, adjacency, arrows, lookup, and pagination are + benchmarked against v7 +- **THEN** every logical relation hop obtains candidates from one authoritative + endpoint stream +- **AND** total reads, integrity-proof work, candidates advanced, tuple bytes, + and active-Caveat overhead are measured without a hidden + v7 fallback + +### Requirement: Prerelease compatibility boundary + +The v9 adapter SHALL validate the specific seven-slot format identity, not arity or version number alone, and SHALL use only this endpoint-pair layout for +authorization, mutation, reads, cleanup, integrity, and proof generation. EACL +SHALL NOT provide an automatic v7-to-v9 relationship migration, dual-read +compatibility mode, mixed-format authorization path, or per-relation fallback. + +A v9 client SHALL refuse startup when v7 relationship datoms remain or when the +persisted relationship-storage version is incompatible. It SHALL provide typed +rebuild/reseed guidance. Legacy Datomic schema definitions MAY remain inert. Every current-basis +selection and mutation commit SHALL also validate a persisted semantic +capability epoch so a long-lived Phase 1 client cannot serve or write after +Phase 2 activation. Activation SHALL drain or fence old clients before serving +Caveated current views; a capability epoch read from a retained old immutable +pin is insufficient. Without an enforceable fence, activation SHALL require a +coordinated stop/upgrade/activate cutover. + +#### Scenario: Existing prerelease demo database + +- **WHEN** a DataScript database created from the old relationship-entity branch is opened with v9 +- **THEN** its old entities are not interpreted as active v9 Relationships +- **AND** startup rejects the incompatible populated store and directs the user to recreate the database or reload Relationships through the v9 API + +#### Scenario: Populated v7 database + +- **WHEN** a v9 client opens a database containing any v7 forward or reverse + relationship datom +- **THEN** startup fails with `:eacl/storage-version` +- **AND** no authorization request interprets the old data +- **AND** the error directs the operator to rebuild or reseed Relationships + +#### Scenario: Fresh or rebuilt database + +- **WHEN** a database contains no old relationship datoms and has the required + v9 schema +- **THEN** EACL stamps or validates relationship storage version 9 +- **AND** every subsequently admitted relationship uses only v9 values + +#### Scenario: Mixed relationship data + +- **WHEN** both v7 and v9 relationship datoms are detected +- **THEN** EACL fails closed at startup rather than merging the graphs + +#### Scenario: Live Phase 1 client during Phase 2 activation + +- **WHEN** another writer atomically enables the Phase 2 Caveat capability epoch +- **THEN** a Phase 1 client fails its next current-basis selection or mutation +- **AND** it cannot treat a Caveated relationship as absent + +#### Scenario: Old cache or cursor + +- **WHEN** a cache artifact or cursor was produced for the old relationship ABI +- **THEN** v9 rejects it through the cache, engine, adapter, or token ABI + boundary + +## ADDED Requirements + +### Requirement: One logical relationship regardless of qualifiers + +Logical relationship identity SHALL consist of the endpoint owner eid plus the +first four endpoint components. Conflict keys and batch deduplication MUST NOT +omit the owner or the existing wildcard/subject-relation identity, and SHALL +resolve endpoint aliases before deduplication. Caveat definition, Caveat context, and `valid-until` +SHALL NOT create distinct relationships. At most one stored relationship SHALL +exist for a logical identity. + +This rule SHALL match SpiceDB behavior: relationships differing only in Caveat, +Caveat context, or expiration cannot coexist. + +#### Scenario: Different Caveat is not a second relationship + +- **WHEN** a relationship already exists and a caller attempts `:create` with + the same logical identity and a different Caveat +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Different context is not a second relationship + +- **WHEN** a relationship already exists and a caller attempts `:create` with + different relationship-bound context +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Different expiry is not a second relationship + +- **WHEN** a relationship already exists and a caller attempts `:create` with + different expiry +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Out-of-band variants coexist + +- **WHEN** multiple complete endpoint values share the same owner and first four components +- **THEN** integrity reports a logical-identity collision +- **AND** an affected operation fails with `:eacl/invalid-relationship-state` +- **AND** no qualifier precedence rule silently selects one variant + +### Requirement: Candidate-stream work is explicitly bounded + +One endpoint seek SHALL mean one ordered candidate source, not constant work or +one total database read. Effective reads, counts, and authorization SHALL charge +every advanced inactive/corrupt candidate, lookahead item, validation read, +payload byte, and evaluator step to existing request limits. Exhaustion SHALL be +a typed failure and MUST NOT be returned as a successful empty/truncated result. + +#### Scenario: Clustered inactive prefix + +- **GIVEN** an endpoint has a long prefix of expired relationships +- **WHEN** an effective page seeks the first 20 active results +- **THEN** every inactive candidate advanced is charged to the request +- **AND** reaching the limit fails without asserting that the stream ended + +#### Scenario: Integrity validation costs reads + +- **WHEN** an unknown-writer source lacks a reusable basis integrity certificate +- **THEN** pair and auxiliary validation may perform reads beyond the one candidate stream +- **AND** telemetry reports those reads separately + +### Requirement: Managed source trust does not mandate duplicate endpoint reads + +EACL SHALL rely on its admitted atomic writer and source-certification contracts for the application invariants they establish. It SHALL NOT require re-reading the opposite endpoint for every candidate solely to recheck storage-engine atomicity. Unknown-writer sources SHALL separately establish required pair/context structure and complete content proofs. An actually encountered authoritative fault SHALL still abort the affected operation. + +#### Scenario: Managed qualified source + +- **WHEN** a source satisfies the managed writer and integrity certification contracts +- **THEN** a traversal can use its local tuple for expiry without an extra peer read solely to obtain or reconfirm that deadline + +#### Scenario: Unknown writer + +- **WHEN** a source cannot establish the required structural guarantees +- **THEN** it performs the necessary scope validation or fails qualification +- **AND** it does not claim a matching digest alone proves pair integrity + +### Requirement: Expiration does not require derived storage + +A finite deadline SHALL NOT require an additional expiration-index datom or a schedule entity. Optional collection SHALL use bounded authoritative forward scans in this release. Both endpoint tuples SHALL independently provide the same expiry value. Any future derived index SHALL require separate qualification and SHALL NOT change this endpoint ABI merely to add maintenance ordering. + +#### Scenario: Finite expiry without collection index + +- **WHEN** a Relationship has a finite deadline and no auxiliary expiration index exists +- **THEN** forward and reverse authorization still enforce expiry directly +- **AND** optional collection can discover it through a bounded forward scan + +#### Scenario: Discarded v9 format + +- **WHEN** a store or artifact uses the withdrawn eight-slot layout or an experimental seven-slot layout with different meanings +- **THEN** v9 rejects it as incompatible even if a numeric version marker says 9 diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/cursor-dependency-validity/spec.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/cursor-dependency-validity/spec.md new file mode 100644 index 00000000..51fdb9ee --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/cursor-dependency-validity/spec.md @@ -0,0 +1,133 @@ +## ADDED Requirements + +### Requirement: Expiry-aware continuations bind mode and deadline + +Every continuation affected by expiry SHALL authenticate its temporal mode, captured evaluation time, and its own exclusive reuse deadline. An explicit EACL snapshot SHALL produce a pinned continuation; a client-targeted query SHALL produce a live continuation unless it explicitly chooses a pinned snapshot. Token expiration and history retention SHALL remain separate checks. + +Pinned resumption SHALL preserve the exact database basis and evaluation time. Live resumption SHALL capture one fresh trusted time and require it to be at or after the certificate start and before any finite deadline, along with ordinary dependency validation. Leaving the certified range SHALL return a typed restart requirement and no resumed page. It SHALL NOT silently reuse the previous position. + +The certificate SHALL cover skipped candidates, subtracting evidence, frontier, lookahead, and residual state as well as emitted results. Incomplete proof SHALL prevent cross-time continuation. Changes to request context or temporal mode SHALL require a separately scoped query rather than reinterpret the cursor. + +#### Scenario: Resume before expiry + +- **WHEN** a live cursor resumes within its certified range with equal complete dependencies +- **THEN** its retained state can be reused at the new selected time + +#### Scenario: Resume at expiry + +- **WHEN** a live cursor resumes exactly at its exclusive deadline +- **THEN** EACL returns a typed restart requirement and no page + +#### Scenario: Pinned snapshot after wall-clock expiry + +- **WHEN** a cursor was pinned at evaluation time 90 with a deadline of 100 and wall time reaches 110 +- **THEN** its time certificate remains valid at 90 +- **AND** exact basis availability, token lifetime, and the other proof checks still apply + +#### Scenario: Unrelated churn + +- **WHEN** unrelated transactions occur while live time and all dependency certificates remain valid +- **THEN** that churn alone does not invalidate continuation + +#### Scenario: Expired ban authorizes an earlier identity + +- **GIVEN** a lookup skipped identity 10 because banned was active and emitted through identity 20 +- **WHEN** that ban expires before live resumption +- **THEN** the cursor cannot resume after identity 20 and silently omit newly authorized identity 10 +- **AND** its complete expiry proof forces a restart at that boundary + +#### Scenario: Earlier frozen time + +- **WHEN** a caller attempts to reuse live state at a time before its certificate start +- **THEN** EACL rejects the reuse rather than reviving state under an unproved temporal view + +### Requirement: Caveat request context is authenticated continuation scope + +A Caveat-aware cursor SHALL authenticate the complete canonical request context, +the Caveat compatibility profile/evaluator identity (including the pinned +cel-parser build, EACL adapter/plan format and partial/error semantics, semantic options, and extension identities for JVM execution), +and the conditional result +semantics needed to reproduce its page. Context supplied on resumption SHALL +match the authenticated scope or cause a typed mismatch or restart. + +Relationship-bound context remains database dependency data: the cursor proof +SHALL cover each relevant context eid/payload, named Caveat definition, and its referenced canonical expression content through the +selected snapshot proof. A resident program or matching expression eid SHALL NOT +substitute for this proof. Programs themselves SHALL NOT be serialized in cursors. + +#### Scenario: Request context changes between pages + +- **WHEN** a caller resumes a cursor with different Caveat request context +- **THEN** EACL rejects the continuation or starts a separately scoped query +- **AND** does not reuse the prior traversal frontier + +#### Scenario: Bound context changes between pages + +- **WHEN** `:touch` replaces a relationship's Caveat context before resumption +- **THEN** relation and content proof validation prevent old continuation reuse + +#### Scenario: Caveat definition changes + +- **WHEN** schema replacement changes a referenced Caveat before resumption +- **THEN** schema validation rejects or recovers the continuation according to + policy + +#### Scenario: Evaluator identity changes + +- **WHEN** the Caveat evaluator compatibility fingerprint changes +- **THEN** old Caveat-aware cursors are incompatible + +### Requirement: Conditional lookup state is stable across pages + +Caveat-aware lookup cursors SHALL preserve whether each returned membership is +definite or conditional and SHALL authenticate retained missing-context or +residual-condition data. A continuation MUST NOT turn a conditional candidate +into a definite grant or omit a definite candidate because condition state was +lost. + +#### Scenario: Conditional candidate crosses page boundary + +- **WHEN** a conditional lookup candidate is the last item on one page or first + item on the next +- **THEN** its permissionship and missing-context fields remain reproducible + +#### Scenario: Definite and conditional candidates share an object id + +- **WHEN** multiple permission paths reach one object with different condition + states +- **THEN** pagination applies the permission algebra before emitting one + correctly classified result +- **AND** does not emit duplicate object identities + +#### Scenario: Boolean compatibility lookup + +- **WHEN** a compatibility lookup omits conditional results +- **THEN** its cursor scope records that result policy +- **AND** it cannot resume through a Caveat-aware result policy + +### Requirement: v9 cursors commit to fixed seven-slot relationship ordering + +Relationship-read and authorization cursors SHALL identify the v9 endpoint ABI +and the ordering boundary derived from endpoint component four. Trailing Caveat, +context, and expiry qualifiers SHALL be authenticated where needed for exact +stored-relationship enumeration, while effective authorization pagination +remains ordered by the opposite endpoint identity. + +#### Scenario: Old cursor reaches v9 + +- **WHEN** a cursor was minted for a four-slot, discarded eight-slot, or differently defined experimental + seven-slot relationship ABI +- **THEN** the v9 client rejects it as incompatible + +#### Scenario: Qualifiers change at the boundary relationship + +- **WHEN** `:touch` changes qualifiers on a relationship used as a stored-read + continuation boundary +- **THEN** dependency and cursor validation prevent the old physical boundary + from being mistaken for its replacement + +#### Scenario: Full-arity vector bound + +- **WHEN** DataScript resumes a stored relationship scan +- **THEN** its comparator boundary uses the complete seven-element vector shape +- **AND** qualifier values cannot shift the endpoint ordering contract diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/dependency-validated-authorization-cache/spec.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/dependency-validated-authorization-cache/spec.md new file mode 100644 index 00000000..8aa0546f --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/dependency-validated-authorization-cache/spec.md @@ -0,0 +1,293 @@ +## ADDED Requirements + +### Requirement: Expiry cache reuse requires a checked temporal certificate + +Every reusable completed answer, denotation, projection, residual, count, page, or continuation that can change with Relationship expiry SHALL carry its computation evaluation time and a conservative exclusive reuse deadline. Nil deadline SHALL mean no future expiry can change that artifact under unchanged non-time dependencies. Reuse SHALL require selected time at or after the certificate's start and strictly before a finite deadline, in addition to all existing complete database, source, schema, identity, and semantic proof checks. + +The certificate SHALL be checked on every accepted hit. Provider TTL, asynchronous eviction, unchanged database basis, and Relation-version equality SHALL NOT replace that check. Exact-time identity or no publication SHALL be the fallback when no complete expiry proof is available. Cache lifting across database revisions SHALL retain its existing causal/source constraints independently of time. + +#### Scenario: Grant expires on one database basis + +- **WHEN** a cached grant's necessary Relationship expires without a database write +- **THEN** the grant is a miss at the exclusive deadline despite equal Relation versions + +#### Scenario: Cached denial becomes a grant + +- **GIVEN** read is viewer minus banned, viewer is permanent, and banned expires at 100 +- **WHEN** a denial computed at 90 is requested at 100 on the same basis +- **THEN** it cannot be reused under a certificate extending past that ban expiry +- **AND** fresh evaluation can return has-permission + +#### Scenario: Dependency changes before expiry + +- **WHEN** selected time is inside the certificate but a relevant database dependency changes +- **THEN** the ordinary proof rejects reuse + +#### Scenario: Earlier explicit snapshot + +- **WHEN** a frozen snapshot's evaluation time precedes the certificate start +- **THEN** forward-time reuse rejects it even if database proofs match + +#### Scenario: Incomplete proof + +- **WHEN** the engine cannot establish a complete temporal certificate +- **THEN** it uses exact-time identity or evaluates without reusable publication +- **AND** does not encode unknown expiry as an unbounded deadline + +#### Scenario: Delayed eviction + +- **WHEN** a provider still returns an authenticated entry after its reuse deadline +- **THEN** the client rejects it before returning an authorization result + +### Requirement: Caveat cache identity is context-complete + +For every cache entry or subproblem influenced by a possible Caveat path, +lookup scope, dependency certificates, and authenticated value data together +SHALL distinguish: + +- the complete canonical request context admitted for the operation; +- each relevant named Caveat definition, its expression reference and complete canonical expression payload, and schema generation; +- each relationship Caveat context eid and authoritative payload; +- the Caveat compatibility profile and evaluator fingerprint, including the + pinned JVM cel-parser build, EACL adapter/plan format and partial/error semantics, answer-affecting options, + enabled declarations/macros/functions, and extension implementation identities; +- permissionship, residual condition, and missing-context result data; and +- the ordinary complete relation dependency proof. + +The lookup key SHALL be computable before evaluation. Permissionship, residual +condition, and missing fields SHALL be authenticated value data, not inputs +that require knowing the answer before a lookup. Definition and bound-context +content, including referenced expression payloads, SHALL be dependency-certificate data. + +A relation-version proof alone SHALL NOT authorize reuse after context payload, +Caveat definition/expression, request context, or evaluator semantics change. + +#### Scenario: Same graph and different request context + +- **WHEN** two requests select the same database and evaluation time but differ in a + context value visible to a possible Caveat path +- **THEN** their semantic cache identities differ + +#### Scenario: Bound context overrides request context + +- **WHEN** relationship-bound context fixes a value also present in request + context +- **THEN** lookup identity includes the admitted request context and dependency + validation covers the authoritative bound values +- **AND** a caller-supplied replacement cannot alias the bound result + +#### Scenario: Caveat definition changes + +- **WHEN** schema replacement changes a referenced Caveat definition +- **THEN** the changed schema generation rejects entries computed under the old + definition + +#### Scenario: Caveat context changes out of band + +- **WHEN** a context payload changes while its eid and relation version remain + unchanged +- **THEN** database-visible proof validation rejects the prior entry + +#### Scenario: CEL configuration changes without a database write + +- **WHEN** a deployment changes cel-parser build, EACL numeric/partial/error handling, or a custom extension implementation while the database is unchanged +- **THEN** the changed evaluator identity prevents reuse of incompatible compiled programs and authorization results + +#### Scenario: Evaluator profile changes + +- **WHEN** the Caveat compatibility profile or evaluator fingerprint changes +- **THEN** entries produced by the old evaluator are cache misses + +#### Scenario: Expression payload changes out of band + +- **WHEN** a referenced expression payload changes while its eid, claimed content key, and Relation versions remain unchanged +- **THEN** selected-view schema content validation rejects prior answer reuse and any stale eid-to-expression resolution +- **AND** a compiled program does not substitute for authoritative content validation + +### Requirement: Program caches reuse expression structure independently of results + +Each client SHALL maintain a bounded, rebuildable local program cache for lazily evaluated Caveat expressions. Its key SHALL distinguish the complete canonical expression payload and evaluator fingerprint, including library/build, adapter/plan format, types/profile, options, numeric/partial/error behavior, and extensions. Digest lookup SHALL verify canonical payload equality. The cache SHALL NOT be keyed solely by Caveat name, expression eid, CEL source alone, or schema generation. + +A fast eid-to-content mapping SHALL be scoped and validated against the selected database source/lifecycle and schema view, including speculative branches. Identical canonical content MAY share a program across validated views; coincident eids SHALL NOT establish equivalence. Program entries SHALL retain executable structure only, with no request/bound context, residual, permissionship, or temporal authorization certificate. Relationship expiry and changed bindings SHALL NOT by themselves require rebuilding an unchanged program, and a program hit SHALL NOT authorize answer reuse. + +The client SHALL establish canonical content identity and collision checks when admitting or refreshing the validated mapping. Repeated traversal within that qualified scope SHALL reuse a cached identity/handle without canonicalizing, hashing, or comparing the complete expression source per Relationship. Selected-view proof rules SHALL still govern mapping validity. + +Concurrent misses for one key SHALL share one in-flight construction. Completed entries and in-flight work SHALL be bounded; successful immutable programs alone SHALL be published. Failed builds SHALL release their in-flight entries and return typed failures to affected callers without poisoning later retries. Eviction SHALL permit equivalent reconstruction. Per-caller cancellation SHALL NOT corrupt a program or another caller's shared construction. Runtime-specific programs SHALL NOT be serialized into database entities, portable cache snapshots, or cursors. + +#### Scenario: Different names share one resident program + +- **WHEN** two named Caveats resolve to equal canonical expression content under the same evaluator fingerprint +- **THEN** evaluations in one client reuse the resident program +- **AND** their schema branch permissions and authorization dependencies remain independently validated + +#### Scenario: Context changes while the program stays resident + +- **WHEN** the same expression is evaluated with different bound or request values +- **THEN** the program is reused with fresh bindings +- **AND** no previous evaluation result is returned solely because the program key matches + +#### Scenario: Concurrent cold requests + +- **WHEN** concurrent traversals miss the same program key +- **THEN** one construction serves those callers within the configured build limits +- **AND** each caller evaluates using its own context after construction + +#### Scenario: Eviction or failed construction + +- **WHEN** a program is evicted or construction fails +- **THEN** a later eligible traversal can rebuild it from validated authoritative expression content +- **AND** neither a partial program nor a fabricated denial is retained + +#### Scenario: Eid collision across database views + +- **WHEN** two sources or divergent speculative views use the same expression eid for different content +- **THEN** their validated resolution and program keys distinguish that content + +#### Scenario: Unrelated schema change + +- **WHEN** schema generation changes but a referenced expression and evaluator fingerprint remain identical +- **THEN** the client may retain its program +- **AND** answer, traversal-plan, and cursor reuse still follow the ordinary schema and dependency proof rules + +#### Scenario: Expression expires on a Relationship + +- **WHEN** a Relationship carrying an expression reaches its expiry deadline +- **THEN** authorization time certificates reject incompatible results and traversal omits that edge +- **AND** the pure program may remain resident for other active Relationships or retained snapshots + +#### Scenario: Warm catalog and program + +- **WHEN** the selected schema catalog already validates the definition-to-expression mapping and the program is resident +- **THEN** repeated evaluation of that expression needs no per-Relationship expression database read or compilation +- **AND** it reuses a qualified identity/handle without rehashing or comparing the complete source per edge +- **AND** required bound-context reads, ordinary proofs, fresh bindings, and evaluation work remain separately accounted for + +### Requirement: Conditional results cannot alias definite results + +The completed-answer and subproblem value formats SHALL distinguish +`:has-permission`, `:no-permission`, and `:conditional-permission`. +Conditional entries SHALL authenticate their residual condition and missing +context fields where retained. Cache lookup MUST NOT substitute one +permissionship state for another. + +#### Scenario: Conditional value is replayed as a grant + +- **WHEN** an external provider returns a validly shaped conditional value under + a key for a definite grant +- **THEN** complete key/value authentication rejects it + +#### Scenario: Missing field set changes + +- **WHEN** two conditional evaluations require different context fields +- **THEN** their retained result identities differ + +#### Scenario: Boolean can? uses a conditional cache entry + +- **WHEN** `can?` obtains a valid cached conditional permissionship +- **THEN** it returns false rather than treating the entry as a grant + +### Requirement: Caveat algebra caching preserves possible paths + +Caveat-aware cache proofs SHALL cover all positive, negative, and conditional +paths that can affect the result. Runtime short-circuiting MAY retain a smaller +witness only when the proof establishes that omitted paths cannot alter the +permissionship or residual condition. + +#### Scenario: Unconditional union witness + +- **WHEN** one union branch grants unconditionally and another is conditional +- **THEN** a cached has-permission result need not retain the irrelevant + conditional branch as a result dependency +- **AND** its ordinary static relationship dependency closure remains complete + +#### Scenario: Conditional union + +- **WHEN** every possible granting branch is false or conditional +- **THEN** a cached conditional result commits to all residual alternatives + needed to reproduce it + +#### Scenario: Conditional exclusion + +- **WHEN** a true positive branch is reduced by a conditional subtracting branch +- **THEN** the cached result remains conditional +- **AND** commits to the subtracting residual condition + +#### Scenario: Missing proof + +- **WHEN** EACL cannot prove that a cached residual condition is complete +- **THEN** it evaluates without reusable Caveat result publication + +### Requirement: Expiry and Caveat ABI changes invalidate portable caches + +The authorization ABI used by exact and managed keys, exported cache snapshots, +subproblem values, and restored entries SHALL change for the v9 relationship +representation and Phase 2 Caveat semantics. Restoring an artifact whose ABI +does not commit to seven-slot qualifiers, evaluation time, request context, and +conditional result semantics SHALL fail closed. + +#### Scenario: Restore old cache snapshot + +- **WHEN** a v9 client restores a cache snapshot produced by the old + relationship ABI +- **THEN** it rejects the snapshot before any entry influences authorization + +#### Scenario: Restore Phase 1 artifact in Phase 2 + +- **WHEN** Phase 2 enables Caveats and encounters a Phase 1 cache artifact whose + ABI cannot encode conditional semantics +- **THEN** it rejects or treats the artifact as a miss + +#### Scenario: Shared provider contains old entries + +- **WHEN** a shared provider returns an authenticated entry carrying an old + authorization ABI +- **THEN** the v9 client treats it as a miss + + +### Requirement: Expiry certificates belong to each reusable artifact + +Each certificate SHALL prove its own retained contents stable. Derivation SHALL cover all expiry-sensitive positive, negative, and conditional evidence required to establish those contents, including skipped candidates, lookahead, and retained frontier. The minimum future deadline over a complete relevant evidence set SHALL be a valid conservative bound; the deadline of returned positive edges alone SHALL NOT suffice. A stable final answer SHALL NOT widen an unstable child or continuation certificate. Static Relation dependency closure SHALL remain complete. + +Authoritative faults SHALL NOT be cached as denials. Expiry-only monotonicity optimizations SHALL require proof over the full relevant permission expression, including nested exclusion, recursion, and Caveat context; absence of a direct subtraction in the top-level expression SHALL NOT suffice. + +#### Scenario: Stable union with expiring child + +- **GIVEN** permanent owner makes owner plus viewer true while viewer expires at 100 +- **WHEN** the engine retains both the Boolean union answer and the viewer denotation at 90 +- **THEN** the answer can be unbounded only under its complete proof +- **AND** the viewer denotation's certificate ends no later than 100 + +#### Scenario: Conditional result loses missing fields + +- **WHEN** an expiring conditional branch can change a cached residual or missing-field set +- **THEN** that result's deadline covers the transition even if a coarser Boolean result stays unchanged + +#### Scenario: Ban before the cursor boundary + +- **WHEN** retained lookup state skipped an earlier identity due to a ban that can expire +- **THEN** its certificate covers that negative evidence rather than only already-emitted identities + +#### Scenario: Proven positive-only denial + +- **WHEN** the full relevant expression is proven positive-only and its Caveat inputs and ordinary dependencies are fixed +- **THEN** an implementation may prove that a denial remains a denial as edges expire +- **AND** that optimization cannot be generalized to exclusion-containing expressions without a separate proof + +### Requirement: Collection follows ordinary Relationship invalidation + +Actual collector deletion SHALL publish the affected Relation mutation identities in the same transaction as authoritative pair/context removal. Cache validation SHALL observe those changes through existing dependency rules even if effective authorization at or after the collection cutoff is unchanged. Merely scanning or observing that time passed SHALL NOT require a Relation mutation. An optional future derived-index-only repair SHALL NOT become authoritative Relationship content. + +#### Scenario: Expired stored Relationship is collected + +- **WHEN** collection retracts an expired pair in a cached dependency scope +- **THEN** its Relation mutation identity changes and prior dependency-equivalent reuse is rejected + +#### Scenario: Unrelated Relation is collected + +- **WHEN** collector deletions touch only Relations outside a request's complete dependency closure +- **THEN** those deletions alone do not invalidate that request's Relation proof + +#### Scenario: Expiry without deletion + +- **WHEN** only time advances beyond a Relationship deadline +- **THEN** temporal proof checks handle invalidation without manufacturing a database mutation diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/expiring-relationships/spec.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/expiring-relationships/spec.md new file mode 100644 index 00000000..edb3c63a --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/expiring-relationships/spec.md @@ -0,0 +1,219 @@ +## Purpose + +Define expiry-only Relationship behavior, trusted evaluation-time snapshots, consumer-owned future creation, and bounded optional collection without depending on a timely boundary transaction. + +## ADDED Requirements + +### Requirement: Relationships have one optional exclusive expiry + +Each stored Relationship SHALL have an optional `valid-until` represented identically in slot seven of both endpoint tuples. EACL SHALL normalize supplied values to exact UTC epoch-millisecond integers in the inclusive range `[-9007199254740991, 9007199254740991]`. Nil SHALL mean no expiration; no integer sentinel SHALL mean infinity. Non-integer, out-of-range, and precision-losing inputs SHALL fail with a typed invalid-expiry error before submission. + +A well-formed stored Relationship SHALL be expiry-active exactly when its bound is nil or the selected evaluation time is strictly less than the bound. Eligibility remains subject to Caveats and the selected permission expression. + +#### Scenario: Permanent Relationship + +- **WHEN** expiry is omitted +- **THEN** both tuples contain nil in slot seven and time passage alone does not expire the Relationship + +#### Scenario: Exact expiry boundary + +- **WHEN** evaluation time equals `valid-until` +- **THEN** the Relationship is inactive in both traversal directions + +#### Scenario: Last representable deadline + +- **WHEN** expiry is 9007199254740991 +- **THEN** it remains finite and is inactive at that exact evaluation time + +#### Scenario: Precision would be lost + +- **WHEN** normalization would round or overflow a supplied deadline +- **THEN** admission fails without writing endpoint or context data + +### Requirement: Future creation belongs to consumers + +EACL SHALL expose no scheduled activation through Relationship `valid-from`, transaction metadata, or schedule references in this v9 contract. Unsupported start/schedule qualifiers SHALL be rejected rather than ignored. A successfully stored Relationship SHALL be eligible as soon as a reader selects a database basis containing it, subject to expiry and Caveats. + +Consumers SHALL create future Relationships through ordinary writes when due. The contract SHALL permit delayed insertion and SHALL NOT silently extend an absolute deadline to compensate for that delay. Already-expired valid writes SHALL be admitted as stored but inactive Relationships, subject to normal identity conflicts. + +#### Scenario: Consumer creates when due + +- **WHEN** a consumer transacts a due Relationship and a reader selects the committed basis containing it +- **THEN** the Relationship can participate immediately if unexpired and its Caveat permits +- **AND** the write invalidates affected Relation dependencies normally + +#### Scenario: Delayed creation misses its expiry + +- **WHEN** a delayed write creates a Relationship whose supplied deadline has passed +- **THEN** the stored deadline is unchanged and the Relationship is inactive + +#### Scenario: Start bound is supplied + +- **WHEN** a write supplies `valid-from` or a schedule qualifier +- **THEN** it fails with a typed unsupported-qualifier error +- **AND** it does not silently create an immediately active Relationship + +### Requirement: Expiry needs no boundary write + +Expiration SHALL be determined from the authoritative tuple and selected time without a timer, scheduler, transaction, listener notification, cache-eviction callback, or collection pass. Passing time SHALL NOT bump `:eacl/relation-version`. Time-dependent reusable results SHALL instead satisfy the expiry-certificate contract. + +#### Scenario: No writes at the deadline + +- **WHEN** time reaches expiry while the database basis and Relation versions stay unchanged +- **THEN** a fresh client-targeted authorization excludes the expired Relationship + +#### Scenario: Collector is disabled + +- **WHEN** expired tuples remain indefinitely because no collector runs +- **THEN** expiration remains correct for checks, lookups, counts, and every permission operator + +### Requirement: Expiry applies to positive and subtracting evidence + +Expiry SHALL be checked before an edge contributes to union, intersection, exclusion, arrow traversal, recursion, lookup, count, or explanation. An inactive Relationship SHALL contribute no positive, negative, or conditional evidence. Expiry SHALL be checked before bound-context loading and Caveat evaluation for that edge, after required local shape validation. + +#### Scenario: Positive witness expires + +- **WHEN** the only granting witness expires +- **THEN** the permission changes from has-permission to no-permission at the boundary + +#### Scenario: Ban expires + +- **GIVEN** `read = viewer - banned`, permanent viewer, and banned expiring at 100 +- **WHEN** evaluation moves from 90 to 100 on one database basis +- **THEN** read changes from no-permission to has-permission without a write + +#### Scenario: Intermediate arrow expires + +- **WHEN** an intermediate Relationship on an arrow path expires +- **THEN** neither forward nor reverse traversal follows that edge + +#### Scenario: Expired Caveated Relationship + +- **WHEN** a well-formed Relationship is expired +- **THEN** its Caveat does not require context loading or evaluation for that edge + +### Requirement: Evaluation time is trusted and captured once + +Each top-level authorization view SHALL pair one immutable database basis with one trusted exact evaluation time in the portable range. Client-targeted operations SHALL capture a fresh time even when reusing a database pin. All subproblems and batch items SHALL use that time. Explicit EACL snapshots SHALL freeze it; speculative derivation SHALL preserve it. + +Invalid clock values and violations of the configured nondecreasing client-clock contract SHALL cause typed clock errors. Documentation SHALL state the supported multi-peer clock/skew model. EACL MUST NOT expire individual negative edges early as a generic safe-skew rule. + +#### Scenario: Clock crosses expiry during a check + +- **WHEN** wall time crosses a deadline during one check +- **THEN** all of its subproblems still use the single captured time + +#### Scenario: Batch crosses expiry + +- **WHEN** a batch spans a wall-clock expiry boundary +- **THEN** all batch results use the same selected basis and time + +#### Scenario: Test clock + +- **WHEN** a deterministic configured clock moves to the exact deadline +- **THEN** the boundary can be tested without sleeps or boundary transactions + +#### Scenario: Invalid or regressing clock + +- **WHEN** the clock returns an invalid value or regresses contrary to its client contract +- **THEN** current snapshot acquisition fails without falling back to an unqualified graph + +#### Scenario: Early expiry would remove a ban + +- **WHEN** a proposed clock-uncertainty adjustment would expire subtracting evidence before its deadline +- **THEN** it cannot grant under a per-edge early-expiry rule +- **AND** any supported uncertainty policy must prove whole-permission invariance or return a typed failure + +### Requirement: Relationship reads distinguish stored and expiry-active state + +Stored Relationship reads SHALL expose normalized expiry and an explicit `:active` or `:expired` status at the selected evaluation time. Expiry-active reads SHALL omit expired tuples. These statuses SHALL describe expiry alone, not Caveat truth or final authorization. No `:scheduled` status SHALL exist in this contract. + +Expired assertions SHALL retain identity, schema in-use, mutation, audit, and integrity significance until deleted. API documentation SHALL distinguish Relationship reads from permission lookups. + +#### Scenario: Read retained expired data + +- **WHEN** a stored read selects an expired Relationship +- **THEN** it returns its deadline and expired status +- **AND** an expiry-active read omits it + +#### Scenario: Active does not mean authorized + +- **WHEN** a Relationship is unexpired but its Caveat is false or conditional +- **THEN** its Relationship-read status remains active +- **AND** permission lookup applies the Caveat and complete permission expression separately + +### Requirement: Renewal replaces qualifiers atomically + +Expiry SHALL NOT alter logical Relationship identity. `:create` SHALL conflict with any stored assertion of the identity, including expired data. `:touch` SHALL create when absent or atomically replace its qualifiers when present. `:delete` SHALL require no expiry or Caveat values. Different identities with different deadlines SHALL be batchable together. + +Each actual renewal SHALL update both endpoint tuples and the affected Relation mutation identity in the same admitted transaction. No committed absence gap or original-transaction-metadata mutation SHALL be required. + +#### Scenario: Create after expiry before collection + +- **WHEN** create targets a retained expired identity +- **THEN** it fails with `:eacl/relationship-conflict` +- **AND** touch can renew the identity with an explicit new deadline + +#### Scenario: Create after collection + +- **WHEN** collection has deleted the identity and no concurrent writer recreates it +- **THEN** a guarded create can succeed + +#### Scenario: Renewal with unchanged bound context + +- **WHEN** touch changes only expiry +- **THEN** it replaces both deadline values atomically and can retain the same immutable context ref + +#### Scenario: Different deadlines in one batch + +- **WHEN** a batch updates distinct identities with different deadlines +- **THEN** each retains its own deadline independently of shared transaction metadata + +### Requirement: Collection is optional bounded maintenance + +EACL SHALL provide an explicitly invoked, bounded collection operation over authoritative forward Relationships without requiring a per-Relationship expiration index. It SHALL select finite expiries at or before an admitted retention cutoff no later than the trusted collection time. It SHALL report progress and work limits, counting scanned candidates independently of deletions. Authorization correctness SHALL be independent of whether or when it runs. + +Each actual deletion SHALL revalidate the current exact endpoint pair and owned context at commit, then retract them and advance `:eacl/relation-version` for every affected Relation once in the same transaction. Scanning and no-op batches SHALL NOT require a Relation bump. A stale collector plan SHALL never delete a renewed or replaced Relationship. + +#### Scenario: Bounded scan finds no expired Relationships + +- **WHEN** the collector exhausts its scan budget without finding an eligible expiry +- **THEN** it reports bounded progress without claiming all stored data was examined +- **AND** it does not mutate Relation versions solely for scanning + +#### Scenario: Collection races renewal + +- **WHEN** touch replaces an expired candidate before its collection transaction commits +- **THEN** current-state guards prevent deleting the replacement +- **AND** collection retries or reports/skips the race explicitly + +#### Scenario: Collection deletes several Relationships of one Relation + +- **WHEN** one admitted batch deletes several eligible pairs belonging to one Relation +- **THEN** both halves and owned context are removed atomically +- **AND** that Relation advances once for the transaction + +#### Scenario: Concurrent rows appear behind the scan cursor + +- **WHEN** another writer adds eligible data behind a collector cursor on a frozen scan basis +- **THEN** pass completion is scoped to that basis and a later pass can collect the new data + +#### Scenario: Future cutoff + +- **WHEN** a requested cutoff exceeds trusted collection time +- **THEN** collection rejects it rather than deleting still-active Relationships + +### Requirement: Collection and historical reconstruction have separate contracts + +Collection SHALL remove current stored assertions under the selected retention policy and SHALL NOT promise database excision or durable byte reclamation. Historical evaluation SHALL require an appropriate retained database basis with its authoritative schema/context and explicit EACL snapshot time. An earlier time alone on a post-collection basis SHALL NOT recreate deleted data. + +#### Scenario: Current basis after collection + +- **WHEN** collection deletes expired data +- **THEN** stored reads and identity conflicts reflect its removal +- **AND** earlier-time reconstruction is not inferred from the current basis alone + +#### Scenario: Retained historical snapshot + +- **WHEN** the backend still supports an older EACL snapshot containing the assertion and its context/schema +- **THEN** it can evaluate using its fixed basis and time subject to documented retention and token limits diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/public-authorization/spec.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/public-authorization/spec.md new file mode 100644 index 00000000..91480837 --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/public-authorization/spec.md @@ -0,0 +1,231 @@ +## ADDED Requirements + +### Requirement: Caveat-aware permission checks accept bounded request context + +EACL SHALL expose a Caveat-aware permission-check operation that accepts an +optional canonical request-context map in addition to the subject, permission, +resource, consistency, and execution controls. Request context SHALL be part of +the selected authorization request and SHALL remain fixed for every subproblem +in that operation. + +The operation SHALL reject malformed, non-canonicalizable, or structurally +over-limit context before cache-key construction. Caveat-specific type validation +SHALL reject invalid effective parameter values after bound-context precedence +is applied, before evaluator execution or successful cache publication. + +#### Scenario: Check without request context + +- **WHEN** a caller checks a permission without request context +- **THEN** EACL evaluates uncaveated paths normally +- **AND** evaluates Caveated paths using only relationship-bound values + +#### Scenario: Check with request context + +- **WHEN** a caller supplies valid request context +- **THEN** every Caveat path in that check observes the same canonical request + context + +#### Scenario: Context changes during execution + +- **WHEN** caller-owned mutable input changes after request admission +- **THEN** the in-flight check continues using the captured canonical context +- **AND** authorization does not observe the mutation + +#### Scenario: Invalid request context + +- **WHEN** request context violates structural depth, entry-count, collection, or + encoded-size limits, or an effective parameter has an invalid Caveat type +- **THEN** EACL rejects the request with a typed context error +- **AND** no fallback uncontextualized check runs + +### Requirement: Permission checks expose three-state permissionship + +A successful Caveat-aware check operation SHALL return exactly one permissionship +state. Typed evaluation, execution-limit, clock, and authoritative integrity +failures are errors, not a fourth permissionship state: + +- `:has-permission`; +- `:no-permission`; or +- `:conditional-permission`. + +A conditional response SHALL include the canonical set of missing context field +names needed to determine the result. It MAY include bounded diagnostic +information or a residual condition, but that information MUST NOT be required +to safely interpret permissionship. + +#### Scenario: Definite grant + +- **WHEN** the graph and Caveats determine that access exists +- **THEN** the result is `:has-permission` +- **AND** missing context is empty + +#### Scenario: Definite denial + +- **WHEN** the graph and Caveats determine that access does not exist +- **THEN** the result is `:no-permission` +- **AND** missing context is empty + +#### Scenario: Missing context can change the result + +- **WHEN** one or more missing Caveat parameters can still change the final + authorization result +- **THEN** the result is `:conditional-permission` +- **AND** the response names the missing fields + +#### Scenario: Missing context is irrelevant + +- **WHEN** an unconditional path determines access despite another conditional + path +- **THEN** the result is `:has-permission` +- **AND** irrelevant missing fields are not reported as making the result + conditional + +### Requirement: Boolean can? remains fail-closed + +The existing Boolean `can?` convenience operation SHALL return true only when +the Caveat-aware permissionship is `:has-permission`. It SHALL return false for +`:no-permission` and `:conditional-permission`. + +`can?` SHALL propagate typed execution or integrity failures; it MUST NOT +convert a failed subproblem into false membership inside the permission graph. + +A caller requiring missing-context diagnostics MUST use the Caveat-aware result +operation rather than infer them from `can?`. + +#### Scenario: Conditional result through can? + +- **WHEN** the Caveat-aware result is `:conditional-permission` +- **THEN** `can?` returns false + +#### Scenario: Definite result through can? + +- **WHEN** the Caveat-aware result is `:has-permission` +- **THEN** `can?` returns true +- **WHEN** the result is `:no-permission` +- **THEN** `can?` returns false + +### Requirement: Caveat-aware lookup preserves result state + +Authorization lookups that can traverse Caveated relationships SHALL accept the +same bounded request context and SHALL distinguish definitely authorized from +conditionally authorized subjects or resources. A result reachable only under +a residual Caveat MUST NOT be presented as definitely authorized. + +Compatibility lookups and numeric counts SHALL include only definitely authorized +identities. Caveat-aware lookups SHALL mark each definite or conditional result; +Caveat-aware counts SHALL report separate definite and conditional identity +counts. Identity deduplication and permission algebra SHALL precede either count. +No surface SHALL silently collapse conditional results into grants. + +#### Scenario: Definite lookup result + +- **WHEN** a resource is reachable through a definite authorization path +- **THEN** lookup marks it `:has-permission` + +#### Scenario: Conditional lookup result + +- **WHEN** a resource is reachable only if missing Caveat context satisfies a + residual condition +- **THEN** lookup marks it `:conditional-permission` +- **AND** includes the missing field names applicable to that result + +#### Scenario: Boolean-only lookup compatibility + +- **WHEN** an existing compatibility lookup returns only definitely authorized + resources +- **THEN** it omits conditional results +- **AND** documentation directs callers to the Caveat-aware lookup for + conditional candidates + +### Requirement: Snapshot and speculative boundaries capture Caveat request state + +An EACL snapshot SHALL continue to represent database basis, schema, and +evaluation time, while Caveat request context SHALL belong to one authorization +request rather than become persistent snapshot state. `eacl/with` and +`eacl/with-schema` SHALL not silently copy caller request context into stored +relationships. + +Relationship-bound context SHALL enter only through an explicit relationship +write/update qualifier and SHALL be persisted separately from request context. + +#### Scenario: Reuse one snapshot with different request context + +- **WHEN** a caller checks the same immutable EACL snapshot with two different + request-context maps +- **THEN** each check has a distinct semantic request identity +- **AND** the snapshot's database basis and evaluation time remain unchanged + +#### Scenario: Prospective relationship with bound context + +- **WHEN** a caller uses `eacl/tx-relationship` and `eacl/with` to add a + Caveated relationship with bound context +- **THEN** the speculative snapshot contains the persisted qualifier state +- **AND** later permission checks may add separate request context + +#### Scenario: Request context is not persisted + +- **WHEN** a permission check supplies request context +- **THEN** EACL does not transact or attach it to any relationship + + +### Requirement: Effective result limits do not hide scan exhaustion + +Effective lookup and count SHALL charge inactive candidates and validation work +to request limits. A work-limit failure SHALL NOT be returned as a successful +empty page, end-of-stream, exact count, or ordinary denial. Temporal restarts +SHALL be explicit errors and SHALL emit no resumed page under the old cursor. + +#### Scenario: Active result follows a large inactive run + +- **WHEN** scanning reaches its work limit before finding or excluding further active results +- **THEN** the operation returns a typed limit error +- **AND** no successful exhaustion marker or continuation is published + +#### Scenario: Temporal restart required + +- **WHEN** live resumption crosses the retained state's certified interval +- **THEN** the caller receives a typed restart requirement +- **AND** can start a fresh query that includes earlier identities newly authorized by expiry of subtracting evidence + +### Requirement: Public authorization captures database and expiry time together + +Each top-level client-targeted check, lookup, count, explanation, relationship read, or batch SHALL use one selected immutable database basis and one fresh trusted evaluation time. Reusing a minimize-latency database pin SHALL NOT reuse the preceding request's clock sample. An explicit EACL snapshot SHALL freeze its captured time as well as its database basis; speculative derivation SHALL preserve that time. Request context remains separate from snapshot state. + +Ordinary authorization SHALL NOT accept a caller-supplied native filtered database or a request-context field as an override of the trusted expiry clock. Selecting a database basis with a causal consistency token SHALL NOT by itself freeze evaluation time. + +#### Scenario: Same database pin after expiry + +- **WHEN** two client-targeted checks reuse one database basis on opposite sides of a Relationship deadline +- **THEN** each check uses its own captured time and observes the appropriate expiry state + +#### Scenario: Explicit snapshot after wall-clock expiry + +- **WHEN** an EACL snapshot captured before expiry is evaluated after wall-clock expiry +- **THEN** evaluation still uses its original basis and time +- **AND** documentation identifies it as a fixed historical view, subject to token and retention limits + +#### Scenario: Prospective update + +- **WHEN** an explicit snapshot is passed to `eacl/with` or `eacl/with-schema` +- **THEN** the derived snapshot retains the parent's evaluation time +- **AND** guarded Relationship helpers and schema validation retain committed/speculative parity + +#### Scenario: Caller supplies a false current time + +- **WHEN** request Caveat context contains a clock-like parameter +- **THEN** it affects only Caveats that declare that parameter +- **AND** it cannot revive an expired Relationship by overriding the trusted clock + +### Requirement: Conditional counts are not definite permissions + +Count results SHALL distinguish definitely authorized identities from identities that could be authorized if missing Caveat context were supplied. Compatibility numeric counts SHALL count the definite set only. An identity reached through both definite and conditional paths SHALL be classified once using the composed permission result. + +#### Scenario: Definite and conditional paths meet + +- **WHEN** one identity has a definite union witness and another conditional witness +- **THEN** Caveat-aware count adds one to the definite count and zero to the conditional count for that identity + +#### Scenario: Only conditional paths exist + +- **WHEN** one identity remains conditional after permission algebra +- **THEN** compatibility count excludes it and Caveat-aware count includes it only in the conditional count diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/relationship-caveats/spec.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/relationship-caveats/spec.md new file mode 100644 index 00000000..7900da2b --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/specs/relationship-caveats/spec.md @@ -0,0 +1,814 @@ +## Purpose + +Define Phase 2 SpiceDB-compatible Caveat behavior for EACL relationships, +including schema definitions, sparse relationship-bound context, request +context, conditional permission results, and deterministic evaluation. + +## ADDED Requirements + +### Requirement: JVM Caveats use the selected cel-parser implementation + +The JVM Caveat implementation SHALL use [exoscale/cel-parser](https://github.com/exoscale/cel-parser), coordinate `com.exoscale/cel-parser`, for CEL parsing and interpretation. EACL SHALL pin an exact version/build and a versioned admitted configuration/profile before Phase 2 activation. EACL SHALL retain responsibility for Caveat schema grammar, static name/type/profile validation, context precedence/conversion, partial-result semantics, permission algebra, expiry, bounded execution, and public result/error mapping. Parser acceptance SHALL NOT substitute for required schema validation. + +JVM/ANTLR program objects SHALL remain internal rebuildable runtime state. Authoritative Caveat definitions, expression payloads, typed values, and portable cache/cursor data SHALL use the canonical EACL representation. Selecting the library SHALL NOT imply complete SpiceDB compatibility, native partial evaluation, or CLJS execution, or replace their qualification gates. + +#### Scenario: JVM schema admission and evaluation + +- **WHEN** a supported Caveat is admitted and evaluated on the JVM +- **THEN** the EACL integration validates its CEL body under the declared parameter environment before commit and evaluates through the pinned cel-parser implementation +- **AND** EACL maps its result into the specified definite, conditional, or typed-error outcome + +#### Scenario: Repeated evaluation of an unchanged definition + +- **WHEN** an unchanged Caveat is evaluated repeatedly under the same complete compiler/runtime identity +- **THEN** its resident program is reused without parsing the source per Relationship +- **AND** rebuilding a missing program from the authoritative definition yields the same behavior + +#### Scenario: Dependency or configuration changes + +- **WHEN** the cel-parser build, adapter semantics, enabled options, declared macros/functions, or extension implementation changes +- **THEN** the evaluator identity changes and incompatible compiled/cache/cursor state is not reused + +#### Scenario: Portable export + +- **WHEN** Caveat definitions or reusable state cross process/runtime boundaries +- **THEN** the portable representation contains no serialized JVM/ANTLR program object dependency + +#### Scenario: Error returned as a value + +- **WHEN** the selected library returns an error value rather than throwing +- **THEN** the adapter reports the specified typed operation failure +- **AND** Clojure truthiness does not turn that value into a grant + +#### Scenario: Required evaluator facility is unqualified + +- **WHEN** name/type validation, conditional semantics, or execution limits cannot meet the admitted profile +- **THEN** Phase 2 activation remains blocked until the integration is qualified or the unsupported construct is explicitly excluded from the profile + +### Requirement: Named Caveats reference shared immutable expression entities + +Each named Caveat definition SHALL reference one expression entity containing a versioned canonical payload of exact CEL source, typed parameter declarations, and semantic profile identity. Relationship slot five SHALL continue to reference the named definition; the expression entity SHALL NOT add a tuple component or a per-Relationship entity. Schema reads SHALL reconstruct the named declaration from this representation. + +Managed schema writes SHALL validate and atomically intern identical expression payloads, including under concurrent admission. Canonical identity SHALL include source text, parameter names/types in canonical order, and profile, and SHALL exclude the Caveat name, Relationship identity, expiry, and request/bound values. Equal canonical payloads SHALL share an expression within one database. Logical equivalence of different formulas, whitespace normalization, and parameter renaming SHALL NOT be required. Digest-based lookup SHALL compare full canonical payloads and fail explicitly on unequal content under the same digest. + +Published expression payloads SHALL be immutable under admitted writes. Editing a named definition SHALL update its expression reference and schema generation atomically, with existing in-use and compatibility guards. Schema proofs SHALL cover the reference and full expression payload. Eids SHALL be interpreted in the selected database/source/branch; a bare eid SHALL NOT establish cross-view content equivalence. Relationship deletion and collection SHALL NOT delete shared expression entities. The initial implementation SHALL retain unused expressions rather than introduce shared-expression collection. + +#### Scenario: Two names have identical expressions + +- **WHEN** two named Caveats declare identical canonical source, parameters, and profile +- **THEN** they reference one interned expression entity while retaining distinct named schema identities + +#### Scenario: Same text with a different parameter type + +- **WHEN** two admitted expressions have equal source but different parameter types or semantic profiles +- **THEN** they have distinct expression identities and cannot alias one program-cache entry + +#### Scenario: Equivalent formulas have different text + +- **WHEN** two formulas are logically equivalent but have different source text +- **THEN** the implementation may retain separate expression entities and programs +- **AND** it does not claim semantic equivalence from a shared digest or a name + +#### Scenario: Concurrent interning + +- **WHEN** concurrent supported schema writes admit identical expression payloads +- **THEN** committed references resolve to one interned entity for that content +- **AND** a digest collision with unequal content rejects admission without partial schema changes + +#### Scenario: Definition replacement preserves old expression content + +- **WHEN** a supported schema edit changes the expression of an existing named Caveat +- **THEN** the definition references the appropriate immutable expression and its schema generation changes +- **AND** a retained old database view still resolves its original expression under the backend's retention contract + +#### Scenario: Relationship collection leaves shared expressions available + +- **WHEN** collection removes the last Relationship using a Caveat +- **THEN** it removes only the endpoint pair and owned non-empty bound context +- **AND** it does not retract the named definition or shared expression + +#### Scenario: Missing expression payload + +- **WHEN** a selected named definition references a missing or malformed authoritative expression +- **THEN** the affected operation fails with a typed authoritative-state error +- **AND** a previously cached program does not conceal the missing payload + +### Requirement: Clients compile programs lazily during traversal + +An EACL client SHALL construct a program on the first cache miss when traversal actually evaluates an expiry-active Caveated Relationship. Construction SHALL use the selected snapshot's validated expression content. Client construction, ordinary schema reads, stored Relationship enumeration, expired candidates, and paths not evaluated SHALL NOT trigger eager runtime program compilation. Bounded validation at schema-write admission SHALL remain required and SHALL NOT be deferred to this first traversal. + +On the JVM, the integration SHALL use `exoscale.cel.parser/make-program` to build reusable parsed programs and `eval-for` for repeated evaluation with fresh effective bindings. It SHALL NOT use `parse-eval` per Relationship. Program construction SHALL include any reusable EACL validation/evaluation plan required by the admitted profile. Programs SHALL remain independent of request bindings, Relationship-bound values, expiry, and residual/result state. Program-cache lifecycle and identity SHALL satisfy the separate cache requirements. + +#### Scenario: Unused and expired Caveats + +- **WHEN** a client reads a schema with several Caveats but traverses only uncaveated or expired edges +- **THEN** it performs no runtime program compilation for those unused expressions + +#### Scenario: First and repeated traversal + +- **WHEN** an active Caveated edge first needs evaluation and its program is absent +- **THEN** the client builds and retains the program under the expression/evaluator key +- **AND** later evaluations reuse it while resident, including through another Caveat name with identical expression content + +#### Scenario: Fresh bindings for a shared program + +- **WHEN** two Relationships or concurrent requests evaluate one program with different effective contexts +- **THEN** each evaluation observes only its own context and produces its own result +- **AND** neither the first bindings nor the first result becomes cached program state + +#### Scenario: Invalid schema is rejected before lazy traversal + +- **WHEN** a schema write contains an undefined name, unsupported overload, or non-Boolean expression +- **THEN** admission rejects it before commit even if no traversal would immediately reach that Caveat + +### Requirement: Warm program evaluation accounts for macro bodies and bounded work + +For admitted collection macros, the qualified integration SHALL reuse parsed body plans rather than reparse source for each collection element or Relationship evaluation. The inspected upstream visitor reparses bodies inside iterations; top-level program caching alone SHALL NOT establish this requirement. An unqualified macro SHALL be rejected at schema admission under the published profile. Qualification SHALL instrument nested macro-body parser invocations and distinguish program construction from warm evaluation. + +The integration SHALL enforce explicit input, parse/tree, collection, regex, and aggregate execution limits. It SHALL qualify actual work termination rather than assume a timeout cancels underlying execution. Cold program construction, concurrent build admission, context conversion, and repeated evaluations SHALL be accounted for separately where appropriate and SHALL NOT bypass request limits. + +#### Scenario: Repeated collection macro evaluation + +- **WHEN** an admitted macro is evaluated over several collection elements using a resident program +- **THEN** parser instrumentation records no body-source reparsing during those evaluations +- **AND** evaluation still applies the correct per-element and per-request bindings + +#### Scenario: Nested macro + +- **WHEN** the admitted profile includes nested macros +- **THEN** the same reuse and work-limit requirements apply to every nested body + +#### Scenario: Cold build exceeds its budget + +- **WHEN** program construction or build admission exceeds the configured execution limit +- **THEN** the affected request receives a typed limit failure +- **AND** no partial program or authorization result is published + + +### Requirement: Caveat definitions are typed schema declarations + +EACL SHALL parse, validate, persist, read, compare, and replace SpiceDB-style +top-level Caveat definitions. A Caveat definition SHALL have a stable name, +typed parameters, and a bounded CEL expression that produces a boolean or a +partial result when required context is absent. + +EACL SHALL publish a versioned Caveat compatibility profile. Syntax, parameter +types, functions, and operators outside that profile MUST be rejected at schema +write rather than approximated. + +#### Scenario: Valid Caveat definition + +- **WHEN** a schema declares a Caveat whose parameters and expression are + supported by the active compatibility profile +- **THEN** EACL persists a canonical named Caveat schema entity referencing its interned expression +- **AND** schema reads reproduce an equivalent declaration +- **AND** all supported runtimes interpret the same canonical typed representation, with programs built lazily when evaluated + +#### Scenario: Unknown Caveat parameter + +- **WHEN** a Caveat expression references a name absent from its parameter list +- **THEN** schema validation fails with a typed Caveat error +- **AND** no schema replacement commits + +#### Scenario: Non-boolean expression + +- **WHEN** a Caveat expression cannot produce a boolean result +- **THEN** schema validation rejects it + +#### Scenario: Unsupported CEL construct + +- **WHEN** a Caveat uses syntax, a type, a function, or an overload outside the + declared EACL compatibility profile +- **THEN** schema validation rejects it explicitly +- **AND** does not silently change its meaning + +#### Scenario: Caveat definition changes + +- **WHEN** a supported schema write changes a Caveat's parameters or expression +- **THEN** the schema generation changes +- **AND** cached plans and answers from the old definition cannot be reused + +### Requirement: Relation subject branches declare allowed Caveats + +A relation type reference SHALL be able to name one allowed Caveat using +SpiceDB-compatible `with ` syntax. A logical relationship may attach +zero or one Caveat, and the selected Caveat SHALL be valid for the exact +resource relation, subject type, wildcard state, and optional subject relation. + +A schema MAY include both uncaveated and caveated branches for the same subject +form to make the Caveat optional. When only a caveated branch matches, the +relationship write SHALL require that Caveat. + +#### Scenario: Optional Caveat branch + +- **GIVEN** a relation allows both `user` and `user with ip_allowlist` +- **WHEN** a relationship is written for a user with no Caveat +- **THEN** the write is valid +- **WHEN** the same relation is written with `ip_allowlist` +- **THEN** the Caveated write is also valid + +#### Scenario: Required Caveat branch + +- **GIVEN** the only matching branch is `user with ip_allowlist` +- **WHEN** a relationship is written without a Caveat +- **THEN** EACL rejects it with a typed required-Caveat error + +#### Scenario: Caveat is not allowed + +- **WHEN** a relationship names a Caveat not allowed by its matching relation + branch +- **THEN** EACL rejects the write before transaction submission + +#### Scenario: Missing Caveat definition + +- **WHEN** a relation branch references an undefined Caveat +- **THEN** schema validation fails before persistence + +#### Scenario: More than one Caveat on one relationship + +- **WHEN** a relationship update attempts to attach multiple Caveats +- **THEN** EACL rejects it +- **AND** callers must combine the condition inside one Caveat definition + +### Requirement: Caveat qualifiers use tuple slots five and six + +For a Caveated relationship, endpoint slot five SHALL contain the internal eid +of the selected schema-level Caveat definition. Endpoint slot six SHALL be +either `nil` or the eid of one relationship-specific Caveat context entity. +Both endpoint halves SHALL contain identical values. + +A relationship without a Caveat SHALL contain `nil` in both slots. A context ref +without a Caveat ref SHALL be an authoritative integrity fault that aborts an +affected operation; it SHALL NOT be converted to absent membership. + +#### Scenario: Uncaveated relationship + +- **WHEN** a relationship has no Caveat +- **THEN** both endpoint halves contain `nil` in slots five and six +- **AND** no Caveat context entity is created + +#### Scenario: Caveat with empty bound context + +- **WHEN** a relationship attaches a Caveat with no relationship-bound values +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six is `nil` +- **AND** request context may supply the Caveat parameters + +#### Scenario: Caveat with non-empty bound context + +- **WHEN** a relationship attaches a Caveat with one or more bound values +- **THEN** slot five contains the Caveat definition eid +- **AND** slot six contains one context eid shared by both endpoint halves + +#### Scenario: Mismatched Caveat qualifiers + +- **WHEN** the endpoint halves contain different Caveat or context refs +- **THEN** integrity reporting identifies the mismatch +- **AND** an affected authorization operation fails with + `:eacl/invalid-relationship-state` + +### Requirement: Relationship-bound Caveat context is sparse and immutable + +Non-empty relationship-bound Caveat context SHALL live in one internal, +singly-owned entity referenced by endpoint slot six. The entity SHALL contain +one canonical, type-preserving, bounded context-map payload and SHALL have no +public object ID. + +An empty context SHALL use a nil tuple ref and no entity. Context entities SHALL +not be deduplicated across relationships. Once referenced by a committed +relationship, a context entity SHALL be immutable; changing context SHALL +replace the entity and both endpoint refs atomically. + +#### Scenario: Sparse context storage + +- **WHEN** a Caveated relationship binds no parameter values +- **THEN** it allocates no relationship-specific context datom + +#### Scenario: Non-empty context storage + +- **WHEN** a Caveated relationship binds a non-empty context map +- **THEN** EACL stores one canonical payload datom on one context entity +- **AND** arbitrary context bytes are not duplicated in the two endpoint tuples + +#### Scenario: Context changes through touch + +- **WHEN** `:touch` changes relationship-bound context +- **THEN** EACL creates a replacement immutable context entity +- **AND** replaces both endpoint refs and retracts the old singly-owned entity + in the same admitted transaction + +#### Scenario: Context entity is shared out of band + +- **WHEN** integrity inspection finds one context eid referenced by more than + one logical relationship +- **THEN** it reports an ownership violation +- **AND** repair does not guess which relationship owns it + +#### Scenario: Context payload changes in place out of band + +- **WHEN** a context payload is mutated without replacing the endpoint ref +- **THEN** database-visible content proofs detect the change +- **AND** prior cached results are not reused + +### Requirement: Relationship writes validate Caveat context + +Relationship-bound context SHALL be validated against the selected Caveat's +declared parameter names and types before transaction submission. Context input +SHALL use a canonical JSON-like value model compatible with the declared +SpiceDB profile and EACL's portable CLJ/CLJS encoding. + +EACL SHALL enforce configured depth, entry-count, string/byte, collection, and +encoded-size limits without truncation. + +#### Scenario: Partially bound context + +- **WHEN** relationship context supplies a valid subset of Caveat parameters +- **THEN** the write succeeds +- **AND** unsupplied parameters remain available for request-time context + +#### Scenario: Unknown bound key + +- **WHEN** relationship context includes a key not declared by the Caveat +- **THEN** EACL rejects the write with a typed context error + +#### Scenario: Bound value has wrong type + +- **WHEN** a relationship-bound value cannot inhabit the declared parameter type +- **THEN** EACL rejects the write + +#### Scenario: Oversized bound context + +- **WHEN** bound context exceeds any configured structural or encoded-size limit +- **THEN** EACL rejects it before allocating a context entity +- **AND** does not truncate it into a potentially granting value + +### Requirement: Caveat and expiry do not change relationship identity + +EACL SHALL permit at most one stored relationship for a +resolved subject/relation/resource identity, including the endpoint owner and +existing wildcard/subject-relation form, regardless of Caveat, Caveat context, or +expiry. Caveat qualifiers SHALL be mutable properties of that relationship, +matching SpiceDB behavior. + +`:create` SHALL conflict with any existing qualifier state. `:touch` SHALL +replace Caveat, context, and expiry atomically. `:delete` SHALL not require +the caller to supply Caveat or context. + +#### Scenario: Duplicate differs only by Caveat + +- **WHEN** a caller creates the same logical relationship with another Caveat +- **THEN** EACL reports `:eacl/relationship-conflict` +- **AND** does not store a second relationship + +#### Scenario: Duplicate differs only by context + +- **WHEN** a caller creates the same logical relationship with different bound + context +- **THEN** EACL reports `:eacl/relationship-conflict` + +#### Scenario: Touch replaces Caveat + +- **WHEN** `:touch` changes an uncaveated relationship to a Caveated one or + changes the selected Caveat +- **THEN** one canonical relationship remains with the requested qualifiers + +#### Scenario: Delete omits Caveat + +- **WHEN** a caller deletes a Caveated relationship by logical identity +- **THEN** EACL deletes it without requiring Caveat name or context +- **AND** retracts its singly-owned context entity + +### Requirement: Request context merges with bound context using SpiceDB precedence + +Caveat-aware authorization and lookup requests SHALL accept a bounded canonical +request-context map. EACL SHALL merge request context with +relationship-bound context so bound values take precedence for duplicate keys. + +The merge SHALL be shallow by parameter name: a bound map value replaces +the whole request parameter, rather than recursively merging nested maps. +Unknown request keys SHALL remain outside the expression parameter environment +and SHALL remain in the conservative request identity. Structural/size admission precedes the merge; Caveat-specific type conversion +SHALL apply to the effective merged values before evaluation. A caller MUST NOT override a value fixed on the relationship. + +#### Scenario: Request supplies missing value + +- **GIVEN** a relationship binds `cidr` but not `user_ip` +- **WHEN** the request supplies a valid `user_ip` +- **THEN** the evaluator receives both values + +#### Scenario: Bound value overrides request value + +- **GIVEN** a relationship binds `expected_aud` +- **WHEN** the request supplies a different `expected_aud` +- **THEN** the evaluator receives the relationship-bound value + +#### Scenario: Shadowed request value has wrong Caveat type + +- **GIVEN** a relationship binds a valid value for a Caveat parameter +- **WHEN** a structurally valid request supplies a value of another type for that same parameter +- **THEN** Caveat type validation uses the bound value and does not reject the shadowed value as the effective parameter + +#### Scenario: Bound map replaces request map + +- **GIVEN** a relationship binds parameter `settings` to a map without key `admin` +- **WHEN** request context supplies a `settings` map containing `admin` +- **THEN** the effective `settings` is exactly the bound map +- **AND** the request cannot add keys through recursive merging + +#### Scenario: Request value has wrong type + +- **WHEN** request context supplies an invalid type for a visible Caveat + parameter +- **THEN** EACL returns a typed invalid-context request error +- **AND** does not treat the edge as true + +#### Scenario: Oversized request context + +- **WHEN** request context exceeds a configured limit +- **THEN** EACL rejects the request before cache-key construction or evaluation + +### Requirement: Caveat evaluation returns definite or conditional membership + +For an expiry-active, structurally valid Caveated relationship, EACL SHALL +evaluate the selected Caveat using the merged context. Evaluation SHALL produce +one of: + +- true: the relationship may participate in the graph; +- false: the relationship is absent from the effective graph; +- partial: required context is missing, so the relationship contributes a + conditional residual expression and missing parameter names. + +Evaluation errors, malformed definitions, unsupported values, or missing +referenced entities SHALL abort the affected operation with a typed error. +They SHALL NOT become false/absent operands or successful no-permission +results inside permission algebra. Only a valid false evaluation or a well-formed Relationship that has expired +contributes definite absence. + +The EACL adapter SHALL explicitly represent absent declared parameters and map unresolved outcomes into canonical residuals and missing fields. Native unknown/partial evaluation SHALL NOT be assumed from the cel-parser API. The qualified integration SHALL provide this behavior for the admitted profile. Missing context SHALL NOT be implemented by binding absent parameters to nil, by treating every unbound-variable error as conditional, or by treating a set of missing names as a complete residual formula. The checked expression and effective bindings needed to reproduce a residual SHALL remain part of its canonical identity. Reported evaluation failures and execution-limit violations SHALL remain errors rather than unknown values. + +#### Scenario: Missing parameter is unknown + +- **WHEN** an admitted Caveat result still depends on an absent declared parameter +- **THEN** the adapter returns conditional membership with the relevant missing fields and reproducible residual identity +- **AND** the Relationship is neither denied merely because a binding is absent nor granted unconditionally + +#### Scenario: Known Boolean branch decides an expression + +- **WHEN** the pinned CEL profile determines a definite result despite an unknown operand +- **THEN** the adapter preserves that definite result rather than reporting irrelevant missing context + +#### Scenario: Failure is not missing context + +- **WHEN** the cel-parser integration reports an evaluation failure or the EACL work budget is exceeded +- **THEN** the affected operation fails with the specified typed error +- **AND** the failure is not converted to false or conditional subtracting evidence + +#### Scenario: Caveat evaluates true + +- **WHEN** every required parameter is known and the Caveat evaluates true +- **THEN** the relationship participates subject to all other qualifiers + +#### Scenario: Caveat evaluates false + +- **WHEN** every required parameter is known and the Caveat evaluates false +- **THEN** the relationship does not participate + +#### Scenario: Context is missing + +- **WHEN** the result depends on one or more absent parameters +- **THEN** the relationship produces a conditional result +- **AND** identifies the missing parameter names + +#### Scenario: Inactive relationship is Caveated + +- **WHEN** expiry makes a Caveated relationship inactive +- **THEN** the engine rejects it before loading bound context or invoking the + Caveat evaluator + +#### Scenario: Evaluator error + +- **WHEN** evaluator execution fails or exceeds a configured bound +- **THEN** EACL returns a typed evaluation failure +- **AND** publishes neither a successful permissionship nor a continuation + +### Requirement: Conditional conditions compose through permission algebra + +The engine SHALL preserve conditional residual conditions until permission +evaluation is determined. Union, intersection, exclusion, arrows, recursion, +and subject-relation traversal SHALL combine definite and conditional branches +without collapsing missing context to false prematurely. + +An unconditional witness MAY determine a positive union despite another +conditional branch. A definite subtracting witness MAY determine an exclusion +denial. When missing context can still change the final answer, the result SHALL +remain conditional and report the complete minimal or conservative missing +field set. + +#### Scenario: Unconditional union witness + +- **GIVEN** a permission is `owner + viewer` +- **WHEN** `owner` is definitely true and `viewer` is conditional +- **THEN** the permission is has-permission + +#### Scenario: Conditional union + +- **WHEN** every possible granting branch is false or conditional and at least + one conditional branch could grant +- **THEN** the permission is conditional-permission + +#### Scenario: Conditional intersection + +- **WHEN** one required intersection branch is conditional and no branch is + definitely false +- **THEN** the permission is conditional-permission + +#### Scenario: Definite false intersection + +- **WHEN** one required intersection branch is definitely false +- **THEN** the permission is no-permission even if another branch is + conditional + +#### Scenario: Conditional exclusion + +- **GIVEN** a permission is `viewer - banned` +- **WHEN** `viewer` is true and `banned` is conditional +- **THEN** the result remains conditional because missing context can determine + whether access is subtracted + +#### Scenario: Arrow contains conditional edge + +- **WHEN** an arrow path depends on a conditional relationship +- **THEN** the resulting subject/resource membership retains that condition + +### Requirement: Public Caveat-aware results match SpiceDB permissionship + +The Caveat-aware permission API SHALL return one of +`:has-permission`, `:no-permission`, or `:conditional-permission`. +A conditional response SHALL include the context field names still required to +determine the answer and MAY include a bounded diagnostic residual condition. + +The existing Boolean `can?` API SHALL return true only for +`:has-permission`. It SHALL return false for both no-permission and +conditional-permission so missing context never grants access. + +Lookup operations SHALL distinguish definitely authorized and conditionally +authorized results. Continuations SHALL preserve that result semantics. + +#### Scenario: Complete true check + +- **WHEN** Caveat evaluation and graph traversal determine access +- **THEN** the Caveat-aware API returns `:has-permission` +- **AND** `can?` returns true + +#### Scenario: Complete false check + +- **WHEN** graph traversal determines access is absent +- **THEN** the Caveat-aware API returns `:no-permission` +- **AND** `can?` returns false + +#### Scenario: Incomplete check + +- **WHEN** missing context prevents a definite decision +- **THEN** the Caveat-aware API returns `:conditional-permission` +- **AND** returns the missing field names +- **AND** `can?` returns false + +#### Scenario: Conditional lookup result + +- **WHEN** a lookup candidate is reachable only under a residual Caveat +- **THEN** the lookup result marks that candidate conditional rather than + silently including it as definitely authorized + +### Requirement: Caveat dependencies are cache- and cursor-complete + +Any cache entry, subproblem, continuation, explanation, or cursor influenced by +Caveats SHALL distinguish the canonical request context, Caveat definition and +schema generation, relationship context eid and payload, evaluator +profile/fingerprint, residual condition, missing fields, and result kind. + +A relation-version proof alone SHALL NOT authorize reuse after a context payload +or evaluator dependency changes. Cache/context identity SHALL be bounded before +hashing or canonicalization. + +#### Scenario: Request context changes + +- **WHEN** two requests differ in a context value visible to any possible + Caveat path +- **THEN** they cannot collide in cache or cursor identity + +#### Scenario: Caveat definition changes + +- **WHEN** schema replacement changes a referenced Caveat +- **THEN** old plans, answers, and continuations cannot be reused + +#### Scenario: Context payload changes out of band + +- **WHEN** a context entity's payload changes without a relation-version update +- **THEN** database-visible proof validation rejects prior results + +#### Scenario: Conditional result is replayed as definite + +- **WHEN** an external cache attempts to substitute a conditional value for a + definite value or vice versa +- **THEN** complete authenticated entry identity rejects the substitution + +### Requirement: Caveat faults and orphaning fail closed + +A missing Caveat definition, missing context entity, malformed payload, invalid +type, mismatched endpoint qualifier, evaluator incompatibility, or unsupported +profile SHALL never unconditionalize a relationship. + +Committed schema replacement SHALL reject removal of a Caveat definition still +referenced by stored relationships with no inert-orphan exception in v9. Referenced parameter removals and type +changes SHALL be rejected when they would invalidate stored bound context or +relation branches. Commit-time schema guards SHALL serialize these checks +with relationship writes. Integrity reports SHALL identify +orphaned and malformed Caveat data. + +#### Scenario: Caveat definition is missing + +- **WHEN** a relationship references a Caveat entity absent from the selected + schema +- **THEN** the affected operation fails with a typed authoritative-state error +- **AND** integrity reports the orphan + +#### Scenario: Context entity is missing + +- **WHEN** slot six references an absent context entity +- **THEN** the affected operation fails with a typed authoritative-state error +- **AND** the missing entity is neither empty context nor false membership + +#### Scenario: Referenced Caveat removal + +- **WHEN** schema replacement would remove a Caveat used by stored + relationships +- **THEN** the committed write fails with a typed in-use error + +#### Scenario: Unknown writer creates invalid Caveat data + +- **WHEN** an out-of-band writer creates malformed Caveat qualifiers +- **THEN** authorization fails closed +- **AND** proof and integrity paths expose the change + +### Requirement: Relationship entities remain a future assertion boundary + +Caveat definitions, shared expression entities, and sparse context entities SHALL NOT require EACL to reify +every relationship. First-class relationship or grant-assertion entities MAY be +introduced only when the domain requires independent assertion identity, such +as multiple grantors, independently revocable grants, multiple simultaneous +intervals, provenance, approvals, or stable grant IDs. + +#### Scenario: Ordinary Caveated relationship + +- **WHEN** one Caveat with optional context qualifies one logical relationship +- **THEN** the endpoint pair remains the authoritative relationship +- **AND** the context entity is qualifier data, not a relationship vertex + +#### Scenario: Multiple independent grants are requested + +- **WHEN** a future feature requires two independently revocable grants for one + subject/relation/resource identity +- **THEN** that feature receives a separate storage-model proposal +- **AND** is not simulated by duplicate v9 qualifier variants + +### Requirement: Cross-runtime Caveat conformance + +The JVM and ClojureScript implementations SHALL consume one canonical typed +Caveat representation and produce equivalent definite, false, partial, +missing-context, and error results. Phase 2 SHALL include differential fixtures +against the declared SpiceDB compatibility profile. The JVM path SHALL use the +pinned cel-parser adapter. The CLJS path SHALL provide equivalent behavior without +requiring JVM/ANTLR program objects at JavaScript runtime. Phase 2 SHALL +remain gated on this qualification; library selection SHALL NOT silently reduce +the supported runtime contract or introduce remote evaluation. + +#### Scenario: JavaScript runtime boundary + +- **WHEN** DataScript Caveats run under CLJS/Node +- **THEN** the qualified execution path consumes the portable representation without loading Java classes +- **AND** its results agree with the JVM cel-parser adapter and declared reference corpus + +#### Scenario: Shared conformance corpus + +- **WHEN** the same Caveat, relationship context, and request context are + evaluated on every supported runtime +- **THEN** the permissionship and missing-context results are identical + +#### Scenario: SpiceDB reference fixture + +- **WHEN** a fixture uses syntax and values inside the declared compatibility + profile +- **THEN** EACL and the pinned SpiceDB reference produce equivalent observable + results + +#### Scenario: Runtime divergence + +- **WHEN** differential testing finds a semantic mismatch +- **THEN** Phase 2 release is blocked until EACL rejects the construct or the + runtimes agree + + +### Requirement: Caveat value encoding is explicitly typed and portable + +Before Phase 2 activation, EACL SHALL freeze a versioned supported type/coercion +profile and an injective typed encoding for every admitted CEL value. The +secure-format v1 envelope SHALL NOT be mistaken for that value model: native +doubles and integers beyond the JS safe range are not directly supported. +Unsupported values SHALL be rejected without rounding, overflow, or conflating +the types exposed to `any`. The reference SpiceDB release or commit SHALL be +recorded with the differential corpus. + +#### Scenario: Large integer and double encoding + +- **WHEN** the profile admits a signed/unsigned 64-bit integer or a double +- **THEN** CLJ and CLJS preserve the exact declared value and its type in canonical bytes +- **AND** numeric strings follow the declared conversion rule rather than silently rounding through a JS number + +#### Scenario: Encoding is not yet qualified + +- **WHEN** a profile type lacks a shared encoding or conformance result +- **THEN** Phase 2 admission for that type remains disabled + +### Requirement: Authoritative faults never erase subtracting evidence + +An encountered authoritative integrity or evaluation failure SHALL abort the +entire affected check, lookup, count, explanation, or batch operation. EACL +MUST NOT turn it into false or absent membership. `can?` SHALL propagate the +typed error. No successful reusable result SHALL be published from the failed +operation. This is separate from conditional permission caused by missing +request context. + +#### Scenario: Corrupt subtracting edge + +- **GIVEN** permission is `viewer - banned`, `viewer` is true, and `banned` has missing context storage, mismatched halves, or duplicate variants +- **WHEN** the operation encounters the fault +- **THEN** it fails with a typed error +- **AND** it cannot grant by dropping `banned` + +#### Scenario: Subtracting evaluator fails + +- **GIVEN** permission is `viewer - banned` and the `banned` Caveat exceeds its evaluation limit +- **WHEN** the evaluator reports failure +- **THEN** the whole operation fails rather than treating `banned` as false + +### Requirement: Recursive residual evaluation terminates within declared limits + +Recursive Caveat evaluation SHALL use canonical bounded residual identity and +fixed-point processing. Repeated visits to equivalent conditional states MUST +NOT grow residual formulas indefinitely. Unsupported recursive constructs SHALL +be rejected at schema admission. Work-limit exhaustion SHALL be a typed failure. + +#### Scenario: Conditional cycle + +- **WHEN** a supported recursive relation revisits the same residual state +- **THEN** evaluation converges without unbounded expression unfolding +- **AND** the shared conformance corpus verifies permissionship and missing fields + +#### Scenario: Same Caveat with different bound values + +- **WHEN** two paths attach one Caveat definition with different bound contexts +- **THEN** their residual identities preserve those bindings +- **AND** simplification cannot equate the paths merely by Caveat name + +### Requirement: Caveat evaluation has no ambient mutable inputs + +For a fixed selected schema, Relationship qualifiers, and admitted request context, the evaluator SHALL produce deterministic results within configured work limits. It MUST NOT read an ambient clock, network service, or other mutable state outside the declared request and dependency scope. Clock-like Caveat parameters SHALL be explicit context values and SHALL NOT override trusted Relationship expiry time. + +#### Scenario: Repeated deterministic evaluation + +- **WHEN** a Caveat is evaluated repeatedly with identical declared inputs +- **THEN** its permissionship, residual identity, missing fields, and error classification agree + +#### Scenario: Ambient clock request + +- **WHEN** a schema requests an evaluator feature that reads an undeclared current clock or external service +- **THEN** admission rejects the construct rather than making cached results depend on hidden state + +### Requirement: Expired and speculatively inert references retain Caveat lifecycle guards + +Caveat in-use checks SHALL cover stored references even when their Relationships are expired or their Relations are retained inert in a speculative snapshot. The existing speculative Relation orphan policy SHALL NOT permit removing a still-referenced Caveat definition or invalidating its stored bound context. The caller SHALL first remove or rewrite the references through an explicit guarded Relationship operation. + +#### Scenario: Expired reference still exists + +- **WHEN** schema replacement removes a Caveat referenced only by expired stored Relationships +- **THEN** the replacement fails with a typed in-use error until those references are deleted or rewritten + +#### Scenario: Relation is retained inert + +- **WHEN** speculative schema replacement retains Relationship tuples inert while also removing their referenced Caveat +- **THEN** the Caveat lifecycle guard rejects that replacement + +### Requirement: Caveat activation is a semantic capability boundary + +Phase 1 SHALL reject non-nil Caveat qualifiers and Caveat feature use with a typed unsupported-feature error. Phase 2 SHALL enable the same reserved tuple positions only after profile, encoding, evaluator, algebra, cache/cursor, and cross-runtime conformance gates pass. An encountered unsupported Caveat SHALL NOT be treated as an absent edge. + +#### Scenario: Phase 1 encounters a Caveated subtracting edge + +- **WHEN** a Phase 1 reader encounters a non-nil Caveat slot on evidence for an exclusion +- **THEN** it fails the affected operation rather than granting by omitting the edge + +#### Scenario: Phase 2 enables the qualified profile + +- **WHEN** the capability epoch activates after all Phase 2 gates pass +- **THEN** existing seven-slot positions retain their meanings +- **AND** old current-serving clients and reusable artifacts are fenced by the compatibility contract diff --git a/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/tasks.md b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/tasks.md new file mode 100644 index 00000000..b4c65b6d --- /dev/null +++ b/openspec/history/superseded/2026-09-04-add-v9-caveats-and-expiring-relationships/tasks.md @@ -0,0 +1,109 @@ +## 1. Freeze implementation contracts and qualification fixtures + +- [ ] 1.1 Record the seven-slot attribute definitions, format fingerprint, storage version, and Phase 1/2 capability epochs; verify a reviewed ABI manifest matches the design and all six delta specs. +- [ ] 1.2 Freeze public expiry/read-mode/clock/collector options and typed error names against existing API conventions; verify examples cover absent expiry, exact boundary, rejected valid-from, delayed already-expired creation, renewal, and stored versus expiry-active reads. +- [ ] 1.3 Define numeric latency, allocation/space, and collection-throughput budgets before candidate qualification; verify the benchmark plan distinguishes raw primitives from complete public operations and records 0%, 1%, 5%, and clustered-expiry workloads. +- [ ] 1.4 Establish isolated nREPL fixtures and assertion helpers for seven-slot storage on every backend; verify independent logical-identity and forward/reverse counts rather than assuming seed completion means correct contents. + +## 2. Install the seven-slot storage boundary — Phase 1 + +- [ ] 2.1 Add the Datomic Pro schema and storage-format admission checks; verify through an in-memory REPL that nil qualifiers, finite expiry, and all seven declared component types transact and incompatible populated layouts fail startup. +- [ ] 2.2 Add equivalent Datahike and Datalevin schemas and format checks; verify their native round trips and rejection of old/mixed representations through nREPL. +- [ ] 2.3 Add DataScript's full seven-element representation and validation; verify JVM and CLJS round trips retain explicit nils and reject shortened/malformed values. +- [ ] 2.4 Require nil Caveat slots in Phase 1 and fingerprint cache/cursor/engine semantics; verify unsupported Caveats and old four/eight/differently-defined-seven-slot artifacts cannot influence authorization. +- [ ] 2.5 Implement capability epoch validation and document the coordinated cutover or enforceable deployment fence; verify a long-lived Phase 1 client retaining a minimize-latency pin cannot serve or write current Phase 2 data. + +## 3. Update the shared codec and ordered access + +- [ ] 3.1 Implement construction, decoding, inversion, exact retractions, owner-qualified identity, and expiry normalization; verify round trips for permanent/expiring tuples and distinct owners with identical forward values. +- [ ] 3.2 Replace forward/reverse adjacency and logical-identity probes with guarded seven-slot access; verify no v7 fallback or second candidate stream appears and expiry is available in either direction without a schedule lookup. +- [ ] 3.3 Implement full-arity lower/upper bounds and whole-identity pagination boundaries for every backend; verify ascending/descending inclusion and exclusion with nil, negative, minimum, and maximum timestamps and adjacent prefixes. +- [ ] 3.4 Extend Relationship filtering, relation-in-use reads, and stored read projections to qualifiers; verify raw stored reads retain expired assertions and normalized status while expiry-active reads omit them. +- [ ] 3.5 Charge skipped candidates, lookahead, validation reads, and payload work to execution limits; verify a long expired prefix causes a typed limit failure rather than a false exhaustion marker, denial, or exact count. + +## 4. Preserve atomic logical identity and writer coherence + +- [ ] 4.1 Guard create/touch/delete against current owner-qualified identity state at commit for Datomic; verify racing creates and stale competing touches through in-memory nREPL transactions leave one canonical pair or a typed conflict. +- [ ] 4.2 Establish equivalent commit-time guards for Datahike, Datalevin, and DataScript writer topologies; verify unsupported remote/other topologies reject writes instead of relying on stale plan-time checks. +- [ ] 4.3 Implement full qualifier replacement and identity-only deletion; verify create conflicts before collection, touch renews retained expired data, different deadlines batch together, and renewal has no committed absence gap. +- [ ] 4.4 Resolve aliases before batch deduplication and reject conflicting updates of one identity; verify same-owner duplicates collapse, different owners remain distinct, and wildcard/subject-relation forms preserve identity. +- [ ] 4.5 Stamp every affected Relation once per actual mutation transaction and preserve the existing CAS protocol; verify unchanged touches avoid unnecessary state churn while renewal, deletion, and multi-Relationship batches update the right Relations. +- [ ] 4.6 Update public transaction helpers, speculative execution, object deletion, and bounded repair; verify committed/speculative parity, complete peer cleanup, atomic repair-limit failure, and no deletion of ambiguously owned context. + +## 5. Add trusted expiry-time evaluation + +- [ ] 5.1 Capture one trusted exact time per top-level client operation and per batch, independently of a reused database pin; verify deterministic clocks crossing expiry change fresh requests but not an in-flight operation. +- [ ] 5.2 Freeze time on explicit EACL snapshots and preserve it through with/with-schema; verify historical/speculative evaluation and database consistency tokens do not silently change or substitute the temporal mode. +- [ ] 5.3 Reject invalid/regressing configured-clock values and precision-losing expiry inputs; verify portable numeric extrema and non-integer cases on CLJ/CLJS without sleeps. +- [ ] 5.4 Filter expiry at every forward/reverse leaf before context evaluation and permission algebra; verify direct grants, intersections, exclusion, arrows, recursion, subject relations, wildcard paths, lookups, counts, and explanations at the exact deadline. +- [ ] 5.5 Enforce consumer-owned future creation with explicit unsupported-start errors and no implicit deadline extension; verify delayed writes remain stored/inactive when their absolute expiry has passed. + +## 6. Certify caches and continuations under expiry + +- [ ] 6.1 Add evaluation-time start and exclusive deadline to authenticated reusable value formats; verify every accepted hit checks both time and ordinary dependency proofs even if provider eviction is delayed. +- [ ] 6.2 Implement conservative proof derivation for completed grants, denials, counts, and pages with exact-time/no-publication fallback; verify viewer-minus-banned denial at 90 cannot survive banned expiry at 100 on an unchanged database basis. +- [ ] 6.3 Give denotations, projections, recursive state, and frontiers their own certificates; verify a permanent union answer cannot widen the certificate of its expiring child. +- [ ] 6.4 Preserve complete static Relation closure and source/branch/causal rules independently of temporal witnesses; verify unrelated writes can retain reuse, relevant writes invalidate it, and forward certificates never lift to an earlier frozen time. +- [ ] 6.5 Implement authenticated pinned/live cursor modes and explicit deadline restart; verify pinned evaluation at 90 survives wall time 110 while a live cursor at its deadline returns a restart error and no page. +- [ ] 6.6 Certify skipped negative evidence and lookahead for both pagination directions; verify expiry of a ban on an identity before the cursor forces a restart rather than silently omitting the newly authorized identity. +- [ ] 6.7 Extend the formal/refinement model for expiry-only graph removal, exclusion gains, artifact-local certificates, and renewal; verify the repository's applicable formal checks and nREPL counterexamples agree before Phase 1 qualification. + +## 7. Implement optional bounded collection and integrity + +- [ ] 7.1 Add explicitly invoked forward-scan collection with trusted cutoff, retention, scan/delete budgets, and progress scoped to its scan basis; verify no expiration index is required and permanent data is never selected. +- [ ] 7.2 Revalidate exact pair/context at commit and reuse normal guarded deletion/Relation stamping; verify renewal/collection races in both commit orders cannot delete a replacement and failed batches make no partial changes. +- [ ] 7.3 Define no-op and concurrent-insertion progress behavior; verify scans without deletions do not bump Relations and completion never claims to cover concurrent rows behind the scan cursor. +- [ ] 7.4 Update offline integrity and unknown-writer proofs for full seven-slot values and context ownership; verify detected faults abort affected authorization while qualified managed traversal avoids redundant peer reads solely to distrust atomicity. +- [ ] 7.5 Verify cache/cursor behavior after collection and retained-history reads; confirm affected Relation invalidation, unrelated Relation reuse, CREATE conflict removal, and absence of any claim that ordinary retraction excises Datomic history. + +## 8. Qualify and document Phase 1 + +- [ ] 8.1 Run all backend/JVM expiry, mutation, ordering, proof, and public API suites through nREPL; verify no unexpected failures and retain reproducible outputs with versions and fixture counts. +- [ ] 8.2 Run DataScript CLJS conformance after JVM work that needs the session's agents; verify equivalent tuple bounds, expiry, snapshot, cache, cursor, and writer outcomes under Node. +- [ ] 8.3 Benchmark actual seven-slot storage in an isolated local :dev Datomic database with at least one million independently enumerated identities; verify the predeclared matrix covers proportion versus local concentration, endpoint degree, both directions, cold/warm behavior, and concurrent renewal/collection. +- [ ] 8.4 Measure full public checks, pages, counts, proof costs, and scan-based collector throughput separately from primitive reads; verify the numerical budgets from task 1.3 and disclose unsupported/unmeasured scenarios rather than substituting the archived eight-slot timings. +- [ ] 8.5 Publish rebuild/reseed, rollback, consumer scheduling, trusted-clock, snapshot/live-cursor, retention, and collection guidance; verify examples use only valid-until and exercise a fresh rebuilt database before Phase 1 release. + +## 9. Freeze the Caveat profile and schema — Phase 2 + +- [ ] 9.1 Pin an exact `com.exoscale/cel-parser` version or immutable source build, concrete SpiceDB reference release/commit, and bounded supported syntax/type/coercion/function profile; verify the dependency/adapter/options/extension matrix identifies every supported and rejected construct, including documented upstream differences, without claiming full CEL compatibility. +- [ ] 9.2 Define the canonical typed representation, EACL name/type validation over the selected parsed form, and injective CLJ/CLJS value encoding within the portable envelope; verify byte-identical typed fixtures for admitted types and explicit rejection otherwise, without treating a parsed ANTLR tree as a checked CEL AST. +- [ ] 9.3 Add Caveat declaration parsing and bounded CEL-body syntax/name/type/profile validation before schema commit, with canonical storage, reads, comparison, and replacement; verify round trips and typed rejection of undefined names, non-Boolean expressions, unsupported constructs, and ambient mutable-input functions while client runtime program construction remains lazy. +- [ ] 9.4 Validate allowed/required Caveats per exact subject/wildcard/subject-relation branch; verify optional bare-plus-Caveated branches, required-only branches, and rejection of multiple attachments. +- [ ] 9.5 Serialize Caveat/schema in-use guards with Relationship writes; verify definition removal or incompatible parameter/branch changes fail for active, expired, and speculatively inert retained references. + +- [ ] 9.6 Add the pinned cel-parser dependency behind the JVM adapter boundary and qualify its JDK/ANTLR/transitive dependency compatibility; verify through nREPL that `make-program` and `eval-for` load and evaluate supported definitions, characterize returned versus thrown errors, and keep JVM/ANTLR classes out of CLJS builds. +- [ ] 9.7 Specify and qualify the CLJS execution path against the same portable representation/profile before Phase 2 activation; verify a cross-runtime prototype/corpus agrees with the selected JVM cel-parser integration without implicit remote evaluation or removing CLJS support. +- [ ] 9.8 Freeze expression-entity attributes, canonical payload encoding, digest identity, and named-definition references; verify equal source/parameter/profile payloads share identity across Caveat names, different types/profiles differ, and exact-text identity makes no claim of logical formula equivalence. +- [ ] 9.9 Implement guarded immutable expression interning and named-reference replacement on every backend, including speculative views; verify concurrent equal admissions converge, unequal-payload digest collisions reject atomically, edits update schema generation without mutating shared expressions, and current/retained views resolve their own content. +- [ ] 9.10 Preserve shared-expression lifecycle independently of Relationship-owned context; verify deletion/collection retains named definitions and expression entities, in-use schema guards remain enforced, and initially unused expression entities are retained for reuse. + +## 10. Add bound context and deterministic evaluation + +- [ ] 10.1 Store one immutable canonical payload only for non-empty bound context; verify empty context allocates nothing and both tuple halves share the same singly-owned context ref. +- [ ] 10.2 Implement context replacement/retraction with touch, delete, object cleanup, and collection; verify expiry-only touch can retain unchanged context while changed context atomically replaces both refs without orphaning shared definitions or deleting shared expressions. +- [ ] 10.3 Enforce structural size limits and bound parameter admission before allocation; verify unknown bound keys, wrong types, oversized/deep values, and malformed payloads fail without partial writes. +- [ ] 10.4 Capture canonical request context and apply shallow bound-over-request precedence; verify a bound map replaces an entire request map, shadowed values are type-checked only after precedence, unknown request keys stay outside the expression environment, and input is bounded before hashing. +- [ ] 10.5 Implement the JVM cel-parser adapter with explicit EACL partial evaluation, typed value conversion, and result/error mapping; verify in nREPL that missing declared parameters yield reproducible conditional residuals, decisive Boolean branches remain definite, returned error objects cannot become truthy grants, and reported errors abort an affected exclusion rather than becoming false or conditional operands. +- [ ] 10.6 Implement canonical residual composition for every operator and supported recursion; verify repeated predicates, different bound values under one Caveat name, conditional cycles, negation/exclusion, and termination/resource-limit behavior. + +- [ ] 10.7 Implement enforceable parse/tree, collection, regex, input, and aggregate work limits in the qualified integration, including cold compilation and extensions; verify through nREPL that actual underlying work terminates with the specified typed failure rather than relying on an unverified timeout or nonexistent library budget API. +- [ ] 10.8 Build the bounded client-local program cache with complete expression/evaluator identity and selected-view eid resolution; verify first active traversal compiles lazily, repeated identical expressions across names reuse a resident program and qualified handle without source rereads/rehashing/comparison per edge, ordinary schema reads and expired/unvisited paths do not compile, and source/branch eid collisions cannot alias unequal programs. +- [ ] 10.9 Coalesce concurrent program-cache misses and bound resident/in-flight work; verify one successful build serves a same-key cohort, different contexts remain isolated under concurrent `eval-for`, failures release in-flight state, eviction allows equivalent rebuild, and cancellation does not corrupt other callers. +- [ ] 10.10 Qualify reusable macro-body plans for every admitted collection macro, including nested bodies, or exclude unqualified constructs at schema admission; verify parser instrumentation detects the inspected upstream per-element reparsing and that the selected integration performs none during warm evaluation while preserving bindings and work limits. + +## 11. Expose Caveat results and complete reuse proofs + +- [ ] 11.1 Add three-state permission checks with missing-field diagnostics and Boolean can? compatibility; verify true only for has-permission, false for successful no/conditional states, and error propagation without fallback checks. +- [ ] 11.2 Add definite/conditional lookup and count semantics with identity deduplication; verify compatibility numeric counts exclude conditional identities and structured counts classify each identity once after permission algebra. +- [ ] 11.3 Extend lookup keys, dependency certificates, and authenticated values separately for complete request context, named definitions and expression content, bound payloads, cel-parser build and adapter/options/extensions identity, permissionship, residuals, and expiry certificates; verify configuration-only changes invalidate incompatible state, a program hit never substitutes for answer proof, and no answer-dependent cache lookup key is required. +- [ ] 11.4 Authenticate Caveat scope and retained partial state in cursors; verify changed request context/profile forces mismatch/restart and boundary deduplication never promotes a conditional result to a definite grant. +- [ ] 11.5 Extend source qualification, unknown-writer content proofs, and formal/refinement obligations to Caveats; verify context/expression payload or definition-ref mutation without a Relation/schema stamp invalidates affected reuse and eid resolution, missing expression state is not hidden by a cached program, and detected failed subtracting evidence cannot become an ordinary denial or grant. + +## 12. Qualify Phase 2 and complete release artifacts + +- [ ] 12.1 Run the pinned SpiceDB differential corpus against the selected cel-parser JVM integration and qualified CLJS path; verify definite, conditional, missing-field, type conversion, bounded recursion, and error behavior for every admitted profile construct, including known upstream divergences and EACL extensions. +- [ ] 12.2 Benchmark permanent, expiring, empty-context Caveated, non-empty-context Caveated, and mixed workloads using budgets fixed before qualification; verify separate reports for cold schema/expression reads and program construction, warm evaluation, parser invocation counts including macro bodies, partial/residual work, context/proof reads, cache hit/miss/eviction and memory behavior, repeated versus distinct expressions, concurrent cold requests, and evaluator limits. +- [ ] 12.3 Exercise fenced Phase 2 activation, retained historical Phase 1 snapshots, and old portable cache/cursor rejection; verify an obsolete pinned current-serving client cannot bypass the activation boundary. +- [ ] 12.4 Publish the supported Caveat profile, request/write/read/check/lookup/count examples, rollout constraints, performance evidence, and known limits; verify examples against the implementation through nREPL. +- [ ] 12.5 Run the repository's applicable formal/conformance and public-source closure gates after implementation, then strict OpenSpec validation; verify all required implementation tasks are complete before syncing/archiving this replacement and leave the deprecated predecessor untouched as history. diff --git a/openspec/history/superseded/README.md b/openspec/history/superseded/README.md new file mode 100644 index 00000000..f46f247e --- /dev/null +++ b/openspec/history/superseded/README.md @@ -0,0 +1,14 @@ +# Superseded qualifier plans + +The active series is: + +1. `2026-09-04-01-adopt-v9-qualifier-reference-storage` — land qualifier-eid in EACL v8. +2. `2026-09-04-02-build-caveat-qualifier-foundation`. +3. `2026-09-04-03-enable-qualified-relationship-evaluation`. +4. `2026-09-04-04-enable-live-security-keyring-rotation`. + +The two older extracted plans in this directory describe the superseded +representation. They are retained with their evidence for historical reference +and excluded from the active change list. The phased ZIP was removed after +verifying its extracted files were identical. The older ZIP differs from its +extracted plan and is retained here to avoid losing that history. diff --git a/openspec/history/superseded/eacl-v9-validity-and-caveats-openspec.zip b/openspec/history/superseded/eacl-v9-validity-and-caveats-openspec.zip new file mode 100644 index 00000000..9680ef53 Binary files /dev/null and b/openspec/history/superseded/eacl-v9-validity-and-caveats-openspec.zip differ diff --git a/src-build/eacl/build/config.clj b/src-build/eacl/build/config.clj index 43926176..203269ab 100644 --- a/src-build/eacl/build/config.clj +++ b/src-build/eacl/build/config.clj @@ -2,7 +2,7 @@ "Single source of truth for EACL Maven module identity and release metadata." (:require [clojure.string :as string])) -(def default-version "8.0.0-SNAPSHOT") +(def default-version "9.0.0-SNAPSHOT") (def default-java-release 25) (def minimum-java-release 8) (def maximum-java-release 26)