From d8ba9e7c11ce2f5827535e1d80992b924b0865d3 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sat, 13 Jun 2026 08:38:05 +0000 Subject: [PATCH 1/3] Add canonicalize(Float) -> Float for byte-equivalence use cases Floats are non-canonical by design: multiple (coefficient, exponent) pairs encode the same numeric value and `eq` rescales before comparing rather than relying on byte equality. Consumers that need raw-byte equality (mapping(Float => X) keys, hashing, set membership, content-addressed storage) had no way to obtain a canonical form. Add `LibDecimalFloat.canonicalize` as a public-API (internal) library function. It returns the representative with the largest |coefficient| that fits int224 subject to the exponent staying >= int32.min, reached by scaling the coefficient up by ten directly within those bounds. This avoids the two failure modes of the obvious `packLossless(maximizeFull)` composition (packLossless flagging value-preserving int224 truncation as lossy, and packLossy silently zeroing tiny inputs on exponent underflow). The function never reverts for any valid input Float, is idempotent, and is value-preserving (the result is `eq` to the input). The function is internal and unused by the concrete `DecimalFloat` contract, so it is dead-code-eliminated from the deployed bytecode: `testDeployAddress` and `testExpectedCodeHashDecimalFloat` still pass, so no redeploy / deploy-constant regeneration is required. Adds test/src/lib/LibDecimalFloat.canonicalize.t.sol covering zero, byte-equality across representations (positive and negative), value preservation, idempotence, and three fuzz tests (value preservation, idempotence, eq-implies-byte-equal) at 5096 runs each. Fixes #183 Co-Authored-By: Claude Opus 4.8 --- src/lib/LibDecimalFloat.sol | 40 ++++++++ .../lib/LibDecimalFloat.canonicalize.t.sol | 97 +++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 test/src/lib/LibDecimalFloat.canonicalize.t.sol diff --git a/src/lib/LibDecimalFloat.sol b/src/lib/LibDecimalFloat.sol index d06a9af..b9c4ad2 100644 --- a/src/lib/LibDecimalFloat.sol +++ b/src/lib/LibDecimalFloat.sol @@ -426,6 +426,46 @@ library LibDecimalFloat { } } + /// Canonicalize a Float to a unique byte representation per numeric value. + /// Floats are non-canonical by design (see the docstring on the `Float` + /// type): multiple `(coefficient, exponent)` pairs encode the same number + /// and equality is numeric (`eq`) rather than byte-level. This function + /// returns the single representative whose magnitude-maximised packing is + /// stable, so two Floats are numerically equal iff their canonical forms + /// are byte-equal (`Float.unwrap(a.canonicalize()) == Float.unwrap(b.canonicalize())`). + /// Intended for consumers that need raw-byte equality: `mapping(Float => X)` + /// keys, hashing, set membership, content-addressed storage. + /// + /// The chosen representative has the largest `|coefficient|` that fits + /// int224 subject to the exponent staying `>= type(int32).min`, reached by + /// scaling the coefficient up by ten directly within those bounds. This + /// never reverts for any valid input Float: scaling simply stops at the + /// limit. `canonicalize` is idempotent and value-preserving (the result is + /// `eq` to the input). + /// @param float The float to canonicalize. + /// @return The canonical representative of the float's numeric value. + function canonicalize(Float float) internal pure returns (Float) { + (int256 signedCoefficient, int256 exponent) = float.unpack(); + if (signedCoefficient == 0) { + return FLOAT_ZERO; + } + unchecked { + while (exponent > type(int32).min) { + int256 trySignedCoefficient = signedCoefficient * 10; + // int224 overflow is the termination condition for the scaling + // loop, not a bug. The cast back is compared against the + // pre-cast value to detect that overflow. + // forge-lint: disable-next-line(unsafe-typecast) + if (int224(trySignedCoefficient) != trySignedCoefficient) { + break; + } + signedCoefficient = trySignedCoefficient; + exponent -= 1; + } + } + return packLossless(signedCoefficient, exponent); + } + /// Same as add, but accepts a Float struct instead of separate values. /// Costs more gas but helps mitigate stack depth issues, and is more /// ergonomic for the caller. diff --git a/test/src/lib/LibDecimalFloat.canonicalize.t.sol b/test/src/lib/LibDecimalFloat.canonicalize.t.sol new file mode 100644 index 0000000..dd5b0ba --- /dev/null +++ b/test/src/lib/LibDecimalFloat.canonicalize.t.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity =0.8.25; + +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +import {Test} from "forge-std-1.16.1/src/Test.sol"; + +contract LibDecimalFloatCanonicalizeTest is Test { + using LibDecimalFloat for Float; + + function canonicalizeExternal(Float float) external pure returns (Float) { + return float.canonicalize(); + } + + /// Zero at any exponent canonicalizes to FLOAT_ZERO. + function testCanonicalizeZero(int32 exponent) external pure { + Float zero = LibDecimalFloat.packLossless(0, exponent); + Float canonical = zero.canonicalize(); + assertEq(Float.unwrap(canonical), Float.unwrap(LibDecimalFloat.FLOAT_ZERO), "zero canonicalizes to FLOAT_ZERO"); + } + + /// Several different byte representations of the same positive value all + /// canonicalize to the same bytes32. + function testCanonicalizeEqualValuesByteEqual() external pure { + bytes32 expected = Float.unwrap(LibDecimalFloat.packLossless(5, 0).canonicalize()); + assertEq(Float.unwrap(LibDecimalFloat.packLossless(50, -1).canonicalize()), expected, "50e-1"); + assertEq(Float.unwrap(LibDecimalFloat.packLossless(5000, -3).canonicalize()), expected, "5000e-3"); + assertEq(Float.unwrap(LibDecimalFloat.packLossless(5e10, -10).canonicalize()), expected, "5e10 e-10"); + } + + /// Same as above for a negative value. + function testCanonicalizeNegativeEqualValuesByteEqual() external pure { + bytes32 expected = Float.unwrap(LibDecimalFloat.packLossless(-5, 0).canonicalize()); + assertEq(Float.unwrap(LibDecimalFloat.packLossless(-50, -1).canonicalize()), expected, "-50e-1"); + assertEq(Float.unwrap(LibDecimalFloat.packLossless(-5000, -3).canonicalize()), expected, "-5000e-3"); + assertEq(Float.unwrap(LibDecimalFloat.packLossless(-5e10, -10).canonicalize()), expected, "-5e10 e-10"); + } + + /// canonicalize preserves the numeric value. + function testCanonicalizeValuePreserving() external pure { + Float f = LibDecimalFloat.packLossless(1234567890, -3); + assertTrue(f.eq(f.canonicalize()), "value preserved"); + } + + /// canonicalize is idempotent for a concrete value. + function testCanonicalizeIdempotent() external pure { + Float f = LibDecimalFloat.packLossless(42, 7); + Float once = f.canonicalize(); + Float twice = once.canonicalize(); + assertEq(Float.unwrap(once), Float.unwrap(twice), "idempotent"); + } + + /// Fuzz: canonicalize preserves the numeric value for any valid Float. + function testCanonicalizeValuePreservingFuzz(int224 signedCoefficient, int32 exponent) external pure { + Float f = LibDecimalFloat.packLossless(signedCoefficient, exponent); + Float canonical = f.canonicalize(); + assertTrue(f.eq(canonical), "value preserved"); + } + + /// Fuzz: canonicalize is idempotent for any valid Float. + function testCanonicalizeIdempotentFuzz(int224 signedCoefficient, int32 exponent) external pure { + Float f = LibDecimalFloat.packLossless(signedCoefficient, exponent); + Float once = f.canonicalize(); + Float twice = once.canonicalize(); + assertEq(Float.unwrap(once), Float.unwrap(twice), "idempotent"); + } + + /// Fuzz: two Floats that are numerically equal (same coefficient shifted by + /// a power of ten) are byte-equal after canonicalize. + function testCanonicalizeEqImpliesByteEqualFuzz(int128 signedCoefficient, uint8 shift) external pure { + // Avoid the degenerate all-zero case where the shift is irrelevant; it + // is covered separately by testCanonicalizeZero. + vm.assume(signedCoefficient != 0); + + // Base representation. + Float a = LibDecimalFloat.packLossless(signedCoefficient, 0); + + // Shifted representation of the same value: multiply the coefficient by + // 10^shift and lower the exponent by the same amount. int128 * 10^255 + // cannot overflow int256, so packLossless is safe here (it tolerates a + // coefficient that does not fit int224 only via packLossy, but for + // shift large enough to exceed int224 we would need packLossy; keep the + // shift bounded so the shifted coefficient still fits int224). + uint256 boundedShift = shift % 19; + int256 shiftedCoefficient = int256(signedCoefficient) * int256(10 ** boundedShift); + Float b = LibDecimalFloat.packLossless(shiftedCoefficient, -int256(boundedShift)); + + // Same numeric value. + assertTrue(a.eq(b), "same value precondition"); + + // Byte-equal after canonicalize. + assertEq( + Float.unwrap(a.canonicalize()), Float.unwrap(b.canonicalize()), "eq implies byte-equal after canonicalize" + ); + } +} From 0dca9ccdbfce5935e5024e7acd829abead3f40ab Mon Sep 17 00:00:00 2001 From: David Meister Date: Sat, 13 Jun 2026 09:39:06 +0000 Subject: [PATCH 2/3] Add adversarial boundary + no-collision tests for canonicalize The existing canonicalize suite stays inside the int128/uint8 "easy middle": every coefficient fits int224 with room to spare, every exponent is far from int32.min, and the safety property (numerically distinct Floats must canonicalize to byte-UNEQUAL results) is never asserted at all. Add test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol covering: - NO-COLLISION (the documented "eq iff byte-equal" guarantee in its contrapositive): fuzzed over the full int224 coefficient x int32 exponent box and over a narrow band pinned to the int32.min floor, asserting !eq(a,b) implies the canonical bytes differ; plus concrete floor-pinned and int224-cap cases. Kills a mutant that forces the packed exponent to int32.min (which collapses distinct values). - eq-iff-byte-equal in BOTH directions over raw int224/int32 inputs. - Cross-representation uniqueness at the int32.min floor: the same value reached via a floor-pinned rep and a one-step-higher rep must produce identical bytes. Kills an off-by-one mutant on the `exponent > type(int32).min` floor condition that the existing tests miss. - Value preservation + idempotence pinned to the int32.min floor band (coefficient NOT maximised) and to the int224.min/int224.max coefficient edges. Kills mutants that drop the int224 overflow guard or change the x10 scaling step. All tests pass on the unmutated implementation at 5096 fuzz runs and were mutation-validated against four production mutations (drop the int224 guard, off-by-one floor, x100 scaling step, force-floor exponent); each mutation is killed by at least one new test. No production code change. The scaling/floor structure was additionally proved injective and collision-free by exhaustive enumeration over scaled-down int224/int32 analogues, so no bug was found; these tests pin that down at the real boundaries. Co-Authored-By: Claude Opus 4.8 --- ...DecimalFloat.canonicalizeAdversarial.t.sol | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol diff --git a/test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol b/test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol new file mode 100644 index 0000000..821f69d --- /dev/null +++ b/test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity =0.8.25; + +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; + +import {Test} from "forge-std-1.16.1/src/Test.sol"; + +/// Adversarial tests for `canonicalize` that deliberately leave the int128/uint8 +/// "easy middle" the existing suite stays inside. These target the int224 and +/// int32 type boundaries and, crucially, assert the SAFETY property the existing +/// tests never check: numerically DISTINCT Floats must canonicalize to +/// byte-UNEQUAL results (no collision). They also pin the exponent to the +/// int32.min floor where the scaling loop is capped before the coefficient is +/// maximised, and the int224.min/int224.max coefficient boundaries. +contract LibDecimalFloatCanonicalizeAdversarialTest is Test { + using LibDecimalFloat for Float; + + int224 constant INT224_MAX = type(int224).max; + int224 constant INT224_MIN = type(int224).min; + int32 constant INT32_MIN = type(int32).min; + int32 constant INT32_MAX = type(int32).max; + + // --------------------------------------------------------------------- + // NO-COLLISION: numerically distinct -> byte-UNEQUAL after canonicalize. + // --------------------------------------------------------------------- + + /// Concrete no-collision at the int32.min exponent floor. Two tiny values + /// pinned at the floor that differ only in their (already minimal) + /// coefficient must NOT collapse to the same bytes. At the floor the loop + /// cannot scale, so this directly exercises whether distinct floor-pinned + /// coefficients survive distinct. + function testCanonicalizeNoCollisionAtFloorConcrete() external pure { + // 1e(int32.min) and 2e(int32.min): genuinely different numbers. + Float a = LibDecimalFloat.packLossless(1, INT32_MIN); + Float b = LibDecimalFloat.packLossless(2, INT32_MIN); + assertTrue(!a.eq(b), "precondition: distinct values"); + assertTrue( + Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), + "distinct floor-pinned values must not collide" + ); + } + + /// Concrete no-collision where one value's loop is capped by the int224 + /// bound and a neighbouring distinct value's is too. Maximised positive vs. + /// negative of nearly-equal magnitude must not collide. + function testCanonicalizeNoCollisionInt224CapConcrete() external pure { + Float a = LibDecimalFloat.packLossless(7, 0); + Float b = LibDecimalFloat.packLossless(7, 1); // 70, distinct value + Float c = LibDecimalFloat.packLossless(-7, 0); // distinct sign + bytes32 ca = Float.unwrap(a.canonicalize()); + bytes32 cb = Float.unwrap(b.canonicalize()); + bytes32 cc = Float.unwrap(c.canonicalize()); + assertTrue(ca != cb, "7e0 vs 70e0 must not collide"); + assertTrue(ca != cc, "7 vs -7 must not collide"); + assertTrue(cb != cc, "70 vs -7 must not collide"); + } + + /// Fuzz no-collision across the FULL int224/int32 box. Build two Floats from + /// raw boundary-capable inputs; if they are numerically distinct (`!eq`), + /// their canonical forms must be byte-distinct. `eq` is the library's own + /// numeric-equality oracle, so this is exactly the documented guarantee + /// "two Floats are numerically equal iff their canonical forms are + /// byte-equal" applied in the contrapositive. + function testCanonicalizeNoCollisionFuzz(int224 coefficientA, int32 exponentA, int224 coefficientB, int32 exponentB) + external + pure + { + Float a = LibDecimalFloat.packLossless(coefficientA, exponentA); + Float b = LibDecimalFloat.packLossless(coefficientB, exponentB); + if (!a.eq(b)) { + assertTrue( + Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), + "distinct values must canonicalize to distinct bytes" + ); + } + } + + /// Fuzz no-collision focused at the int32.min floor: both operands pinned to + /// a narrow band just above the floor so the loop terminates at the floor + /// for small coefficients, the regime the existing tests never reach. + function testCanonicalizeNoCollisionNearFloorFuzz(int224 coefficientA, int224 coefficientB, uint8 lift) + external + pure + { + // Exponent within [int32.min, int32.min + 255]: the floor band. + int32 exponent = int32(int256(INT32_MIN) + int256(uint256(lift))); + Float a = LibDecimalFloat.packLossless(coefficientA, exponent); + Float b = LibDecimalFloat.packLossless(coefficientB, exponent); + if (!a.eq(b)) { + assertTrue( + Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), + "distinct near-floor values must not collide" + ); + } + } + + // --------------------------------------------------------------------- + // EQ -> BYTE-EQUAL across representations, driven at the FLOOR and the + // int224 cap (the (c) hunt: same value via different starting reps). + // --------------------------------------------------------------------- + + /// Same value reached two different ways, both bottoming out at the floor. + /// 10e(min) and 1e(min+1) are the same number; both must canonicalize to the + /// identical bytes even though one starts already-scaled. + function testCanonicalizeFloorCrossRepConcrete() external pure { + Float a = LibDecimalFloat.packLossless(10, INT32_MIN); + Float b = LibDecimalFloat.packLossless(1, int32(int256(INT32_MIN) + 1)); + assertTrue(a.eq(b), "precondition: same value"); + assertEq( + Float.unwrap(a.canonicalize()), + Float.unwrap(b.canonicalize()), + "same value at floor -> identical canonical bytes" + ); + } + + /// Fuzz: a value whose loop is capped by the floor, reached from two + /// different starting representations, canonicalizes identically. Start with + /// coefficient `base` at `int32.min + k`, and the pre-scaled `base*10^j` at + /// `int32.min + k - j`; both encode the same value and both must hit the + /// same floor-capped canonical form. + function testCanonicalizeFloorCrossRepFuzz(int64 base, uint8 k, uint8 j) external pure { + vm.assume(base != 0); + // Keep both exponents in the floor band and >= int32.min. + uint256 kk = uint256(k) % 30 + 1; // 1..30 + uint256 jj = uint256(j) % kk; // 0..kk-1 so exp stays >= floor + int32 expA = int32(int256(INT32_MIN) + int256(kk)); + // base * 10^jj still fits int224 comfortably (int64 * 10^29 < int224). + int256 scaled = int256(base) * int256(10 ** jj); + int32 expB = int32(int256(INT32_MIN) + int256(kk) - int256(jj)); + + Float a = LibDecimalFloat.packLossless(base, expA); + Float b = LibDecimalFloat.packLossless(scaled, expB); + assertTrue(a.eq(b), "precondition: same value"); + assertEq( + Float.unwrap(a.canonicalize()), Float.unwrap(b.canonicalize()), "cross-rep at floor must be byte-equal" + ); + } + + // --------------------------------------------------------------------- + // VALUE PRESERVATION + IDEMPOTENCE at the int224/int32 boundaries the + // existing suite avoids. + // --------------------------------------------------------------------- + + /// Value preservation at the floor where the coefficient is NOT maximised + /// (the loop is capped by int32.min, leaving a small coefficient). 1e(min) + /// cannot scale at all; the canonical form must still be `eq` to the input + /// and idempotent. + function testCanonicalizeFloorValuePreservedConcrete() external pure { + Float f = LibDecimalFloat.packLossless(123, INT32_MIN); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved at floor"); + // At the floor with a tiny coefficient the loop cannot run, so the form + // is already canonical: the input bytes must equal the canonical bytes. + assertEq(Float.unwrap(c), Float.unwrap(f), "floor-pinned tiny coeff is already canonical"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent at floor"); + } + + /// int224.max / int224.min coefficients: already at the magnitude ceiling so + /// the loop cannot scale (×10 overflows int224). Value-preserving and + /// idempotent, and the input is already its own canonical form. + function testCanonicalizeInt224ExtremesConcrete() external pure { + int224[2] memory extremes = [INT224_MAX, INT224_MIN]; + for (uint256 i = 0; i < extremes.length; i++) { + Float f = LibDecimalFloat.packLossless(extremes[i], 3); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved at int224 extreme"); + assertEq(Float.unwrap(c), Float.unwrap(f), "int224 extreme already canonical"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent at int224 extreme"); + } + } + + /// Fuzz value-preservation + idempotence pinned to the int32.min floor band. + /// This is the regime the existing fuzz (uniform int32 exponent) almost + /// never samples. + function testCanonicalizeFloorValuePreservedFuzz(int224 coefficient, uint16 lift) external pure { + int32 exponent = int32(int256(INT32_MIN) + int256(uint256(lift))); + Float f = LibDecimalFloat.packLossless(coefficient, exponent); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved near floor"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent near floor"); + } + + /// Fuzz value-preservation + idempotence with coefficients pinned to the + /// top/bottom of the int224 range (where the loop terminates immediately or + /// after a single step), across arbitrary int32 exponents. + function testCanonicalizeInt224EdgeValuePreservedFuzz(uint64 delta, bool negative, int32 exponent) external pure { + // Coefficient within `delta` of an int224 extreme. + int256 coefficient = + negative ? int256(INT224_MIN) + int256(uint256(delta)) : int256(INT224_MAX) - int256(uint256(delta)); + Float f = LibDecimalFloat.packLossless(coefficient, exponent); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved near int224 edge"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent near int224 edge"); + } + + /// Fuzz the documented end-to-end guarantee at full boundary width: build two + /// Floats from raw int224/int32 inputs and assert `eq` <=> byte-equal after + /// canonicalize (both directions in one test). The existing eq->byte-equal + /// fuzz only ever feeds equal values; this also feeds unequal ones. + function testCanonicalizeEqIffByteEqualFuzz( + int224 coefficientA, + int32 exponentA, + int224 coefficientB, + int32 exponentB + ) external pure { + Float a = LibDecimalFloat.packLossless(coefficientA, exponentA); + Float b = LibDecimalFloat.packLossless(coefficientB, exponentB); + bool numericallyEqual = a.eq(b); + bool byteEqual = Float.unwrap(a.canonicalize()) == Float.unwrap(b.canonicalize()); + assertEq(numericallyEqual, byteEqual, "eq iff byte-equal after canonicalize"); + } +} From 8398200f7c46cf766dbb6ba250e49243810401a5 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sat, 13 Jun 2026 13:52:04 +0000 Subject: [PATCH 3/3] Consolidate canonicalize tests per-source + expose canonicalize in concrete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework per maintainer review. Requirement 1 — tests organized per source, not a separate "adversarial" file. Moved every test from test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol into the existing source-organized test/src/lib/LibDecimalFloat.canonicalize.t.sol (boundary/no-collision/floor/int224-extreme tests) and deleted the adversarial file. The int224/int32 boundary constants moved with them (dropping the unused INT32_MAX). 19 tests, all passing. Requirement 2 — expose canonicalize in the concrete for deployment + wasm. LibDecimalFloat.canonicalize was internal, so it was dead-code-eliminated from the deployed DecimalFloat contract and unreachable from Rust/JS. Added canonicalize(Float) returns (Float) as an external pure method on src/concrete/DecimalFloat.sol (matching abs/minus). Wired it through the Rust layer: Float::canonicalize in crates/float/src/lib.rs (revm-delegating) and the canonicalize wasm export in crates/float/src/js_api.rs. Added a concrete fuzz test asserting the deployed method matches the lib result, a Rust doctest, and a JS binding test. Regenerated the committed ABI (crates/float/abi/DecimalFloat.json) via CopyArtifacts. Deploy consequence — exposing the method changes DecimalFloat's bytecode, hence its Zoltu address + codehash. Updated the pinned constants in src/lib/deploy/LibDecimalFloatDeploy.sol to the new values: ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS = 0x6b7C246F02E67299b5801f8215d7f40abD82056d DECIMAL_FLOAT_CONTRACT_HASH = 0x61bbc303586dc1b233644acdebe38dcc757907d7b39edcf6b1152c2081cf3197 so testDeployAddress / testExpectedCodeHashDecimalFloat pass against the new bytecode. An on-chain redeploy (manual-sol-artifacts.yaml -f suite=decimal-float) is still required to land the new bytecode at this address; that is a separate human-triggered step. Co-Authored-By: Claude Opus 4.8 --- crates/float/abi/DecimalFloat.json | 23 +- crates/float/src/js_api.rs | 25 ++ crates/float/src/lib.rs | 35 +++ src/concrete/DecimalFloat.sol | 7 + src/lib/deploy/LibDecimalFloatDeploy.sol | 4 +- .../concrete/DecimalFloat.canonicalize.t.sol | 31 +++ .../lib/LibDecimalFloat.canonicalize.t.sol | 204 +++++++++++++++++ ...DecimalFloat.canonicalizeAdversarial.t.sol | 213 ------------------ test_js/float.test.ts | 12 + 9 files changed, 337 insertions(+), 217 deletions(-) create mode 100644 test/src/concrete/DecimalFloat.canonicalize.t.sol delete mode 100644 test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol diff --git a/crates/float/abi/DecimalFloat.json b/crates/float/abi/DecimalFloat.json index 8b5f5b7..07a746e 100644 --- a/crates/float/abi/DecimalFloat.json +++ b/crates/float/abi/DecimalFloat.json @@ -74,6 +74,25 @@ ], "stateMutability": "pure" }, + { + "type": "function", + "name": "canonicalize", + "inputs": [ + { + "name": "a", + "type": "bytes32", + "internalType": "Float" + } + ], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "Float" + } + ], + "stateMutability": "pure" + }, { "type": "function", "name": "ceil", @@ -1102,9 +1121,9 @@ } ], "bytecode": { - "object": "0x6080604052348015600e575f80fd5b5060156019565b60b8565b73c51a14251b0dcf0ae24a96b7153991378938f5f53f7f2573004ac3a9ee7fc8d73654d76386f1b6b99e34cdf86a689c4691e47143420f811460b557604051630912d0ff60e31b815273c51a14251b0dcf0ae24a96b7153991378938f5f560048201527f2573004ac3a9ee7fc8d73654d76386f1b6b99e34cdf86a689c4691e47143420f60248201526044810182905260640160405180910390fd5b50565b614cad806100c55f395ff3fe608060405234801561000f575f80fd5b506004361061029d575f3560e01c806381f7e2f511610171578063cde72ef3116100d2578063dd64691711610088578063e5526ecd1161006e578063e5526ecd1461062e578063e75f991f14610641578063ffae15ba14610654575f80fd5b8063dd64691714610608578063e0db58881461061b575f80fd5b8063d1de592a116100b8578063d1de592a146105bb578063d35273a7146105ce578063d3d6ffa8146105f5575f80fd5b8063cde72ef314610582578063d102b4d3146105a8575f80fd5b8063a19684b711610127578063bc1b392d1161010d578063bc1b392d1461050b578063bc62d8d814610511578063cb09682b1461055c575f80fd5b8063a19684b7146104d2578063a90d041a146104f8575f80fd5b806396ce1ec71161015757806396ce1ec7146104885780639b4afd991461049b578063a100a3d9146104bf575f80fd5b806381f7e2f5146104625780638dc2980714610475575f80fd5b80633447c0301161021b5780635ca0e7a4116101d1578063719cd99d116101b7578063719cd99d1461042957806373bfb2831461043c57806381a822721461044f575f80fd5b80635ca0e7a4146103f0578063602c35fc14610403575f80fd5b80633b3bd868116102015780633b3bd868146103b757806341aa0080146103ca5780635b23771d146103dd575f80fd5b80633447c03014610374578063371493ce14610397575f80fd5b80631ee62f111161027057806328fa1f011161025657806328fa1f011461033b5780633004fa411461034e5780633029740014610361575f80fd5b80631ee62f11146103155780632538835014610328575f80fd5b806304327dc5146102a1578063078b665b146102c75780630b6429bc146102da578063146e82ad14610302575b5f80fd5b6102b46102af36600461462f565b61067a565b6040519081526020015b60405180910390f35b6102b46102d5366004614646565b61068a565b6102ed6102e8366004614676565b61069c565b604080519283529015156020830152016102be565b6102b461031036600461462f565b6106b4565b6102b461032336600461462f565b6106d3565b6102b461033636600461462f565b6106f2565b6102b461034936600461462f565b610711565b6102b461035c366004614646565b61071b565b6102b461036f366004614646565b61073b565b610387610382366004614646565b610746565b60405190151581526020016102be565b6103aa6103a53660046146a0565b610751565b6040516102be91906146d2565b6102b46103c5366004614676565b61075d565b6102b46103d8366004614646565b610768565b6102b46103eb366004614676565b610773565b6102b46103fe36600461462f565b61077e565b7f80000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102b4565b6102b461043736600461462f565b610788565b61038761044a366004614646565b610792565b6102b461045d36600461462f565b61079d565b610387610470366004614646565b6107a7565b610387610483366004614646565b6107b2565b6102b4610496366004614646565b6107bd565b6102b47c090000000000000000000000000000000000000000000000000000000181565b6103aa6104cd36600461462f565b6107c8565b7f80000000000000000000000000000000000000000000000000000000000000016102b4565b6102b4610506366004614646565b610812565b5f6102b4565b61052461051f366004614752565b61081d565b604080517fffffffff0000000000000000000000000000000000000000000000000000000090931683526020830191909152016102be565b7f7fffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffff6102b4565b7f7fffffff800000000000000000000000000000000000000000000000000000006102b4565b6103876105b6366004614646565b610836565b6102b46105c9366004614646565b610841565b6102b47ffffffffc0000000000000000000000000000000000000000000000000000000181565b6102b461060336600461462f565b61084c565b61038761061636600461462f565b610856565b6102b461062936600461462f565b61087c565b6103aa61063c36600461481b565b610886565b6102ed61064f366004614676565b61090f565b7fffffffbe19cfc6ef4f44cf88f14500d013df534fcaad48fca1d5ca47bea26fcc6102b4565b5f6106848261091b565b92915050565b5f610695838361095d565b9392505050565b5f806106a88484610979565b915091505b9250929050565b5f6106848273c51a14251b0dcf0ae24a96b7153991378938f5f56109ba565b5f6106848273c51a14251b0dcf0ae24a96b7153991378938f5f56109e6565b5f6106848273c51a14251b0dcf0ae24a96b7153991378938f5f5610a33565b5f61068482610a66565b5f610695838373c51a14251b0dcf0ae24a96b7153991378938f5f5610aa7565b5f6106958383610d01565b5f6106958383610d63565b60606106958383610db1565b5f6106958383610e44565b5f6106958383610e5f565b5f6106958383610ea4565b5f61068482610ed7565b5f61068482610f8d565b5f6106958383611039565b5f61068482611089565b5f61069583836110ce565b5f610695838361111e565b5f610695838361116d565b6060610684827ffffffffc000000000000000000000000000000000000000000000000000000017c0900000000000000000000000000000000000000000000000000000001610886565b5f61069583836111b2565b5f805f8061082a856111bd565b90969095509350505050565b5f61069583836112ab565b5f61069583836112fa565b5f6106848261133f565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff821615610684565b5f61068482611371565b606061089283836112ab565b6108d7576040517f3be5bf9400000000000000000000000000000000000000000000000000000000815260048101849052602481018390526044015b60405180910390fd5b5f6108e185611089565b9050610906856108f183876112ab565b806109015750610901838661111e565b610db1565b95945050505050565b5f806106a884846113b2565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d61094d82826113f2565b90925090505f6109068383611440565b5f610968838361111e565b6109725781610695565b5090919050565b5f807bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416601b0b60e085901d6109ad8282876114a6565b9350935050509250929050565b5f610695837fffffffff0000000000000000000000000000000000000000000000000000000584610aa7565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a198483836116b0565b90925090505f610a298383611440565b9695505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a19848383611821565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d82610a998383611b56565b9150505f610a298284611440565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff848116601b0b9060e086901d908516610ae45750600191506106959050565b5f8213610b7d57815f03610b4157610afc855f6112ab565b15610b36576040517f8be82972000000000000000000000000000000000000000000000000000000008152600481018690526024016108ce565b505f91506106959050565b6040517fcceba0f100000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016108ce565b610b88856001610d63565b8015610b995750610b99865f61111e565b15610ba8578592505050610695565b610bb2855f6112ab565b15610bdb57610bd2610bc38761091b565b610bcc8761133f565b86610aa7565b92505050610695565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8516601b0b60e086901d5f80610c0e8484611b56565b915091505f610c1e83855f611bbe565b905060015f8080610c558f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116601b0b9160e09190911d90565b915091505b60018510610c9c5784600116600103610c7f57610c7984848484611cd5565b90945092505b600185901c9450610c9282828484611cd5565b9092509050610c5a565b5f80610ca98f8e8e611821565b91509150610cb982828a8d611cd5565b9092509050610cc98f83836116b0565b9092509050610cda82828888611cd5565b90925090505f610cea8383611440565b9e5050505050505050505050505050509392505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686611df4565b915091505f610d558383611440565b9a9950505050505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d610da684848484612418565b979650505050505050565b60607bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d5f829003610e21576040518060400160405280600181526020017f300000000000000000000000000000000000000000000000000000000000000081525092505050610684565b8315610e3a57610e31828261242f565b92505050610684565b61090682826126ff565b5f805f610e518585612e66565b915091506109068282612ec8565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686612f1a565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610906828286612f44565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610f0e57509192915050565b5f80610f1a8484611b56565b915091505f84128015610f2c57508015155b15610f8257610f7d82847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4612f1a565b935091505b5f610da68385611440565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610fc457509192915050565b5f80610fd08484611b56565b91509150805f03610fe5575093949350505050565b5f811315610f8257610f7d82847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4612fa0565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d61107c848484846130d0565b1315979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828212156110c35761094d82826131a4565b5f6109068383611440565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611111848484846130d0565b1215979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611161848484846130d0565b12979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686611cd5565b5f61096883836112ab565b80515f908190602084810191850101828080806111da8686613247565b929650909450925090507fffffffff0000000000000000000000000000000000000000000000000000000084165f0361129c5784830361126c575f8061122084846136e6565b915091508061125b57507f32b8b8be000000000000000000000000000000000000000000000000000000009a5f9a5098505050505050505050565b505f9a909950975050505050505050565b507fad384e8700000000000000000000000000000000000000000000000000000000985f98509650505050505050565b5091975f975095505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d6112ee848484846130d0565b13979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686612fa0565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d61094d82826131a4565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d826113a48383611b56565b5090505f610a298284611440565b5f805f805f6113c187876137ec565b9250925092505f806113d385856136e6565b91509150818380156113e25750815b9650965050505050509250929050565b5f806106a87f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb48686611df4565b5f805f61144d85856136e6565b915091508015801561145d575081155b1561149e576040517f8eba4d0700000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044016108ce565b509392505050565b5f805f8512156114ec576040517f4a7d166b00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044016108ce565b845f036114fe57505f905060016116a8565b8460ff8416850185811215611549576040517fd556b11100000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016108ce565b5f805f8312156115b3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb383121561158a575f8095509550505050506116a8565b825f03600a0a91508184816115a1576115a1614844565b04955050840290911491506116a89050565b5f83131561169d57604d831315611609576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff881660448201526064016108ce565b82600a0a9150817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8161163e5761163e614844565b0484111561168b576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff881660448201526064016108ce565b50919091029250600191506116a89050565b509193506001925050505b935093915050565b5f805f8412156116ec576116c484846131a4565b90945092506116d48585856116b0565b90945092506116e384846113f2565b915091506116a8565b5f806116f88686611b56565b9092509050845f808061170b8585613848565b9194509250905061270d61271061172360018261489e565b851461173a576117348d8686613937565b90925090505b83156117c5575f61174c8660016148c4565b90505b80848583028161176157611761614844565b051461177857600a84059350600a8805975061174f565b6117b961178585886148eb565b8961179087856148eb565b8f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6139f0565b909d509b506117ec9050565b819b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9a505b5050505050866117fd84835f611bbe565b6118088860016148c4565b61181291906148c4565b94509450505050935093915050565b5f80838361182f8282613a8d565b90965094505f86136118b057855f03611874576040517f561fc7b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5d3fd4db00000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016108ce565b5050837f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000036118ee576118e483604c6148c4565b5f915091506116a8565b7f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000008412158061193d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb561195f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb45b5f0b8412611b1a575f805f805f8561197857604b61197b565b604c5b611985908a6148c4565b90505f80876119b2577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e10000000000000000006119d3565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000005b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169050808c81611a0557611a05614844565b05818102955090850193507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18019050611a3e8c82613ae3565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e10000000000000000000295508594508a8414611aca576123278114611aa557611a808c82600101613ae3565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000002611ac7565b7f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000005b94505b50611afa838b848c89897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb46139f0565b909a509850611b0b8a8a835f612fa0565b975097505050505050506116a8565b611b2485856113f2565b9095509350611b34868686611821565b9095509350611b4385856131a4565b92509250506116a8565b50935093915050565b5f805f8312611b6957508290505f6106ad565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4831215611b9b57505f9050826106ad565b5f839003600a0a808581611bb157611bb1614844565b0794859003959350505050565b5f818303611bcd575082610695565b82821315611c1057828203604c811380611be757505f8113155b15611bf5575f915050610695565b80600a0a8581611c0757611c07614844565b05915050610695565b818303604c811380611c2257505f8113155b15611c6a576040517f1d5d6d100000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604481018490526064016108ce565b600a81900a85810286828281611c8257611c82614844565b0514611ccb576040517f1d5d6d100000000000000000000000000000000000000000000000000000000081526004810188905260248101879052604481018690526064016108ce565b9250610695915050565b5f8085158415178015611ced575f92505f9150611dea565b611cf784876148c4565b91505f611d0388613b6a565b90505f611d0f87613b6a565b90505f611d1c8383613bd0565b5090505f6f0785ee10d5da46d900f436a000000000821115611d51576f0785ee10d5da46d900f436a000000000820491506025015b670de0b6b3a7640000821115611d7257670de0b6b3a7640000820491506012015b633b9aca00821115611d8b57633b9aca00820491506009015b612710821115611da057612710820491506004015b8115611db457600a82049150600101611da0565b611dbe81876148c4565b9550611de08b8a611dda8787611dd587600a614a54565b613c0b565b89613cf0565b9097509550505050505b5094509492505050565b5f80835f03611e39576040517f7a97930f00000000000000000000000000000000000000000000000000000000815260048101879052602481018690526044016108ce565b855f03611e4a57505f90508061240f565b5f805f80611e588a8a613df7565b919b5099509150611e698888613df7565b919950975090505f611e7a8b613b6a565b90505f611e868a613b6a565b90507f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000604c818310156122a3578415611ee457507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b612262565b6f4b3b4ca85a86c47a098a22400000000083101561201057678ac7230489e80000831015611f74576402540be400831015611f4257620186a0831015611f325750620186a0905060056121eb565b506402540be4009050600a6121eb565b655af3107a4000831015611f615750655af3107a40009050600e6121eb565b50678ac7230489e80000905060136121eb565b6b204fce5e3e25026110000000831015611fc65769152d02c7e14af6800000831015611faf575069152d02c7e14af6800000905060176121eb565b506b204fce5e3e250261100000009050601c6121eb565b6d314dc6448d9338c15b0a00000000831015611ff557506d314dc6448d9338c15b0a00000000905060216121eb565b506f4b3b4ca85a86c47a098a224000000000905060266121eb565b780197d4df19d605767337e9f14d3eec8920e4000000000000008310156121085773af298d050e4395d69670b12b7f410000000000008310156120a3577172cb5bd86321e38cb6ce6682e8000000000083101561208457507172cb5bd86321e38cb6ce6682e800000000009050602b6121eb565b5073af298d050e4395d69670b12b7f41000000000000905060306121eb565b76010b46c6cdd6e3e0828f4db456ff0c8ea00000000000008310156120e4575076010b46c6cdd6e3e0828f4db456ff0c8ea0000000000000905060356121eb565b50780197d4df19d605767337e9f14d3eec8920e4000000000000009050603a6121eb565b7c03b58e88c75313ec9d329eaaa18fb92f75215b1710000000000000000083101561219e577a026e4d30eccc3215dd8f3157d27e23acbdcfe6800000000000000083101561217657507a026e4d30eccc3215dd8f3157d27e23acbdcfe680000000000000009050603f6121eb565b507c03b58e88c75313ec9d329eaaa18fb92f75215b17100000000000000000905060446121eb565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000008310156121eb57507e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca000000000000000000905060495b81831161221f57600a820491507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016121eb565b815f03612262576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018d9052602481018c90526044016108ce565b856122a3576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018f9052602481018e90526044016108ce565b807f8000000000000000000000000000000000000000000000000000000000000000018d126122d657808d039c50612349565b7f80000000000000000000000000000000000000000000000000000000000000009c90038c015f81131561234957807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038b1361233657998a0199612349565b5f8099509950505050505050505061240f565b5f808e12801561235857505f8c135b15612395577f80000000000000000000000000000000000000000000000000000000000000008e01808d1361238d575f612391565b808d035b9150505b8b818f010397506123b28f8e6123ac888789613c0b565b8b613cf0565b90995097505f8113156123fc57604c8113156123dc575f809a509a5050505050505050505061240f565b80600a0a89816123ee576123ee614844565b059850885f036123fc575f97505b5096985094965061240f95505050505050565b94509492505050565b5f612425858585856130d0565b1495945050505050565b606061243b8383613a8d565b90935091505f8061246c7f161bcca7119915b50764b4abe86529797775a5f171951000000000000000000086614a5f565b1561249c57507f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000009050604c6124c3565b507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b5b5f6124ce8387614a5f565b90505f6124db8488614ac6565b90505f808312156124f5575060016124f283614ad9565b92505b5f82121561250c5750600161250982614ad9565b91505b60408051602081019091525f81528215612615575f8061252d600a89614b09565b90505b61253a8186614a5f565b5f0361255f5761254b600a82614b09565b90508161255781614b1c565b925050612530565b60408051602081019091525f8082525b838110156125c057816040516020016125889190614b6a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052915060010161256f565b505b6125cd600a87614ac6565b5f036125e5576125de600a87614a5f565b95506125c2565b806125ef87614098565b604051602001612600929190614ba2565b60405160208183030381529060405293505050505b5f61261f85614098565b90505f61262c878b6148c4565b90505f81156126625761263e82614098565b60405160200161264e9190614be4565b604051602081830303815290604052612672565b60405180602001604052805f8152505b90505f8561268e5760405180602001604052805f8152506126c5565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b9050808486846040516020016126de9493929190614c15565b6040516020818303038152906040529a505050505050505050505092915050565b60606103e882138061271a57506127176103e8614ad9565b82125b15612754576040517fe44c72b0000000000000000000000000000000000000000000000000000000008152600481018390526024016108ce565b5f80841290811561276f5761276885614ad9565b9050612772565b50835b5f61277c82614124565b80519091505f5b81811080156127ff5750828161279a600185614c35565b6127a49190614c35565b815181106127b4576127b4614c48565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f3000000000000000000000000000000000000000000000000000000000000000145b15612816578061280e81614b1c565b915050612783565b5f6128218284614c35565b90505f61282e838a6148c4565b90505f8761284a5760405180602001604052805f815250612881565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b90505f82126129fb57815f6128968286614c75565b67ffffffffffffffff8111156128ae576128ae614725565b6040519080825280601f01601f1916602001820160405280156128d8576020820181803683370190505b5090505f5b8581101561294a578881815181106128f7576128f7614c48565b602001015160f81c60f81b82828151811061291457612914614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506001016128dd565b505f5b828110156129c7577f3000000000000000000000000000000000000000000000000000000000000000826129818389614c75565b8151811061299157612991614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a90535060010161294d565b5082816040516020016129db929190614c88565b6040516020818303038152906040529a5050505050505050505050610684565b5f612a0583614ad9565b905080841115612c1c575f612a1a8286614c35565b90505f612a28866001614c75565b67ffffffffffffffff811115612a4057612a40614725565b6040519080825280601f01601f191660200182016040528015612a6a576020820181803683370190505b5090505f5b82811015612adc57898181518110612a8957612a89614c48565b602001015160f81c60f81b828281518110612aa657612aa6614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612a6f565b507f2e00000000000000000000000000000000000000000000000000000000000000818381518110612b1057612b10614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b83811015612be75789612b538285614c75565b81518110612b6357612b63614c48565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612b97866001614c75565b612ba19190614c75565b81518110612bb157612bb1614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612b40565b508381604051602001612bfb929190614c88565b6040516020818303038152906040529b505050505050505050505050610684565b5f612c278583614c35565b90505f85612c36836002614c75565b612c409190614c75565b67ffffffffffffffff811115612c5857612c58614725565b6040519080825280601f01601f191660200182016040528015612c82576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000815f81518110612cb857612cb8614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053507f2e0000000000000000000000000000000000000000000000000000000000000081600181518110612d1a57612d1a614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b82811015612dc5577f300000000000000000000000000000000000000000000000000000000000000082612d7f836002614c75565b81518110612d8f57612d8f614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612d4a565b505f5b86811015612be757898181518110612de257612de2614c48565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612e16866002614c75565b612e209190614c75565b81518110612e3057612e30614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612dc8565b5f805f805f612e7587876137ec565b92509250925080612ebc576040517fc471796600000000000000000000000000000000000000000000000000000000815260048101849052602481018390526044016108ce565b50909590945092505050565b5f805f612ed585856136e6565b915091508061149e576040517f22c9f7bb00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044016108ce565b5f80612f2684846131a4565b9094509250612f3786868686612fa0565b9150915094509492505050565b5f805f612f528686866114a6565b9150915080612f97576040517f05e4767800000000000000000000000000000000000000000000000000000000815260048101879052602481018690526044016108ce565b50949350505050565b5f8085158415178015612fcc57865f03612fc0578484925092505061240f565b8686925092505061240f565b612fd68787613a8d565b9097509550612fe58585613a8d565b909550935085841315612ff9579395929492935b838603604c81111561301257878793509350505061240f565b80600a0a868161302457613024614844565b0595505086850180881860ff90811c151589881890911c151680156130bf57877f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff036130a6576040517fd556b111000000000000000000000000000000000000000000000000000000008152600481018a9052602481018990526044016108ce565b600a9687900596909805860197600197909701966130c3565b8198505b5096979596505050505050565b5f80851584151781871282861218178584141780156130f5578685925092505061240f565b505f85841315613109575092949193919260015b8386035f8112604c821317801561313c57821561312e575f899450945050505061240f565b885f9450945050505061240f565b600a82900a8981028a82828161315457613154614844565b0514613180578415613170575f8b96509650505050505061240f565b8a5f96509650505050505061240f565b841561319557889650945061240f9350505050565b955087945061240f9350505050565b5f807f8000000000000000000000000000000000000000000000000000000000000000840361323d577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8303613230576040517fd556b11100000000000000000000000000000000000000000000000000000000815260048101859052602481018490526044016108ce565b600a840593508260010192505b50505f9190910391565b5f82818061325c8386652000000000006141e0565b9250858314158361327681886703ff0000000000006141e0565b94508085036132b057507f34bd20690000000000000000000000000000000000000000000000000000000094505f92508291506136dd9050565b5f806132bc8a88614209565b90925090507fffffffff000000000000000000000000000000000000000000000000000000008216156132fa575095505f93508392506136dd915050565b86519095506540000000000060015f9290921a9190911b16151588871016915050801561358957506001909301925f8461333d81896703ff0000000000006141e0565b955080860361337857507f7bfa48af0000000000000000000000000000000000000000000000000000000095505f93508392506136dd915050565b855b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018051908a1160015f9290921a9190911b6601000000000000161515166001036133e7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161337a565b81811461343e575f806133fa8484614209565b90925090507fffffffff0000000000000000000000000000000000000000000000000000000082161561343a575097505f95508594506136dd9350505050565b9350505b5f83121561347957507f7bfa48af0000000000000000000000000000000000000000000000000000000096505f94508493506136dd92505050565b831561348557825f0392505b80820394505f8513156134c557507f013b2aaa0000000000000000000000000000000000000000000000000000000096505f94508493506136dd92505050565b855f036134d457829550613586565b5f859003604381111561351557507f32b8b8be0000000000000000000000000000000000000000000000000000000097505f95508594506136dd9350505050565b600a0a8681025f8883838161352c5761352c614844565b0514159050601b82900b82141581806135425750805b1561357e57507f32b8b8be000000000000000000000000000000000000000000000000000000009a505f98508897506136dd9650505050505050565b505084019650505b50505b84516c2000000020000000000000000060015f9290921a9190911b1615158786101680156136ce57600190950194856135c9818a652000000000006141e0565b9650866135df818b6703ff0000000000006141e0565b975080880361361c57507f013b2aaa0000000000000000000000000000000000000000000000000000000097505f95508594506136dd9350505050565b505f80613629838a614209565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615613669575097505f95508594506136dd9350505050565b9250508482015f8313801561367d57508581125b8061369157505f8312801561369157508581135b156136ca57507fd556b1110000000000000000000000000000000000000000000000000000000097505f95508594506136dd9350505050565b9450505b845f036136d9575f93505b5050505b92959194509250565b5f601b83900b831483838261374a577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000086051561372a57620186a0860595506005850194505b8586601b0b1461374557600a8605955084600101945061372a565b613760565b855f0361376057505f9250600191506106ad9050565b848560030b146137bc575f85121561378057505f92508291506106ad9050565b6040517fd556b11100000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016108ce565b50507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660e084901b1791509250929050565b5f808060ff841681037f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561383757600a860460018201600a88065f1493509350935050613841565b8593509150600190505b9250925092565b5f805f837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0361388057508391505f90506001613841565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc84121561390e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb08412156138df57505f9150508215156001613841565b5f846004015f03600a0a90505f8187816138fb576138fb614844565b0594505080840286141592509050613841565b5f841261392357505f91508190506001613841565b50505060048101600a0a82025f6001613841565b5f80806064613949600a612328614b09565b613956600a612328614b09565b613961906002614c96565b61396c906001614c75565b6139769190614c75565b6139809190614c75565b90506139c4565b5f8052600280600a8504028301601e833c5f80516107d0840193505f80526001600a8606600a6064880402018501601f853c5f5101949350505050565b6139cf858288613987565b92508315611b4d576139e5600186018288613987565b915050935093915050565b5f80888803613a03575083905081613a81565b5f805f80613a138c8b8f8d612f1a565b915091505f80613a258a8a8d8c612f1a565b91509150613a3584848484611cd5565b8096508197505050505050505f80613a4f8b8b8f8d612f1a565b915091505f80613a6186868686611df4565b915091505f80613a7384848f8e612fa0565b909a50985050505050505050505b97509795505050505050565b5f805f805f613a9c8787613df7565b92509250925080612ebc576040517f05e51ecb00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016108ce565b5f80613af2600a612328614b09565b613afd906002614c96565b613b08906001614c75565b90505f613b18600a612328614b09565b90506002600a8504026001015f8052600281601e883c505f51617fff81169350618000811615613b4757918101915b505f80526001600a8506600a6064870402018301601f873c50505f510192915050565b5f80821215613bc7577f80000000000000000000000000000000000000000000000000000000000000008203613bc157507f8000000000000000000000000000000000000000000000000000000000000000919050565b505f0390565b5090565b919050565b5f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83850993909202808410938190039390930393915050565b5f805f613c188686613bd0565b91509150815f03613c3c57838181613c3257613c32614844565b0492505050610695565b838210613c86576040517f6c59da120000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604481018590526064016108ce565b5f84868809600186198101871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103025f82900382900490920185841190960395909502919093039390930492909217029150509392505050565b5f805f8587181215613dba577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613da757613d4f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001614c75565b8403613d7f57507f800000000000000000000000000000000000000000000000000000000000000090508161240f565b613d8a600a85614b09565b613d9390614ad9565b613d9e8460016148c4565b9150915061240f565b613db084614ad9565b839150915061240f565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613ded57613d93600a85614b09565b508290508161240f565b5f805f845f03613e0f57505f91508190506001613841565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee800000000000000000085055f03614023576f4b3b4ca85a86c47a098a2240000000008505158015613e7857507f80000000000000000000000000000000000000000000000000000000000000268412155b15613e99576f4b3b4ca85a86c47a098a224000000000850294506026840393505b7728c87cb5c89a2571ebfdcb54864ada834a000000000000008505158015613ee157507f80000000000000000000000000000000000000000000000000000000000000138412155b15613efa57678ac7230489e80000850294506013840393505b7b097edd871cfda3a5697758bf0e3cbb5ac5741c6400000000000000008505158015613f4657507f800000000000000000000000000000000000000000000000000000000000000a8412155b15613f5c576402540be40085029450600a840393505b7e3899162693736ac531a5a58f1fbb4b746504382ca7e40000000000000000008505158015613fab57507f80000000000000000000000000000000000000000000000000000000000000028412155b15613fc157606485029450600284039350613f5c565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee8000000000000000000850515801561401157507f80000000000000000000000000000000000000000000000000000000000000018412155b1561402357600a850294506001840393505b600a8086029081058614801561405957507f80000000000000000000000000000000000000000000000000000000000000018512155b15614068578095506001850394505b50939492935050507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000008305151590565b60605f82126140b55760405180602001604052805f8152506140ec565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b6140fd60ff84901d80850118614124565b60405160200161410e929190614c88565b6040516020818303038152906040529050919050565b60605f61413083614333565b60010190505f8167ffffffffffffffff81111561414f5761414f614725565b6040519080825280601f01601f191660200182016040528015614179576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461418357509392505050565b5f5b5f82600186515f1a1b16118385101615614201576001840193506141e2565b509192915050565b81515f90819065200000000000600191831a9190911b161515838510168085019082806142368488614414565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615614271575093505f92506106ad915050565b825f036142d4577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116142a5575f6142c7565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b955093506106ad92505050565b7f80000000000000000000000000000000000000000000000000000000000000008111614301575f614323565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b95505f0393505050509250929050565b5f807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061437b577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106143a7576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106143c557662386f26fc10000830492506010015b6305f5e10083106143dd576305f5e100830492506008015b61271083106143f157612710830492506004015b60648310614403576064830492506002015b600a83106106845760010192915050565b5f8082841061444757507f34bd20690000000000000000000000000000000000000000000000000000000090505f6106ad565b835f03614480576040517fda6966d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60305f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8501815b8782101580156144b85750604d83105b156144fb57815160018401937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90930192600a0a5f9190911a85900302016144a8565b8782106146215781515f1a849003600181111561454357507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106ad9350505050565b600a84900a810282810183111561458657507f0fdc26350000000000000000000000000000000000000000000000000000000096505f95506106ad945050505050565b9190910190507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b8782106146215781515f1a603081146145f657507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106ad9350505050565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101906145b2565b5f9890975095505050505050565b5f6020828403121561463f575f80fd5b5035919050565b5f8060408385031215614657575f80fd5b50508035926020909101359150565b803560ff81168114613bcb575f80fd5b5f8060408385031215614687575f80fd5b8235915061469760208401614666565b90509250929050565b5f80604083850312156146b1575f80fd5b82359150602083013580151581146146c7575f80fd5b809150509250929050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215614762575f80fd5b813567ffffffffffffffff80821115614779575f80fd5b818401915084601f83011261478c575f80fd5b81358181111561479e5761479e614725565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156147e4576147e4614725565b816040528281528760208487010111156147fc575f80fd5b826020860160208301375f928101602001929092525095945050505050565b5f805f6060848603121561482d575f80fd5b505081359360208301359350604090920135919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181035f8312801583831316838312821617156148bd576148bd614871565b5092915050565b8082018281125f8312801582168215821617156148e3576148e3614871565b505092915050565b8082025f82127f80000000000000000000000000000000000000000000000000000000000000008414161561492257614922614871565b818105831482151761068457610684614871565b600181815b8085111561498f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561497557614975614871565b8085161561498257918102915b93841c939080029061493b565b509250929050565b5f826149a557506001610684565b816149b157505f610684565b81600181146149c757600281146149d1576149ed565b6001915050610684565b60ff8411156149e2576149e2614871565b50506001821b610684565b5060208310610133831016604e8410600b8410161715614a10575081810a610684565b614a1a8383614936565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614a4c57614a4c614871565b029392505050565b5f6106958383614997565b5f82614a6d57614a6d614844565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615614ac157614ac1614871565b500590565b5f82614ad457614ad4614844565b500790565b5f7f80000000000000000000000000000000000000000000000000000000000000008203613bc157613bc1614871565b5f82614b1757614b17614844565b500490565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614b4c57614b4c614871565b5060010190565b5f81518060208401855e5f93019283525090919050565b5f614b758284614b53565b7f300000000000000000000000000000000000000000000000000000000000000081526001019392505050565b7f2e0000000000000000000000000000000000000000000000000000000000000081525f614bdc614bd66001840186614b53565b84614b53565b949350505050565b7f650000000000000000000000000000000000000000000000000000000000000081525f6106956001830184614b53565b5f610a29614bd6614c2f614c29858a614b53565b88614b53565b86614b53565b8181038181111561068457610684614871565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b8082018082111561068457610684614871565b5f614bdc614bd68386614b53565b80820281158282048414176106845761068461487156" + "object": "0x6080604052348015600e575f80fd5b5060156019565b60b8565b73c51a14251b0dcf0ae24a96b7153991378938f5f53f7f2573004ac3a9ee7fc8d73654d76386f1b6b99e34cdf86a689c4691e47143420f811460b557604051630912d0ff60e31b815273c51a14251b0dcf0ae24a96b7153991378938f5f560048201527f2573004ac3a9ee7fc8d73654d76386f1b6b99e34cdf86a689c4691e47143420f60248201526044810182905260640160405180910390fd5b50565b614d8c806100c55f395ff3fe608060405234801561000f575f80fd5b50600436106102b7575f3560e01c80638dc2980711610171578063cde72ef3116100d2578063dd64691711610088578063e5526ecd1161006e578063e5526ecd1461065b578063e75f991f1461066e578063ffae15ba14610681575f80fd5b8063dd64691714610635578063e0db588814610648575f80fd5b8063d1de592a116100b8578063d1de592a146105e8578063d35273a7146105fb578063d3d6ffa814610622575f80fd5b8063cde72ef3146105af578063d102b4d3146105d5575f80fd5b8063a90d041a11610127578063bc1b392d1161010d578063bc1b392d14610538578063bc62d8d81461053e578063cb09682b14610589575f80fd5b8063a90d041a14610512578063ac344ead14610525575f80fd5b80639b4afd99116101575780639b4afd99146104b5578063a100a3d9146104d9578063a19684b7146104ec575f80fd5b80638dc298071461048f57806396ce1ec7146104a2575f80fd5b8063371493ce1161021b578063602c35fc116101d157806373bfb283116101b757806373bfb2831461045657806381a822721461046957806381f7e2f51461047c575f80fd5b8063602c35fc1461041d578063719cd99d14610443575f80fd5b806341aa00801161020157806341aa0080146103e45780635b23771d146103f75780635ca0e7a41461040a575f80fd5b8063371493ce146103b15780633b3bd868146103d1575f80fd5b806325388350116102705780633004fa41116102565780633004fa4114610368578063302974001461037b5780633447c0301461038e575f80fd5b8063253883501461034257806328fa1f0114610355575f80fd5b80630b6429bc116102a05780630b6429bc146102f4578063146e82ad1461031c5780631ee62f111461032f575f80fd5b806304327dc5146102bb578063078b665b146102e1575b5f80fd5b6102ce6102c9366004614716565b6106a7565b6040519081526020015b60405180910390f35b6102ce6102ef36600461472d565b6106b7565b61030761030236600461475d565b6106c9565b604080519283529015156020830152016102d8565b6102ce61032a366004614716565b6106e1565b6102ce61033d366004614716565b610700565b6102ce610350366004614716565b61071f565b6102ce610363366004614716565b61073e565b6102ce61037636600461472d565b610748565b6102ce61038936600461472d565b610768565b6103a161039c36600461472d565b610773565b60405190151581526020016102d8565b6103c46103bf366004614787565b61077e565b6040516102d891906147b9565b6102ce6103df36600461475d565b61078a565b6102ce6103f236600461472d565b610795565b6102ce61040536600461475d565b6107a0565b6102ce610418366004614716565b6107ab565b7f80000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102ce565b6102ce610451366004614716565b6107b5565b6103a161046436600461472d565b6107bf565b6102ce610477366004614716565b6107ca565b6103a161048a36600461472d565b6107d4565b6103a161049d36600461472d565b6107df565b6102ce6104b036600461472d565b6107ea565b6102ce7c090000000000000000000000000000000000000000000000000000000181565b6103c46104e7366004614716565b6107f5565b7f80000000000000000000000000000000000000000000000000000000000000016102ce565b6102ce61052036600461472d565b61083f565b6102ce610533366004614716565b61084a565b5f6102ce565b61055161054c366004614839565b610854565b604080517fffffffff0000000000000000000000000000000000000000000000000000000090931683526020830191909152016102d8565b7f7fffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffff6102ce565b7f7fffffff800000000000000000000000000000000000000000000000000000006102ce565b6103a16105e336600461472d565b61086d565b6102ce6105f636600461472d565b610878565b6102ce7ffffffffc0000000000000000000000000000000000000000000000000000000181565b6102ce610630366004614716565b610883565b6103a1610643366004614716565b61088d565b6102ce610656366004614716565b6108b3565b6103c4610669366004614902565b6108bd565b61030761067c36600461475d565b610946565b7fffffffbe19cfc6ef4f44cf88f14500d013df534fcaad48fca1d5ca47bea26fcc6102ce565b5f6106b182610952565b92915050565b5f6106c28383610994565b9392505050565b5f806106d584846109b0565b915091505b9250929050565b5f6106b18273c51a14251b0dcf0ae24a96b7153991378938f5f56109f1565b5f6106b18273c51a14251b0dcf0ae24a96b7153991378938f5f5610a1d565b5f6106b18273c51a14251b0dcf0ae24a96b7153991378938f5f5610a6a565b5f6106b182610a9d565b5f6106c2838373c51a14251b0dcf0ae24a96b7153991378938f5f5610ade565b5f6106c28383610d38565b5f6106c28383610d9a565b60606106c28383610de8565b5f6106c28383610e7b565b5f6106c28383610e96565b5f6106c28383610edb565b5f6106b182610f0e565b5f6106b182610fc4565b5f6106c28383611070565b5f6106b1826110c0565b5f6106c28383611105565b5f6106c28383611155565b5f6106c283836111a4565b60606106b1827ffffffffc000000000000000000000000000000000000000000000000000000017c09000000000000000000000000000000000000000000000000000000016108bd565b5f6106c283836111e9565b5f6106b1826111f4565b5f805f80610861856112a4565b90969095509350505050565b5f6106c28383611392565b5f6106c283836113e1565b5f6106b182611426565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216156106b1565b5f6106b182611458565b60606108c98383611392565b61090e576040517f3be5bf9400000000000000000000000000000000000000000000000000000000815260048101849052602481018390526044015b60405180910390fd5b5f610918856110c0565b905061093d856109288387611392565b8061093857506109388386611155565b610de8565b95945050505050565b5f806106d58484611499565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d61098482826114d9565b90925090505f61093d8383611527565b5f61099f8383611155565b6109a957816106c2565b5090919050565b5f807bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416601b0b60e085901d6109e482828761158d565b9350935050509250929050565b5f6106c2837fffffffff0000000000000000000000000000000000000000000000000000000584610ade565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a50848383611797565b90925090505f610a608383611527565b9695505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a50848383611908565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d82610ad08383611c3d565b9150505f610a608284611527565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff848116601b0b9060e086901d908516610b1b5750600191506106c29050565b5f8213610bb457815f03610b7857610b33855f611392565b15610b6d576040517f8be8297200000000000000000000000000000000000000000000000000000000815260048101869052602401610905565b505f91506106c29050565b6040517fcceba0f10000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610905565b610bbf856001610d9a565b8015610bd05750610bd0865f611155565b15610bdf5785925050506106c2565b610be9855f611392565b15610c1257610c09610bfa87610952565b610c0387611426565b86610ade565b925050506106c2565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8516601b0b60e086901d5f80610c458484611c3d565b915091505f610c5583855f611ca5565b905060015f8080610c8c8f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116601b0b9160e09190911d90565b915091505b60018510610cd35784600116600103610cb657610cb084848484611dbc565b90945092505b600185901c9450610cc982828484611dbc565b9092509050610c91565b5f80610ce08f8e8e611908565b91509150610cf082828a8d611dbc565b9092509050610d008f8383611797565b9092509050610d1182828888611dbc565b90925090505f610d218383611527565b9e5050505050505050505050505050509392505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686611edb565b915091505f610d8c8383611527565b9a9950505050505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d610ddd848484846124ff565b979650505050505050565b60607bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d5f829003610e58576040518060400160405280600181526020017f3000000000000000000000000000000000000000000000000000000000000000815250925050506106b1565b8315610e7157610e688282612516565b925050506106b1565b61093d82826127e6565b5f805f610e888585612f4d565b9150915061093d8282612faf565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686613001565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d61093d82828661302b565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610f4557509192915050565b5f80610f518484611c3d565b915091505f84128015610f6357508015155b15610fb957610fb482847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4613001565b935091505b5f610ddd8385611527565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610ffb57509192915050565b5f806110078484611c3d565b91509150805f0361101c575093949350505050565b5f811315610fb957610fb482847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4613087565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d6110b3848484846131b7565b1315979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828212156110fa57610984828261328b565b5f61093d8383611527565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611148848484846131b7565b1215979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611198848484846131b7565b12979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686611dbc565b5f61099f8383611392565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d81830361122c57505f9392505050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000000081131561129257600a8202601b81900b81146112695750611292565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161122c565b61129c8282612faf565b949350505050565b80515f908190602084810191850101828080806112c1868661332e565b929650909450925090507fffffffff0000000000000000000000000000000000000000000000000000000084165f0361138357848303611353575f8061130784846137cd565b915091508061134257507f32b8b8be000000000000000000000000000000000000000000000000000000009a5f9a5098505050505050505050565b505f9a909950975050505050505050565b507fad384e8700000000000000000000000000000000000000000000000000000000985f98509650505050505050565b5091975f975095505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d6113d5848484846131b7565b13979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686613087565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d610984828261328b565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d8261148b8383611c3d565b5090505f610a608284611527565b5f805f805f6114a887876138d3565b9250925092505f806114ba85856137cd565b91509150818380156114c95750815b9650965050505050509250929050565b5f806106d57f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb48686611edb565b5f805f61153485856137cd565b9150915080158015611544575081155b15611585576040517f8eba4d070000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610905565b509392505050565b5f805f8512156115d3576040517f4a7d166b0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610905565b845f036115e557505f9050600161178f565b8460ff8416850185811215611630576040517fd556b1110000000000000000000000000000000000000000000000000000000081526004810188905260248101879052604401610905565b5f805f83121561169a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb3831215611671575f80955095505050505061178f565b825f03600a0a91508184816116885761168861492b565b049550508402909114915061178f9050565b5f83131561178457604d8313156116f0576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff88166044820152606401610905565b82600a0a9150817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff816117255761172561492b565b04841115611772576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff88166044820152606401610905565b509190910292506001915061178f9050565b509193506001925050505b935093915050565b5f805f8412156117d3576117ab848461328b565b90945092506117bb858585611797565b90945092506117ca84846114d9565b9150915061178f565b5f806117df8686611c3d565b9092509050845f80806117f2858561392f565b9194509250905061270d61271061180a600182614985565b85146118215761181b8d8686613a1e565b90925090505b83156118ac575f6118338660016149ab565b90505b8084858302816118485761184861492b565b051461185f57600a84059350600a88059750611836565b6118a061186c85886149d2565b8961187787856149d2565b8f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc613ad7565b909d509b506118d39050565b819b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9a505b5050505050866118e484835f611ca5565b6118ef8860016149ab565b6118f991906149ab565b94509450505050935093915050565b5f8083836119168282613b74565b90965094505f861361199757855f0361195b576040517f561fc7b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5d3fd4db0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610905565b5050837f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000036119d5576119cb83604c6149ab565b5f9150915061178f565b7f161bcca7119915b50764b4abe86529797775a5f171951000000000000000000084121580611a24577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb5611a46565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb45b5f0b8412611c01575f805f805f85611a5f57604b611a62565b604c5b611a6c908a6149ab565b90505f8087611a99577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e1000000000000000000611aba565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000005b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169050808c81611aec57611aec61492b565b05818102955090850193507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18019050611b258c82613bca565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e10000000000000000000295508594508a8414611bb1576123278114611b8c57611b678c82600101613bca565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000002611bae565b7f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000005b94505b50611be1838b848c89897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4613ad7565b909a509850611bf28a8a835f613087565b9750975050505050505061178f565b611c0b85856114d9565b9095509350611c1b868686611908565b9095509350611c2a858561328b565b925092505061178f565b50935093915050565b5f805f8312611c5057508290505f6106da565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4831215611c8257505f9050826106da565b5f839003600a0a808581611c9857611c9861492b565b0794859003959350505050565b5f818303611cb45750826106c2565b82821315611cf757828203604c811380611cce57505f8113155b15611cdc575f9150506106c2565b80600a0a8581611cee57611cee61492b565b059150506106c2565b818303604c811380611d0957505f8113155b15611d51576040517f1d5d6d10000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610905565b600a81900a85810286828281611d6957611d6961492b565b0514611db2576040517f1d5d6d10000000000000000000000000000000000000000000000000000000008152600481018890526024810187905260448101869052606401610905565b92506106c2915050565b5f8085158415178015611dd4575f92505f9150611ed1565b611dde84876149ab565b91505f611dea88613c51565b90505f611df687613c51565b90505f611e038383613cb7565b5090505f6f0785ee10d5da46d900f436a000000000821115611e38576f0785ee10d5da46d900f436a000000000820491506025015b670de0b6b3a7640000821115611e5957670de0b6b3a7640000820491506012015b633b9aca00821115611e7257633b9aca00820491506009015b612710821115611e8757612710820491506004015b8115611e9b57600a82049150600101611e87565b611ea581876149ab565b9550611ec78b8a611ec18787611ebc87600a614b3b565b613cf2565b89613dd7565b9097509550505050505b5094509492505050565b5f80835f03611f20576040517f7a97930f0000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610905565b855f03611f3157505f9050806124f6565b5f805f80611f3f8a8a613ede565b919b5099509150611f508888613ede565b919950975090505f611f618b613c51565b90505f611f6d8a613c51565b90507f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000604c8183101561238a578415611fcb57507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b612349565b6f4b3b4ca85a86c47a098a2240000000008310156120f757678ac7230489e8000083101561205b576402540be40083101561202957620186a08310156120195750620186a0905060056122d2565b506402540be4009050600a6122d2565b655af3107a40008310156120485750655af3107a40009050600e6122d2565b50678ac7230489e80000905060136122d2565b6b204fce5e3e250261100000008310156120ad5769152d02c7e14af6800000831015612096575069152d02c7e14af6800000905060176122d2565b506b204fce5e3e250261100000009050601c6122d2565b6d314dc6448d9338c15b0a000000008310156120dc57506d314dc6448d9338c15b0a00000000905060216122d2565b506f4b3b4ca85a86c47a098a224000000000905060266122d2565b780197d4df19d605767337e9f14d3eec8920e4000000000000008310156121ef5773af298d050e4395d69670b12b7f4100000000000083101561218a577172cb5bd86321e38cb6ce6682e8000000000083101561216b57507172cb5bd86321e38cb6ce6682e800000000009050602b6122d2565b5073af298d050e4395d69670b12b7f41000000000000905060306122d2565b76010b46c6cdd6e3e0828f4db456ff0c8ea00000000000008310156121cb575076010b46c6cdd6e3e0828f4db456ff0c8ea0000000000000905060356122d2565b50780197d4df19d605767337e9f14d3eec8920e4000000000000009050603a6122d2565b7c03b58e88c75313ec9d329eaaa18fb92f75215b17100000000000000000831015612285577a026e4d30eccc3215dd8f3157d27e23acbdcfe6800000000000000083101561225d57507a026e4d30eccc3215dd8f3157d27e23acbdcfe680000000000000009050603f6122d2565b507c03b58e88c75313ec9d329eaaa18fb92f75215b17100000000000000000905060446122d2565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000008310156122d257507e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca000000000000000000905060495b81831161230657600a820491507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016122d2565b815f03612349576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018d9052602481018c9052604401610905565b8561238a576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018f9052602481018e9052604401610905565b807f8000000000000000000000000000000000000000000000000000000000000000018d126123bd57808d039c50612430565b7f80000000000000000000000000000000000000000000000000000000000000009c90038c015f81131561243057807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038b1361241d57998a0199612430565b5f809950995050505050505050506124f6565b5f808e12801561243f57505f8c135b1561247c577f80000000000000000000000000000000000000000000000000000000000000008e01808d13612474575f612478565b808d035b9150505b8b818f010397506124998f8e612493888789613cf2565b8b613dd7565b90995097505f8113156124e357604c8113156124c3575f809a509a505050505050505050506124f6565b80600a0a89816124d5576124d561492b565b059850885f036124e3575f97505b509698509496506124f695505050505050565b94509492505050565b5f61250c858585856131b7565b1495945050505050565b60606125228383613b74565b90935091505f806125537f161bcca7119915b50764b4abe86529797775a5f171951000000000000000000086614b46565b1561258357507f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000009050604c6125aa565b507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b5b5f6125b58387614b46565b90505f6125c28488614bad565b90505f808312156125dc575060016125d983614bc0565b92505b5f8212156125f3575060016125f082614bc0565b91505b60408051602081019091525f815282156126fc575f80612614600a89614bf0565b90505b6126218186614b46565b5f0361264657612632600a82614bf0565b90508161263e81614c03565b925050612617565b60408051602081019091525f8082525b838110156126a7578160405160200161266f9190614c51565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529150600101612656565b505b6126b4600a87614bad565b5f036126cc576126c5600a87614b46565b95506126a9565b806126d68761417f565b6040516020016126e7929190614c89565b60405160208183030381529060405293505050505b5f6127068561417f565b90505f612713878b6149ab565b90505f8115612749576127258261417f565b6040516020016127359190614cc3565b604051602081830303815290604052612759565b60405180602001604052805f8152505b90505f856127755760405180602001604052805f8152506127ac565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b9050808486846040516020016127c59493929190614cf4565b6040516020818303038152906040529a505050505050505050505092915050565b60606103e882138061280157506127fe6103e8614bc0565b82125b1561283b576040517fe44c72b000000000000000000000000000000000000000000000000000000000815260048101839052602401610905565b5f8084129081156128565761284f85614bc0565b9050612859565b50835b5f6128638261420b565b80519091505f5b81811080156128e657508281612881600185614d14565b61288b9190614d14565b8151811061289b5761289b614d27565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f3000000000000000000000000000000000000000000000000000000000000000145b156128fd57806128f581614c03565b91505061286a565b5f6129088284614d14565b90505f612915838a6149ab565b90505f876129315760405180602001604052805f815250612968565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b90505f8212612ae257815f61297d8286614d54565b67ffffffffffffffff8111156129955761299561480c565b6040519080825280601f01601f1916602001820160405280156129bf576020820181803683370190505b5090505f5b85811015612a31578881815181106129de576129de614d27565b602001015160f81c60f81b8282815181106129fb576129fb614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506001016129c4565b505f5b82811015612aae577f300000000000000000000000000000000000000000000000000000000000000082612a688389614d54565b81518110612a7857612a78614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612a34565b508281604051602001612ac2929190614d67565b6040516020818303038152906040529a50505050505050505050506106b1565b5f612aec83614bc0565b905080841115612d03575f612b018286614d14565b90505f612b0f866001614d54565b67ffffffffffffffff811115612b2757612b2761480c565b6040519080825280601f01601f191660200182016040528015612b51576020820181803683370190505b5090505f5b82811015612bc357898181518110612b7057612b70614d27565b602001015160f81c60f81b828281518110612b8d57612b8d614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612b56565b507f2e00000000000000000000000000000000000000000000000000000000000000818381518110612bf757612bf7614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b83811015612cce5789612c3a8285614d54565b81518110612c4a57612c4a614d27565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612c7e866001614d54565b612c889190614d54565b81518110612c9857612c98614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612c27565b508381604051602001612ce2929190614d67565b6040516020818303038152906040529b5050505050505050505050506106b1565b5f612d0e8583614d14565b90505f85612d1d836002614d54565b612d279190614d54565b67ffffffffffffffff811115612d3f57612d3f61480c565b6040519080825280601f01601f191660200182016040528015612d69576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000815f81518110612d9f57612d9f614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053507f2e0000000000000000000000000000000000000000000000000000000000000081600181518110612e0157612e01614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b82811015612eac577f300000000000000000000000000000000000000000000000000000000000000082612e66836002614d54565b81518110612e7657612e76614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612e31565b505f5b86811015612cce57898181518110612ec957612ec9614d27565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612efd866002614d54565b612f079190614d54565b81518110612f1757612f17614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612eaf565b5f805f805f612f5c87876138d3565b92509250925080612fa3576040517fc47179660000000000000000000000000000000000000000000000000000000081526004810184905260248101839052604401610905565b50909590945092505050565b5f805f612fbc85856137cd565b9150915080611585576040517f22c9f7bb0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610905565b5f8061300d848461328b565b909450925061301e86868686613087565b9150915094509492505050565b5f805f61303986868661158d565b915091508061307e576040517f05e476780000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610905565b50949350505050565b5f80851584151780156130b357865f036130a757848492509250506124f6565b868692509250506124f6565b6130bd8787613b74565b90975095506130cc8585613b74565b9095509350858413156130e0579395929492935b838603604c8111156130f95787879350935050506124f6565b80600a0a868161310b5761310b61492b565b0595505086850180881860ff90811c151589881890911c151680156131a657877f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0361318d576040517fd556b111000000000000000000000000000000000000000000000000000000008152600481018a905260248101899052604401610905565b600a9687900596909805860197600197909701966131aa565b8198505b5096979596505050505050565b5f80851584151781871282861218178584141780156131dc57868592509250506124f6565b505f858413156131f0575092949193919260015b8386035f8112604c8213178015613223578215613215575f89945094505050506124f6565b885f945094505050506124f6565b600a82900a8981028a82828161323b5761323b61492b565b0514613267578415613257575f8b9650965050505050506124f6565b8a5f9650965050505050506124f6565b841561327c5788965094506124f69350505050565b95508794506124f69350505050565b5f807f80000000000000000000000000000000000000000000000000000000000000008403613324577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8303613317576040517fd556b1110000000000000000000000000000000000000000000000000000000081526004810185905260248101849052604401610905565b600a840593508260010192505b50505f9190910391565b5f8281806133438386652000000000006142c7565b9250858314158361335d81886703ff0000000000006142c7565b945080850361339757507f34bd20690000000000000000000000000000000000000000000000000000000094505f92508291506137c49050565b5f806133a38a886142f0565b90925090507fffffffff000000000000000000000000000000000000000000000000000000008216156133e1575095505f93508392506137c4915050565b86519095506540000000000060015f9290921a9190911b16151588871016915050801561367057506001909301925f8461342481896703ff0000000000006142c7565b955080860361345f57507f7bfa48af0000000000000000000000000000000000000000000000000000000095505f93508392506137c4915050565b855b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018051908a1160015f9290921a9190911b6601000000000000161515166001036134ce577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01613461565b818114613525575f806134e184846142f0565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615613521575097505f95508594506137c49350505050565b9350505b5f83121561356057507f7bfa48af0000000000000000000000000000000000000000000000000000000096505f94508493506137c492505050565b831561356c57825f0392505b80820394505f8513156135ac57507f013b2aaa0000000000000000000000000000000000000000000000000000000096505f94508493506137c492505050565b855f036135bb5782955061366d565b5f85900360438111156135fc57507f32b8b8be0000000000000000000000000000000000000000000000000000000097505f95508594506137c49350505050565b600a0a8681025f888383816136135761361361492b565b0514159050601b82900b82141581806136295750805b1561366557507f32b8b8be000000000000000000000000000000000000000000000000000000009a505f98508897506137c49650505050505050565b505084019650505b50505b84516c2000000020000000000000000060015f9290921a9190911b1615158786101680156137b557600190950194856136b0818a652000000000006142c7565b9650866136c6818b6703ff0000000000006142c7565b975080880361370357507f013b2aaa0000000000000000000000000000000000000000000000000000000097505f95508594506137c49350505050565b505f80613710838a6142f0565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615613750575097505f95508594506137c49350505050565b9250508482015f8313801561376457508581125b8061377857505f8312801561377857508581135b156137b157507fd556b1110000000000000000000000000000000000000000000000000000000097505f95508594506137c49350505050565b9450505b845f036137c0575f93505b5050505b92959194509250565b5f601b83900b8314838382613831577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000086051561381157620186a0860595506005850194505b8586601b0b1461382c57600a86059550846001019450613811565b613847565b855f0361384757505f9250600191506106da9050565b848560030b146138a3575f85121561386757505f92508291506106da9050565b6040517fd556b1110000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610905565b50507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660e084901b1791509250929050565b5f808060ff841681037f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561391e57600a860460018201600a88065f1493509350935050613928565b8593509150600190505b9250925092565b5f805f837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0361396757508391505f90506001613928565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8412156139f5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb08412156139c657505f9150508215156001613928565b5f846004015f03600a0a90505f8187816139e2576139e261492b565b0594505080840286141592509050613928565b5f8412613a0a57505f91508190506001613928565b50505060048101600a0a82025f6001613928565b5f80806064613a30600a612328614bf0565b613a3d600a612328614bf0565b613a48906002614d75565b613a53906001614d54565b613a5d9190614d54565b613a679190614d54565b9050613aab565b5f8052600280600a8504028301601e833c5f80516107d0840193505f80526001600a8606600a6064880402018501601f853c5f5101949350505050565b613ab6858288613a6e565b92508315611c3457613acc600186018288613a6e565b915050935093915050565b5f80888803613aea575083905081613b68565b5f805f80613afa8c8b8f8d613001565b915091505f80613b0c8a8a8d8c613001565b91509150613b1c84848484611dbc565b8096508197505050505050505f80613b368b8b8f8d613001565b915091505f80613b4886868686611edb565b915091505f80613b5a84848f8e613087565b909a50985050505050505050505b97509795505050505050565b5f805f805f613b838787613ede565b92509250925080612fa3576040517f05e51ecb0000000000000000000000000000000000000000000000000000000081526004810188905260248101879052604401610905565b5f80613bd9600a612328614bf0565b613be4906002614d75565b613bef906001614d54565b90505f613bff600a612328614bf0565b90506002600a8504026001015f8052600281601e883c505f51617fff81169350618000811615613c2e57918101915b505f80526001600a8506600a6064870402018301601f873c50505f510192915050565b5f80821215613cae577f80000000000000000000000000000000000000000000000000000000000000008203613ca857507f8000000000000000000000000000000000000000000000000000000000000000919050565b505f0390565b5090565b919050565b5f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83850993909202808410938190039390930393915050565b5f805f613cff8686613cb7565b91509150815f03613d2357838181613d1957613d1961492b565b04925050506106c2565b838210613d6d576040517f6c59da12000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610905565b5f84868809600186198101871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103025f82900382900490920185841190960395909502919093039390930492909217029150509392505050565b5f805f8587181215613ea1577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613e8e57613e367f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001614d54565b8403613e6657507f80000000000000000000000000000000000000000000000000000000000000009050816124f6565b613e71600a85614bf0565b613e7a90614bc0565b613e858460016149ab565b915091506124f6565b613e9784614bc0565b83915091506124f6565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613ed457613e7a600a85614bf0565b50829050816124f6565b5f805f845f03613ef657505f91508190506001613928565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee800000000000000000085055f0361410a576f4b3b4ca85a86c47a098a2240000000008505158015613f5f57507f80000000000000000000000000000000000000000000000000000000000000268412155b15613f80576f4b3b4ca85a86c47a098a224000000000850294506026840393505b7728c87cb5c89a2571ebfdcb54864ada834a000000000000008505158015613fc857507f80000000000000000000000000000000000000000000000000000000000000138412155b15613fe157678ac7230489e80000850294506013840393505b7b097edd871cfda3a5697758bf0e3cbb5ac5741c640000000000000000850515801561402d57507f800000000000000000000000000000000000000000000000000000000000000a8412155b15614043576402540be40085029450600a840393505b7e3899162693736ac531a5a58f1fbb4b746504382ca7e4000000000000000000850515801561409257507f80000000000000000000000000000000000000000000000000000000000000028412155b156140a857606485029450600284039350614043565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee800000000000000000085051580156140f857507f80000000000000000000000000000000000000000000000000000000000000018412155b1561410a57600a850294506001840393505b600a8086029081058614801561414057507f80000000000000000000000000000000000000000000000000000000000000018512155b1561414f578095506001850394505b50939492935050507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000008305151590565b60605f821261419c5760405180602001604052805f8152506141d3565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b6141e460ff84901d8085011861420b565b6040516020016141f5929190614d67565b6040516020818303038152906040529050919050565b60605f6142178361441a565b60010190505f8167ffffffffffffffff8111156142365761423661480c565b6040519080825280601f01601f191660200182016040528015614260576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461426a57509392505050565b5f5b5f82600186515f1a1b161183851016156142e8576001840193506142c9565b509192915050565b81515f90819065200000000000600191831a9190911b1615158385101680850190828061431d84886144fb565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615614358575093505f92506106da915050565b825f036143bb577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161438c575f6143ae565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b955093506106da92505050565b7f800000000000000000000000000000000000000000000000000000000000000081116143e8575f61440a565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b95505f0393505050509250929050565b5f807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310614462577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef8100000000831061448e576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144ac57662386f26fc10000830492506010015b6305f5e10083106144c4576305f5e100830492506008015b61271083106144d857612710830492506004015b606483106144ea576064830492506002015b600a83106106b15760010192915050565b5f8082841061452e57507f34bd20690000000000000000000000000000000000000000000000000000000090505f6106da565b835f03614567576040517fda6966d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60305f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8501815b87821015801561459f5750604d83105b156145e257815160018401937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90930192600a0a5f9190911a859003020161458f565b8782106147085781515f1a849003600181111561462a57507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106da9350505050565b600a84900a810282810183111561466d57507f0fdc26350000000000000000000000000000000000000000000000000000000096505f95506106da945050505050565b9190910190507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b8782106147085781515f1a603081146146dd57507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106da9350505050565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90910190614699565b5f9890975095505050505050565b5f60208284031215614726575f80fd5b5035919050565b5f806040838503121561473e575f80fd5b50508035926020909101359150565b803560ff81168114613cb2575f80fd5b5f806040838503121561476e575f80fd5b8235915061477e6020840161474d565b90509250929050565b5f8060408385031215614798575f80fd5b82359150602083013580151581146147ae575f80fd5b809150509250929050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215614849575f80fd5b813567ffffffffffffffff80821115614860575f80fd5b818401915084601f830112614873575f80fd5b8135818111156148855761488561480c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156148cb576148cb61480c565b816040528281528760208487010111156148e3575f80fd5b826020860160208301375f928101602001929092525095945050505050565b5f805f60608486031215614914575f80fd5b505081359360208301359350604090920135919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181035f8312801583831316838312821617156149a4576149a4614958565b5092915050565b8082018281125f8312801582168215821617156149ca576149ca614958565b505092915050565b8082025f82127f800000000000000000000000000000000000000000000000000000000000000084141615614a0957614a09614958565b81810583148215176106b1576106b1614958565b600181815b80851115614a7657817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614a5c57614a5c614958565b80851615614a6957918102915b93841c9390800290614a22565b509250929050565b5f82614a8c575060016106b1565b81614a9857505f6106b1565b8160018114614aae5760028114614ab857614ad4565b60019150506106b1565b60ff841115614ac957614ac9614958565b50506001821b6106b1565b5060208310610133831016604e8410600b8410161715614af7575081810a6106b1565b614b018383614a1d565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614b3357614b33614958565b029392505050565b5f6106c28383614a7e565b5f82614b5457614b5461492b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615614ba857614ba8614958565b500590565b5f82614bbb57614bbb61492b565b500790565b5f7f80000000000000000000000000000000000000000000000000000000000000008203613ca857613ca8614958565b5f82614bfe57614bfe61492b565b500490565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614c3357614c33614958565b5060010190565b5f81518060208401855e5f93019283525090919050565b5f614c5c8284614c3a565b7f300000000000000000000000000000000000000000000000000000000000000081526001019392505050565b7f2e0000000000000000000000000000000000000000000000000000000000000081525f61129c614cbd6001840186614c3a565b84614c3a565b7f650000000000000000000000000000000000000000000000000000000000000081525f6106c26001830184614c3a565b5f610a60614cbd614d0e614d08858a614c3a565b88614c3a565b86614c3a565b818103818111156106b1576106b1614958565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b808201808211156106b1576106b1614958565b5f61129c614cbd8386614c3a565b80820281158282048414176106b1576106b161495856" }, "deployedBytecode": { - "object": "0x608060405234801561000f575f80fd5b506004361061029d575f3560e01c806381f7e2f511610171578063cde72ef3116100d2578063dd64691711610088578063e5526ecd1161006e578063e5526ecd1461062e578063e75f991f14610641578063ffae15ba14610654575f80fd5b8063dd64691714610608578063e0db58881461061b575f80fd5b8063d1de592a116100b8578063d1de592a146105bb578063d35273a7146105ce578063d3d6ffa8146105f5575f80fd5b8063cde72ef314610582578063d102b4d3146105a8575f80fd5b8063a19684b711610127578063bc1b392d1161010d578063bc1b392d1461050b578063bc62d8d814610511578063cb09682b1461055c575f80fd5b8063a19684b7146104d2578063a90d041a146104f8575f80fd5b806396ce1ec71161015757806396ce1ec7146104885780639b4afd991461049b578063a100a3d9146104bf575f80fd5b806381f7e2f5146104625780638dc2980714610475575f80fd5b80633447c0301161021b5780635ca0e7a4116101d1578063719cd99d116101b7578063719cd99d1461042957806373bfb2831461043c57806381a822721461044f575f80fd5b80635ca0e7a4146103f0578063602c35fc14610403575f80fd5b80633b3bd868116102015780633b3bd868146103b757806341aa0080146103ca5780635b23771d146103dd575f80fd5b80633447c03014610374578063371493ce14610397575f80fd5b80631ee62f111161027057806328fa1f011161025657806328fa1f011461033b5780633004fa411461034e5780633029740014610361575f80fd5b80631ee62f11146103155780632538835014610328575f80fd5b806304327dc5146102a1578063078b665b146102c75780630b6429bc146102da578063146e82ad14610302575b5f80fd5b6102b46102af36600461462f565b61067a565b6040519081526020015b60405180910390f35b6102b46102d5366004614646565b61068a565b6102ed6102e8366004614676565b61069c565b604080519283529015156020830152016102be565b6102b461031036600461462f565b6106b4565b6102b461032336600461462f565b6106d3565b6102b461033636600461462f565b6106f2565b6102b461034936600461462f565b610711565b6102b461035c366004614646565b61071b565b6102b461036f366004614646565b61073b565b610387610382366004614646565b610746565b60405190151581526020016102be565b6103aa6103a53660046146a0565b610751565b6040516102be91906146d2565b6102b46103c5366004614676565b61075d565b6102b46103d8366004614646565b610768565b6102b46103eb366004614676565b610773565b6102b46103fe36600461462f565b61077e565b7f80000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102b4565b6102b461043736600461462f565b610788565b61038761044a366004614646565b610792565b6102b461045d36600461462f565b61079d565b610387610470366004614646565b6107a7565b610387610483366004614646565b6107b2565b6102b4610496366004614646565b6107bd565b6102b47c090000000000000000000000000000000000000000000000000000000181565b6103aa6104cd36600461462f565b6107c8565b7f80000000000000000000000000000000000000000000000000000000000000016102b4565b6102b4610506366004614646565b610812565b5f6102b4565b61052461051f366004614752565b61081d565b604080517fffffffff0000000000000000000000000000000000000000000000000000000090931683526020830191909152016102be565b7f7fffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffff6102b4565b7f7fffffff800000000000000000000000000000000000000000000000000000006102b4565b6103876105b6366004614646565b610836565b6102b46105c9366004614646565b610841565b6102b47ffffffffc0000000000000000000000000000000000000000000000000000000181565b6102b461060336600461462f565b61084c565b61038761061636600461462f565b610856565b6102b461062936600461462f565b61087c565b6103aa61063c36600461481b565b610886565b6102ed61064f366004614676565b61090f565b7fffffffbe19cfc6ef4f44cf88f14500d013df534fcaad48fca1d5ca47bea26fcc6102b4565b5f6106848261091b565b92915050565b5f610695838361095d565b9392505050565b5f806106a88484610979565b915091505b9250929050565b5f6106848273c51a14251b0dcf0ae24a96b7153991378938f5f56109ba565b5f6106848273c51a14251b0dcf0ae24a96b7153991378938f5f56109e6565b5f6106848273c51a14251b0dcf0ae24a96b7153991378938f5f5610a33565b5f61068482610a66565b5f610695838373c51a14251b0dcf0ae24a96b7153991378938f5f5610aa7565b5f6106958383610d01565b5f6106958383610d63565b60606106958383610db1565b5f6106958383610e44565b5f6106958383610e5f565b5f6106958383610ea4565b5f61068482610ed7565b5f61068482610f8d565b5f6106958383611039565b5f61068482611089565b5f61069583836110ce565b5f610695838361111e565b5f610695838361116d565b6060610684827ffffffffc000000000000000000000000000000000000000000000000000000017c0900000000000000000000000000000000000000000000000000000001610886565b5f61069583836111b2565b5f805f8061082a856111bd565b90969095509350505050565b5f61069583836112ab565b5f61069583836112fa565b5f6106848261133f565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff821615610684565b5f61068482611371565b606061089283836112ab565b6108d7576040517f3be5bf9400000000000000000000000000000000000000000000000000000000815260048101849052602481018390526044015b60405180910390fd5b5f6108e185611089565b9050610906856108f183876112ab565b806109015750610901838661111e565b610db1565b95945050505050565b5f806106a884846113b2565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d61094d82826113f2565b90925090505f6109068383611440565b5f610968838361111e565b6109725781610695565b5090919050565b5f807bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416601b0b60e085901d6109ad8282876114a6565b9350935050509250929050565b5f610695837fffffffff0000000000000000000000000000000000000000000000000000000584610aa7565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a198483836116b0565b90925090505f610a298383611440565b9695505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a19848383611821565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d82610a998383611b56565b9150505f610a298284611440565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff848116601b0b9060e086901d908516610ae45750600191506106959050565b5f8213610b7d57815f03610b4157610afc855f6112ab565b15610b36576040517f8be82972000000000000000000000000000000000000000000000000000000008152600481018690526024016108ce565b505f91506106959050565b6040517fcceba0f100000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016108ce565b610b88856001610d63565b8015610b995750610b99865f61111e565b15610ba8578592505050610695565b610bb2855f6112ab565b15610bdb57610bd2610bc38761091b565b610bcc8761133f565b86610aa7565b92505050610695565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8516601b0b60e086901d5f80610c0e8484611b56565b915091505f610c1e83855f611bbe565b905060015f8080610c558f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116601b0b9160e09190911d90565b915091505b60018510610c9c5784600116600103610c7f57610c7984848484611cd5565b90945092505b600185901c9450610c9282828484611cd5565b9092509050610c5a565b5f80610ca98f8e8e611821565b91509150610cb982828a8d611cd5565b9092509050610cc98f83836116b0565b9092509050610cda82828888611cd5565b90925090505f610cea8383611440565b9e5050505050505050505050505050509392505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686611df4565b915091505f610d558383611440565b9a9950505050505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d610da684848484612418565b979650505050505050565b60607bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d5f829003610e21576040518060400160405280600181526020017f300000000000000000000000000000000000000000000000000000000000000081525092505050610684565b8315610e3a57610e31828261242f565b92505050610684565b61090682826126ff565b5f805f610e518585612e66565b915091506109068282612ec8565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686612f1a565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610906828286612f44565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610f0e57509192915050565b5f80610f1a8484611b56565b915091505f84128015610f2c57508015155b15610f8257610f7d82847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4612f1a565b935091505b5f610da68385611440565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610fc457509192915050565b5f80610fd08484611b56565b91509150805f03610fe5575093949350505050565b5f811315610f8257610f7d82847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4612fa0565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d61107c848484846130d0565b1315979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828212156110c35761094d82826131a4565b5f6109068383611440565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611111848484846130d0565b1215979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611161848484846130d0565b12979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686611cd5565b5f61096883836112ab565b80515f908190602084810191850101828080806111da8686613247565b929650909450925090507fffffffff0000000000000000000000000000000000000000000000000000000084165f0361129c5784830361126c575f8061122084846136e6565b915091508061125b57507f32b8b8be000000000000000000000000000000000000000000000000000000009a5f9a5098505050505050505050565b505f9a909950975050505050505050565b507fad384e8700000000000000000000000000000000000000000000000000000000985f98509650505050505050565b5091975f975095505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d6112ee848484846130d0565b13979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d4686868686612fa0565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d61094d82826131a4565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d826113a48383611b56565b5090505f610a298284611440565b5f805f805f6113c187876137ec565b9250925092505f806113d385856136e6565b91509150818380156113e25750815b9650965050505050509250929050565b5f806106a87f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb48686611df4565b5f805f61144d85856136e6565b915091508015801561145d575081155b1561149e576040517f8eba4d0700000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044016108ce565b509392505050565b5f805f8512156114ec576040517f4a7d166b00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044016108ce565b845f036114fe57505f905060016116a8565b8460ff8416850185811215611549576040517fd556b11100000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016108ce565b5f805f8312156115b3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb383121561158a575f8095509550505050506116a8565b825f03600a0a91508184816115a1576115a1614844565b04955050840290911491506116a89050565b5f83131561169d57604d831315611609576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff881660448201526064016108ce565b82600a0a9150817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8161163e5761163e614844565b0484111561168b576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff881660448201526064016108ce565b50919091029250600191506116a89050565b509193506001925050505b935093915050565b5f805f8412156116ec576116c484846131a4565b90945092506116d48585856116b0565b90945092506116e384846113f2565b915091506116a8565b5f806116f88686611b56565b9092509050845f808061170b8585613848565b9194509250905061270d61271061172360018261489e565b851461173a576117348d8686613937565b90925090505b83156117c5575f61174c8660016148c4565b90505b80848583028161176157611761614844565b051461177857600a84059350600a8805975061174f565b6117b961178585886148eb565b8961179087856148eb565b8f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6139f0565b909d509b506117ec9050565b819b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9a505b5050505050866117fd84835f611bbe565b6118088860016148c4565b61181291906148c4565b94509450505050935093915050565b5f80838361182f8282613a8d565b90965094505f86136118b057855f03611874576040517f561fc7b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5d3fd4db00000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016108ce565b5050837f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000036118ee576118e483604c6148c4565b5f915091506116a8565b7f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000008412158061193d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb561195f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb45b5f0b8412611b1a575f805f805f8561197857604b61197b565b604c5b611985908a6148c4565b90505f80876119b2577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e10000000000000000006119d3565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000005b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169050808c81611a0557611a05614844565b05818102955090850193507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18019050611a3e8c82613ae3565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e10000000000000000000295508594508a8414611aca576123278114611aa557611a808c82600101613ae3565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000002611ac7565b7f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000005b94505b50611afa838b848c89897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb46139f0565b909a509850611b0b8a8a835f612fa0565b975097505050505050506116a8565b611b2485856113f2565b9095509350611b34868686611821565b9095509350611b4385856131a4565b92509250506116a8565b50935093915050565b5f805f8312611b6957508290505f6106ad565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4831215611b9b57505f9050826106ad565b5f839003600a0a808581611bb157611bb1614844565b0794859003959350505050565b5f818303611bcd575082610695565b82821315611c1057828203604c811380611be757505f8113155b15611bf5575f915050610695565b80600a0a8581611c0757611c07614844565b05915050610695565b818303604c811380611c2257505f8113155b15611c6a576040517f1d5d6d100000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604481018490526064016108ce565b600a81900a85810286828281611c8257611c82614844565b0514611ccb576040517f1d5d6d100000000000000000000000000000000000000000000000000000000081526004810188905260248101879052604481018690526064016108ce565b9250610695915050565b5f8085158415178015611ced575f92505f9150611dea565b611cf784876148c4565b91505f611d0388613b6a565b90505f611d0f87613b6a565b90505f611d1c8383613bd0565b5090505f6f0785ee10d5da46d900f436a000000000821115611d51576f0785ee10d5da46d900f436a000000000820491506025015b670de0b6b3a7640000821115611d7257670de0b6b3a7640000820491506012015b633b9aca00821115611d8b57633b9aca00820491506009015b612710821115611da057612710820491506004015b8115611db457600a82049150600101611da0565b611dbe81876148c4565b9550611de08b8a611dda8787611dd587600a614a54565b613c0b565b89613cf0565b9097509550505050505b5094509492505050565b5f80835f03611e39576040517f7a97930f00000000000000000000000000000000000000000000000000000000815260048101879052602481018690526044016108ce565b855f03611e4a57505f90508061240f565b5f805f80611e588a8a613df7565b919b5099509150611e698888613df7565b919950975090505f611e7a8b613b6a565b90505f611e868a613b6a565b90507f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000604c818310156122a3578415611ee457507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b612262565b6f4b3b4ca85a86c47a098a22400000000083101561201057678ac7230489e80000831015611f74576402540be400831015611f4257620186a0831015611f325750620186a0905060056121eb565b506402540be4009050600a6121eb565b655af3107a4000831015611f615750655af3107a40009050600e6121eb565b50678ac7230489e80000905060136121eb565b6b204fce5e3e25026110000000831015611fc65769152d02c7e14af6800000831015611faf575069152d02c7e14af6800000905060176121eb565b506b204fce5e3e250261100000009050601c6121eb565b6d314dc6448d9338c15b0a00000000831015611ff557506d314dc6448d9338c15b0a00000000905060216121eb565b506f4b3b4ca85a86c47a098a224000000000905060266121eb565b780197d4df19d605767337e9f14d3eec8920e4000000000000008310156121085773af298d050e4395d69670b12b7f410000000000008310156120a3577172cb5bd86321e38cb6ce6682e8000000000083101561208457507172cb5bd86321e38cb6ce6682e800000000009050602b6121eb565b5073af298d050e4395d69670b12b7f41000000000000905060306121eb565b76010b46c6cdd6e3e0828f4db456ff0c8ea00000000000008310156120e4575076010b46c6cdd6e3e0828f4db456ff0c8ea0000000000000905060356121eb565b50780197d4df19d605767337e9f14d3eec8920e4000000000000009050603a6121eb565b7c03b58e88c75313ec9d329eaaa18fb92f75215b1710000000000000000083101561219e577a026e4d30eccc3215dd8f3157d27e23acbdcfe6800000000000000083101561217657507a026e4d30eccc3215dd8f3157d27e23acbdcfe680000000000000009050603f6121eb565b507c03b58e88c75313ec9d329eaaa18fb92f75215b17100000000000000000905060446121eb565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000008310156121eb57507e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca000000000000000000905060495b81831161221f57600a820491507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016121eb565b815f03612262576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018d9052602481018c90526044016108ce565b856122a3576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018f9052602481018e90526044016108ce565b807f8000000000000000000000000000000000000000000000000000000000000000018d126122d657808d039c50612349565b7f80000000000000000000000000000000000000000000000000000000000000009c90038c015f81131561234957807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038b1361233657998a0199612349565b5f8099509950505050505050505061240f565b5f808e12801561235857505f8c135b15612395577f80000000000000000000000000000000000000000000000000000000000000008e01808d1361238d575f612391565b808d035b9150505b8b818f010397506123b28f8e6123ac888789613c0b565b8b613cf0565b90995097505f8113156123fc57604c8113156123dc575f809a509a5050505050505050505061240f565b80600a0a89816123ee576123ee614844565b059850885f036123fc575f97505b5096985094965061240f95505050505050565b94509492505050565b5f612425858585856130d0565b1495945050505050565b606061243b8383613a8d565b90935091505f8061246c7f161bcca7119915b50764b4abe86529797775a5f171951000000000000000000086614a5f565b1561249c57507f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000009050604c6124c3565b507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b5b5f6124ce8387614a5f565b90505f6124db8488614ac6565b90505f808312156124f5575060016124f283614ad9565b92505b5f82121561250c5750600161250982614ad9565b91505b60408051602081019091525f81528215612615575f8061252d600a89614b09565b90505b61253a8186614a5f565b5f0361255f5761254b600a82614b09565b90508161255781614b1c565b925050612530565b60408051602081019091525f8082525b838110156125c057816040516020016125889190614b6a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052915060010161256f565b505b6125cd600a87614ac6565b5f036125e5576125de600a87614a5f565b95506125c2565b806125ef87614098565b604051602001612600929190614ba2565b60405160208183030381529060405293505050505b5f61261f85614098565b90505f61262c878b6148c4565b90505f81156126625761263e82614098565b60405160200161264e9190614be4565b604051602081830303815290604052612672565b60405180602001604052805f8152505b90505f8561268e5760405180602001604052805f8152506126c5565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b9050808486846040516020016126de9493929190614c15565b6040516020818303038152906040529a505050505050505050505092915050565b60606103e882138061271a57506127176103e8614ad9565b82125b15612754576040517fe44c72b0000000000000000000000000000000000000000000000000000000008152600481018390526024016108ce565b5f80841290811561276f5761276885614ad9565b9050612772565b50835b5f61277c82614124565b80519091505f5b81811080156127ff5750828161279a600185614c35565b6127a49190614c35565b815181106127b4576127b4614c48565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f3000000000000000000000000000000000000000000000000000000000000000145b15612816578061280e81614b1c565b915050612783565b5f6128218284614c35565b90505f61282e838a6148c4565b90505f8761284a5760405180602001604052805f815250612881565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b90505f82126129fb57815f6128968286614c75565b67ffffffffffffffff8111156128ae576128ae614725565b6040519080825280601f01601f1916602001820160405280156128d8576020820181803683370190505b5090505f5b8581101561294a578881815181106128f7576128f7614c48565b602001015160f81c60f81b82828151811061291457612914614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506001016128dd565b505f5b828110156129c7577f3000000000000000000000000000000000000000000000000000000000000000826129818389614c75565b8151811061299157612991614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a90535060010161294d565b5082816040516020016129db929190614c88565b6040516020818303038152906040529a5050505050505050505050610684565b5f612a0583614ad9565b905080841115612c1c575f612a1a8286614c35565b90505f612a28866001614c75565b67ffffffffffffffff811115612a4057612a40614725565b6040519080825280601f01601f191660200182016040528015612a6a576020820181803683370190505b5090505f5b82811015612adc57898181518110612a8957612a89614c48565b602001015160f81c60f81b828281518110612aa657612aa6614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612a6f565b507f2e00000000000000000000000000000000000000000000000000000000000000818381518110612b1057612b10614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b83811015612be75789612b538285614c75565b81518110612b6357612b63614c48565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612b97866001614c75565b612ba19190614c75565b81518110612bb157612bb1614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612b40565b508381604051602001612bfb929190614c88565b6040516020818303038152906040529b505050505050505050505050610684565b5f612c278583614c35565b90505f85612c36836002614c75565b612c409190614c75565b67ffffffffffffffff811115612c5857612c58614725565b6040519080825280601f01601f191660200182016040528015612c82576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000815f81518110612cb857612cb8614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053507f2e0000000000000000000000000000000000000000000000000000000000000081600181518110612d1a57612d1a614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b82811015612dc5577f300000000000000000000000000000000000000000000000000000000000000082612d7f836002614c75565b81518110612d8f57612d8f614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612d4a565b505f5b86811015612be757898181518110612de257612de2614c48565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612e16866002614c75565b612e209190614c75565b81518110612e3057612e30614c48565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612dc8565b5f805f805f612e7587876137ec565b92509250925080612ebc576040517fc471796600000000000000000000000000000000000000000000000000000000815260048101849052602481018390526044016108ce565b50909590945092505050565b5f805f612ed585856136e6565b915091508061149e576040517f22c9f7bb00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044016108ce565b5f80612f2684846131a4565b9094509250612f3786868686612fa0565b9150915094509492505050565b5f805f612f528686866114a6565b9150915080612f97576040517f05e4767800000000000000000000000000000000000000000000000000000000815260048101879052602481018690526044016108ce565b50949350505050565b5f8085158415178015612fcc57865f03612fc0578484925092505061240f565b8686925092505061240f565b612fd68787613a8d565b9097509550612fe58585613a8d565b909550935085841315612ff9579395929492935b838603604c81111561301257878793509350505061240f565b80600a0a868161302457613024614844565b0595505086850180881860ff90811c151589881890911c151680156130bf57877f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff036130a6576040517fd556b111000000000000000000000000000000000000000000000000000000008152600481018a9052602481018990526044016108ce565b600a9687900596909805860197600197909701966130c3565b8198505b5096979596505050505050565b5f80851584151781871282861218178584141780156130f5578685925092505061240f565b505f85841315613109575092949193919260015b8386035f8112604c821317801561313c57821561312e575f899450945050505061240f565b885f9450945050505061240f565b600a82900a8981028a82828161315457613154614844565b0514613180578415613170575f8b96509650505050505061240f565b8a5f96509650505050505061240f565b841561319557889650945061240f9350505050565b955087945061240f9350505050565b5f807f8000000000000000000000000000000000000000000000000000000000000000840361323d577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8303613230576040517fd556b11100000000000000000000000000000000000000000000000000000000815260048101859052602481018490526044016108ce565b600a840593508260010192505b50505f9190910391565b5f82818061325c8386652000000000006141e0565b9250858314158361327681886703ff0000000000006141e0565b94508085036132b057507f34bd20690000000000000000000000000000000000000000000000000000000094505f92508291506136dd9050565b5f806132bc8a88614209565b90925090507fffffffff000000000000000000000000000000000000000000000000000000008216156132fa575095505f93508392506136dd915050565b86519095506540000000000060015f9290921a9190911b16151588871016915050801561358957506001909301925f8461333d81896703ff0000000000006141e0565b955080860361337857507f7bfa48af0000000000000000000000000000000000000000000000000000000095505f93508392506136dd915050565b855b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018051908a1160015f9290921a9190911b6601000000000000161515166001036133e7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161337a565b81811461343e575f806133fa8484614209565b90925090507fffffffff0000000000000000000000000000000000000000000000000000000082161561343a575097505f95508594506136dd9350505050565b9350505b5f83121561347957507f7bfa48af0000000000000000000000000000000000000000000000000000000096505f94508493506136dd92505050565b831561348557825f0392505b80820394505f8513156134c557507f013b2aaa0000000000000000000000000000000000000000000000000000000096505f94508493506136dd92505050565b855f036134d457829550613586565b5f859003604381111561351557507f32b8b8be0000000000000000000000000000000000000000000000000000000097505f95508594506136dd9350505050565b600a0a8681025f8883838161352c5761352c614844565b0514159050601b82900b82141581806135425750805b1561357e57507f32b8b8be000000000000000000000000000000000000000000000000000000009a505f98508897506136dd9650505050505050565b505084019650505b50505b84516c2000000020000000000000000060015f9290921a9190911b1615158786101680156136ce57600190950194856135c9818a652000000000006141e0565b9650866135df818b6703ff0000000000006141e0565b975080880361361c57507f013b2aaa0000000000000000000000000000000000000000000000000000000097505f95508594506136dd9350505050565b505f80613629838a614209565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615613669575097505f95508594506136dd9350505050565b9250508482015f8313801561367d57508581125b8061369157505f8312801561369157508581135b156136ca57507fd556b1110000000000000000000000000000000000000000000000000000000097505f95508594506136dd9350505050565b9450505b845f036136d9575f93505b5050505b92959194509250565b5f601b83900b831483838261374a577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000086051561372a57620186a0860595506005850194505b8586601b0b1461374557600a8605955084600101945061372a565b613760565b855f0361376057505f9250600191506106ad9050565b848560030b146137bc575f85121561378057505f92508291506106ad9050565b6040517fd556b11100000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016108ce565b50507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660e084901b1791509250929050565b5f808060ff841681037f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561383757600a860460018201600a88065f1493509350935050613841565b8593509150600190505b9250925092565b5f805f837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0361388057508391505f90506001613841565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc84121561390e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb08412156138df57505f9150508215156001613841565b5f846004015f03600a0a90505f8187816138fb576138fb614844565b0594505080840286141592509050613841565b5f841261392357505f91508190506001613841565b50505060048101600a0a82025f6001613841565b5f80806064613949600a612328614b09565b613956600a612328614b09565b613961906002614c96565b61396c906001614c75565b6139769190614c75565b6139809190614c75565b90506139c4565b5f8052600280600a8504028301601e833c5f80516107d0840193505f80526001600a8606600a6064880402018501601f853c5f5101949350505050565b6139cf858288613987565b92508315611b4d576139e5600186018288613987565b915050935093915050565b5f80888803613a03575083905081613a81565b5f805f80613a138c8b8f8d612f1a565b915091505f80613a258a8a8d8c612f1a565b91509150613a3584848484611cd5565b8096508197505050505050505f80613a4f8b8b8f8d612f1a565b915091505f80613a6186868686611df4565b915091505f80613a7384848f8e612fa0565b909a50985050505050505050505b97509795505050505050565b5f805f805f613a9c8787613df7565b92509250925080612ebc576040517f05e51ecb00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016108ce565b5f80613af2600a612328614b09565b613afd906002614c96565b613b08906001614c75565b90505f613b18600a612328614b09565b90506002600a8504026001015f8052600281601e883c505f51617fff81169350618000811615613b4757918101915b505f80526001600a8506600a6064870402018301601f873c50505f510192915050565b5f80821215613bc7577f80000000000000000000000000000000000000000000000000000000000000008203613bc157507f8000000000000000000000000000000000000000000000000000000000000000919050565b505f0390565b5090565b919050565b5f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83850993909202808410938190039390930393915050565b5f805f613c188686613bd0565b91509150815f03613c3c57838181613c3257613c32614844565b0492505050610695565b838210613c86576040517f6c59da120000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604481018590526064016108ce565b5f84868809600186198101871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103025f82900382900490920185841190960395909502919093039390930492909217029150509392505050565b5f805f8587181215613dba577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613da757613d4f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001614c75565b8403613d7f57507f800000000000000000000000000000000000000000000000000000000000000090508161240f565b613d8a600a85614b09565b613d9390614ad9565b613d9e8460016148c4565b9150915061240f565b613db084614ad9565b839150915061240f565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613ded57613d93600a85614b09565b508290508161240f565b5f805f845f03613e0f57505f91508190506001613841565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee800000000000000000085055f03614023576f4b3b4ca85a86c47a098a2240000000008505158015613e7857507f80000000000000000000000000000000000000000000000000000000000000268412155b15613e99576f4b3b4ca85a86c47a098a224000000000850294506026840393505b7728c87cb5c89a2571ebfdcb54864ada834a000000000000008505158015613ee157507f80000000000000000000000000000000000000000000000000000000000000138412155b15613efa57678ac7230489e80000850294506013840393505b7b097edd871cfda3a5697758bf0e3cbb5ac5741c6400000000000000008505158015613f4657507f800000000000000000000000000000000000000000000000000000000000000a8412155b15613f5c576402540be40085029450600a840393505b7e3899162693736ac531a5a58f1fbb4b746504382ca7e40000000000000000008505158015613fab57507f80000000000000000000000000000000000000000000000000000000000000028412155b15613fc157606485029450600284039350613f5c565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee8000000000000000000850515801561401157507f80000000000000000000000000000000000000000000000000000000000000018412155b1561402357600a850294506001840393505b600a8086029081058614801561405957507f80000000000000000000000000000000000000000000000000000000000000018512155b15614068578095506001850394505b50939492935050507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000008305151590565b60605f82126140b55760405180602001604052805f8152506140ec565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b6140fd60ff84901d80850118614124565b60405160200161410e929190614c88565b6040516020818303038152906040529050919050565b60605f61413083614333565b60010190505f8167ffffffffffffffff81111561414f5761414f614725565b6040519080825280601f01601f191660200182016040528015614179576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461418357509392505050565b5f5b5f82600186515f1a1b16118385101615614201576001840193506141e2565b509192915050565b81515f90819065200000000000600191831a9190911b161515838510168085019082806142368488614414565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615614271575093505f92506106ad915050565b825f036142d4577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116142a5575f6142c7565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b955093506106ad92505050565b7f80000000000000000000000000000000000000000000000000000000000000008111614301575f614323565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b95505f0393505050509250929050565b5f807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061437b577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106143a7576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106143c557662386f26fc10000830492506010015b6305f5e10083106143dd576305f5e100830492506008015b61271083106143f157612710830492506004015b60648310614403576064830492506002015b600a83106106845760010192915050565b5f8082841061444757507f34bd20690000000000000000000000000000000000000000000000000000000090505f6106ad565b835f03614480576040517fda6966d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60305f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8501815b8782101580156144b85750604d83105b156144fb57815160018401937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90930192600a0a5f9190911a85900302016144a8565b8782106146215781515f1a849003600181111561454357507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106ad9350505050565b600a84900a810282810183111561458657507f0fdc26350000000000000000000000000000000000000000000000000000000096505f95506106ad945050505050565b9190910190507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b8782106146215781515f1a603081146145f657507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106ad9350505050565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101906145b2565b5f9890975095505050505050565b5f6020828403121561463f575f80fd5b5035919050565b5f8060408385031215614657575f80fd5b50508035926020909101359150565b803560ff81168114613bcb575f80fd5b5f8060408385031215614687575f80fd5b8235915061469760208401614666565b90509250929050565b5f80604083850312156146b1575f80fd5b82359150602083013580151581146146c7575f80fd5b809150509250929050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215614762575f80fd5b813567ffffffffffffffff80821115614779575f80fd5b818401915084601f83011261478c575f80fd5b81358181111561479e5761479e614725565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156147e4576147e4614725565b816040528281528760208487010111156147fc575f80fd5b826020860160208301375f928101602001929092525095945050505050565b5f805f6060848603121561482d575f80fd5b505081359360208301359350604090920135919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181035f8312801583831316838312821617156148bd576148bd614871565b5092915050565b8082018281125f8312801582168215821617156148e3576148e3614871565b505092915050565b8082025f82127f80000000000000000000000000000000000000000000000000000000000000008414161561492257614922614871565b818105831482151761068457610684614871565b600181815b8085111561498f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561497557614975614871565b8085161561498257918102915b93841c939080029061493b565b509250929050565b5f826149a557506001610684565b816149b157505f610684565b81600181146149c757600281146149d1576149ed565b6001915050610684565b60ff8411156149e2576149e2614871565b50506001821b610684565b5060208310610133831016604e8410600b8410161715614a10575081810a610684565b614a1a8383614936565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614a4c57614a4c614871565b029392505050565b5f6106958383614997565b5f82614a6d57614a6d614844565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615614ac157614ac1614871565b500590565b5f82614ad457614ad4614844565b500790565b5f7f80000000000000000000000000000000000000000000000000000000000000008203613bc157613bc1614871565b5f82614b1757614b17614844565b500490565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614b4c57614b4c614871565b5060010190565b5f81518060208401855e5f93019283525090919050565b5f614b758284614b53565b7f300000000000000000000000000000000000000000000000000000000000000081526001019392505050565b7f2e0000000000000000000000000000000000000000000000000000000000000081525f614bdc614bd66001840186614b53565b84614b53565b949350505050565b7f650000000000000000000000000000000000000000000000000000000000000081525f6106956001830184614b53565b5f610a29614bd6614c2f614c29858a614b53565b88614b53565b86614b53565b8181038181111561068457610684614871565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b8082018082111561068457610684614871565b5f614bdc614bd68386614b53565b80820281158282048414176106845761068461487156" + "object": "0x608060405234801561000f575f80fd5b50600436106102b7575f3560e01c80638dc2980711610171578063cde72ef3116100d2578063dd64691711610088578063e5526ecd1161006e578063e5526ecd1461065b578063e75f991f1461066e578063ffae15ba14610681575f80fd5b8063dd64691714610635578063e0db588814610648575f80fd5b8063d1de592a116100b8578063d1de592a146105e8578063d35273a7146105fb578063d3d6ffa814610622575f80fd5b8063cde72ef3146105af578063d102b4d3146105d5575f80fd5b8063a90d041a11610127578063bc1b392d1161010d578063bc1b392d14610538578063bc62d8d81461053e578063cb09682b14610589575f80fd5b8063a90d041a14610512578063ac344ead14610525575f80fd5b80639b4afd99116101575780639b4afd99146104b5578063a100a3d9146104d9578063a19684b7146104ec575f80fd5b80638dc298071461048f57806396ce1ec7146104a2575f80fd5b8063371493ce1161021b578063602c35fc116101d157806373bfb283116101b757806373bfb2831461045657806381a822721461046957806381f7e2f51461047c575f80fd5b8063602c35fc1461041d578063719cd99d14610443575f80fd5b806341aa00801161020157806341aa0080146103e45780635b23771d146103f75780635ca0e7a41461040a575f80fd5b8063371493ce146103b15780633b3bd868146103d1575f80fd5b806325388350116102705780633004fa41116102565780633004fa4114610368578063302974001461037b5780633447c0301461038e575f80fd5b8063253883501461034257806328fa1f0114610355575f80fd5b80630b6429bc116102a05780630b6429bc146102f4578063146e82ad1461031c5780631ee62f111461032f575f80fd5b806304327dc5146102bb578063078b665b146102e1575b5f80fd5b6102ce6102c9366004614716565b6106a7565b6040519081526020015b60405180910390f35b6102ce6102ef36600461472d565b6106b7565b61030761030236600461475d565b6106c9565b604080519283529015156020830152016102d8565b6102ce61032a366004614716565b6106e1565b6102ce61033d366004614716565b610700565b6102ce610350366004614716565b61071f565b6102ce610363366004614716565b61073e565b6102ce61037636600461472d565b610748565b6102ce61038936600461472d565b610768565b6103a161039c36600461472d565b610773565b60405190151581526020016102d8565b6103c46103bf366004614787565b61077e565b6040516102d891906147b9565b6102ce6103df36600461475d565b61078a565b6102ce6103f236600461472d565b610795565b6102ce61040536600461475d565b6107a0565b6102ce610418366004614716565b6107ab565b7f80000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102ce565b6102ce610451366004614716565b6107b5565b6103a161046436600461472d565b6107bf565b6102ce610477366004614716565b6107ca565b6103a161048a36600461472d565b6107d4565b6103a161049d36600461472d565b6107df565b6102ce6104b036600461472d565b6107ea565b6102ce7c090000000000000000000000000000000000000000000000000000000181565b6103c46104e7366004614716565b6107f5565b7f80000000000000000000000000000000000000000000000000000000000000016102ce565b6102ce61052036600461472d565b61083f565b6102ce610533366004614716565b61084a565b5f6102ce565b61055161054c366004614839565b610854565b604080517fffffffff0000000000000000000000000000000000000000000000000000000090931683526020830191909152016102d8565b7f7fffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffff6102ce565b7f7fffffff800000000000000000000000000000000000000000000000000000006102ce565b6103a16105e336600461472d565b61086d565b6102ce6105f636600461472d565b610878565b6102ce7ffffffffc0000000000000000000000000000000000000000000000000000000181565b6102ce610630366004614716565b610883565b6103a1610643366004614716565b61088d565b6102ce610656366004614716565b6108b3565b6103c4610669366004614902565b6108bd565b61030761067c36600461475d565b610946565b7fffffffbe19cfc6ef4f44cf88f14500d013df534fcaad48fca1d5ca47bea26fcc6102ce565b5f6106b182610952565b92915050565b5f6106c28383610994565b9392505050565b5f806106d584846109b0565b915091505b9250929050565b5f6106b18273c51a14251b0dcf0ae24a96b7153991378938f5f56109f1565b5f6106b18273c51a14251b0dcf0ae24a96b7153991378938f5f5610a1d565b5f6106b18273c51a14251b0dcf0ae24a96b7153991378938f5f5610a6a565b5f6106b182610a9d565b5f6106c2838373c51a14251b0dcf0ae24a96b7153991378938f5f5610ade565b5f6106c28383610d38565b5f6106c28383610d9a565b60606106c28383610de8565b5f6106c28383610e7b565b5f6106c28383610e96565b5f6106c28383610edb565b5f6106b182610f0e565b5f6106b182610fc4565b5f6106c28383611070565b5f6106b1826110c0565b5f6106c28383611105565b5f6106c28383611155565b5f6106c283836111a4565b60606106b1827ffffffffc000000000000000000000000000000000000000000000000000000017c09000000000000000000000000000000000000000000000000000000016108bd565b5f6106c283836111e9565b5f6106b1826111f4565b5f805f80610861856112a4565b90969095509350505050565b5f6106c28383611392565b5f6106c283836113e1565b5f6106b182611426565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216156106b1565b5f6106b182611458565b60606108c98383611392565b61090e576040517f3be5bf9400000000000000000000000000000000000000000000000000000000815260048101849052602481018390526044015b60405180910390fd5b5f610918856110c0565b905061093d856109288387611392565b8061093857506109388386611155565b610de8565b95945050505050565b5f806106d58484611499565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d61098482826114d9565b90925090505f61093d8383611527565b5f61099f8383611155565b6109a957816106c2565b5090919050565b5f807bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416601b0b60e085901d6109e482828761158d565b9350935050509250929050565b5f6106c2837fffffffff0000000000000000000000000000000000000000000000000000000584610ade565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a50848383611797565b90925090505f610a608383611527565b9695505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d610a50848383611908565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d82610ad08383611c3d565b9150505f610a608284611527565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff848116601b0b9060e086901d908516610b1b5750600191506106c29050565b5f8213610bb457815f03610b7857610b33855f611392565b15610b6d576040517f8be8297200000000000000000000000000000000000000000000000000000000815260048101869052602401610905565b505f91506106c29050565b6040517fcceba0f10000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610905565b610bbf856001610d9a565b8015610bd05750610bd0865f611155565b15610bdf5785925050506106c2565b610be9855f611392565b15610c1257610c09610bfa87610952565b610c0387611426565b86610ade565b925050506106c2565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8516601b0b60e086901d5f80610c458484611c3d565b915091505f610c5583855f611ca5565b905060015f8080610c8c8f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116601b0b9160e09190911d90565b915091505b60018510610cd35784600116600103610cb657610cb084848484611dbc565b90945092505b600185901c9450610cc982828484611dbc565b9092509050610c91565b5f80610ce08f8e8e611908565b91509150610cf082828a8d611dbc565b9092509050610d008f8383611797565b9092509050610d1182828888611dbc565b90925090505f610d218383611527565b9e5050505050505050505050505050509392505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686611edb565b915091505f610d8c8383611527565b9a9950505050505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d610ddd848484846124ff565b979650505050505050565b60607bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d5f829003610e58576040518060400160405280600181526020017f3000000000000000000000000000000000000000000000000000000000000000815250925050506106b1565b8315610e7157610e688282612516565b925050506106b1565b61093d82826127e6565b5f805f610e888585612f4d565b9150915061093d8282612faf565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686613001565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b0b60e084901d61093d82828661302b565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610f4557509192915050565b5f80610f518484611c3d565b915091505f84128015610f6357508015155b15610fb957610fb482847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4613001565b935091505b5f610ddd8385611527565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828112610ffb57509192915050565b5f806110078484611c3d565b91509150805f0361101c575093949350505050565b5f811315610fb957610fb482847f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4613087565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d6110b3848484846131b7565b1315979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d828212156110fa57610984828261328b565b5f61093d8383611527565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611148848484846131b7565b1215979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d611198848484846131b7565b12979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686611dbc565b5f61099f8383611392565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d81830361122c57505f9392505050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000000081131561129257600a8202601b81900b81146112695750611292565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161122c565b61129c8282612faf565b949350505050565b80515f908190602084810191850101828080806112c1868661332e565b929650909450925090507fffffffff0000000000000000000000000000000000000000000000000000000084165f0361138357848303611353575f8061130784846137cd565b915091508061134257507f32b8b8be000000000000000000000000000000000000000000000000000000009a5f9a5098505050505050505050565b505f9a909950975050505050505050565b507fad384e8700000000000000000000000000000000000000000000000000000000985f98509650505050505050565b5091975f975095505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d6113d5848484846131b7565b13979650505050505050565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838116601b90810b9160e086811d9291861690910b9085901d8480610d7d86868686613087565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d610984828261328b565b5f7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8216601b0b60e083901d8261148b8383611c3d565b5090505f610a608284611527565b5f805f805f6114a887876138d3565b9250925092505f806114ba85856137cd565b91509150818380156114c95750815b9650965050505050509250929050565b5f806106d57f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb48686611edb565b5f805f61153485856137cd565b9150915080158015611544575081155b15611585576040517f8eba4d070000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610905565b509392505050565b5f805f8512156115d3576040517f4a7d166b0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610905565b845f036115e557505f9050600161178f565b8460ff8416850185811215611630576040517fd556b1110000000000000000000000000000000000000000000000000000000081526004810188905260248101879052604401610905565b5f805f83121561169a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb3831215611671575f80955095505050505061178f565b825f03600a0a91508184816116885761168861492b565b049550508402909114915061178f9050565b5f83131561178457604d8313156116f0576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff88166044820152606401610905565b82600a0a9150817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff816117255761172561492b565b04841115611772576040517fc849483b000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905260ff88166044820152606401610905565b509190910292506001915061178f9050565b509193506001925050505b935093915050565b5f805f8412156117d3576117ab848461328b565b90945092506117bb858585611797565b90945092506117ca84846114d9565b9150915061178f565b5f806117df8686611c3d565b9092509050845f80806117f2858561392f565b9194509250905061270d61271061180a600182614985565b85146118215761181b8d8686613a1e565b90925090505b83156118ac575f6118338660016149ab565b90505b8084858302816118485761184861492b565b051461185f57600a84059350600a88059750611836565b6118a061186c85886149d2565b8961187787856149d2565b8f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc613ad7565b909d509b506118d39050565b819b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9a505b5050505050866118e484835f611ca5565b6118ef8860016149ab565b6118f991906149ab565b94509450505050935093915050565b5f8083836119168282613b74565b90965094505f861361199757855f0361195b576040517f561fc7b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5d3fd4db0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610905565b5050837f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000036119d5576119cb83604c6149ab565b5f9150915061178f565b7f161bcca7119915b50764b4abe86529797775a5f171951000000000000000000084121580611a24577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb5611a46565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb45b5f0b8412611c01575f805f805f85611a5f57604b611a62565b604c5b611a6c908a6149ab565b90505f8087611a99577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e1000000000000000000611aba565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000005b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169050808c81611aec57611aec61492b565b05818102955090850193507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18019050611b258c82613bca565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e10000000000000000000295508594508a8414611bb1576123278114611b8c57611b678c82600101613bca565b7d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000002611bae565b7f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000005b94505b50611be1838b848c89897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4613ad7565b909a509850611bf28a8a835f613087565b9750975050505050505061178f565b611c0b85856114d9565b9095509350611c1b868686611908565b9095509350611c2a858561328b565b925092505061178f565b50935093915050565b5f805f8312611c5057508290505f6106da565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb4831215611c8257505f9050826106da565b5f839003600a0a808581611c9857611c9861492b565b0794859003959350505050565b5f818303611cb45750826106c2565b82821315611cf757828203604c811380611cce57505f8113155b15611cdc575f9150506106c2565b80600a0a8581611cee57611cee61492b565b059150506106c2565b818303604c811380611d0957505f8113155b15611d51576040517f1d5d6d10000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610905565b600a81900a85810286828281611d6957611d6961492b565b0514611db2576040517f1d5d6d10000000000000000000000000000000000000000000000000000000008152600481018890526024810187905260448101869052606401610905565b92506106c2915050565b5f8085158415178015611dd4575f92505f9150611ed1565b611dde84876149ab565b91505f611dea88613c51565b90505f611df687613c51565b90505f611e038383613cb7565b5090505f6f0785ee10d5da46d900f436a000000000821115611e38576f0785ee10d5da46d900f436a000000000820491506025015b670de0b6b3a7640000821115611e5957670de0b6b3a7640000820491506012015b633b9aca00821115611e7257633b9aca00820491506009015b612710821115611e8757612710820491506004015b8115611e9b57600a82049150600101611e87565b611ea581876149ab565b9550611ec78b8a611ec18787611ebc87600a614b3b565b613cf2565b89613dd7565b9097509550505050505b5094509492505050565b5f80835f03611f20576040517f7a97930f0000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610905565b855f03611f3157505f9050806124f6565b5f805f80611f3f8a8a613ede565b919b5099509150611f508888613ede565b919950975090505f611f618b613c51565b90505f611f6d8a613c51565b90507f161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000604c8183101561238a578415611fcb57507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b612349565b6f4b3b4ca85a86c47a098a2240000000008310156120f757678ac7230489e8000083101561205b576402540be40083101561202957620186a08310156120195750620186a0905060056122d2565b506402540be4009050600a6122d2565b655af3107a40008310156120485750655af3107a40009050600e6122d2565b50678ac7230489e80000905060136122d2565b6b204fce5e3e250261100000008310156120ad5769152d02c7e14af6800000831015612096575069152d02c7e14af6800000905060176122d2565b506b204fce5e3e250261100000009050601c6122d2565b6d314dc6448d9338c15b0a000000008310156120dc57506d314dc6448d9338c15b0a00000000905060216122d2565b506f4b3b4ca85a86c47a098a224000000000905060266122d2565b780197d4df19d605767337e9f14d3eec8920e4000000000000008310156121ef5773af298d050e4395d69670b12b7f4100000000000083101561218a577172cb5bd86321e38cb6ce6682e8000000000083101561216b57507172cb5bd86321e38cb6ce6682e800000000009050602b6122d2565b5073af298d050e4395d69670b12b7f41000000000000905060306122d2565b76010b46c6cdd6e3e0828f4db456ff0c8ea00000000000008310156121cb575076010b46c6cdd6e3e0828f4db456ff0c8ea0000000000000905060356122d2565b50780197d4df19d605767337e9f14d3eec8920e4000000000000009050603a6122d2565b7c03b58e88c75313ec9d329eaaa18fb92f75215b17100000000000000000831015612285577a026e4d30eccc3215dd8f3157d27e23acbdcfe6800000000000000083101561225d57507a026e4d30eccc3215dd8f3157d27e23acbdcfe680000000000000009050603f6122d2565b507c03b58e88c75313ec9d329eaaa18fb92f75215b17100000000000000000905060446122d2565b7e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca0000000000000000008310156122d257507e05a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca000000000000000000905060495b81831161230657600a820491507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016122d2565b815f03612349576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018d9052602481018c9052604401610905565b8561238a576040517f05e51ecb000000000000000000000000000000000000000000000000000000008152600481018f9052602481018e9052604401610905565b807f8000000000000000000000000000000000000000000000000000000000000000018d126123bd57808d039c50612430565b7f80000000000000000000000000000000000000000000000000000000000000009c90038c015f81131561243057807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038b1361241d57998a0199612430565b5f809950995050505050505050506124f6565b5f808e12801561243f57505f8c135b1561247c577f80000000000000000000000000000000000000000000000000000000000000008e01808d13612474575f612478565b808d035b9150505b8b818f010397506124998f8e612493888789613cf2565b8b613dd7565b90995097505f8113156124e357604c8113156124c3575f809a509a505050505050505050506124f6565b80600a0a89816124d5576124d561492b565b059850885f036124e3575f97505b509698509496506124f695505050505050565b94509492505050565b5f61250c858585856131b7565b1495945050505050565b60606125228383613b74565b90935091505f806125537f161bcca7119915b50764b4abe86529797775a5f171951000000000000000000086614b46565b1561258357507f161bcca7119915b50764b4abe86529797775a5f17195100000000000000000009050604c6125aa565b507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000009050604b5b5f6125b58387614b46565b90505f6125c28488614bad565b90505f808312156125dc575060016125d983614bc0565b92505b5f8212156125f3575060016125f082614bc0565b91505b60408051602081019091525f815282156126fc575f80612614600a89614bf0565b90505b6126218186614b46565b5f0361264657612632600a82614bf0565b90508161263e81614c03565b925050612617565b60408051602081019091525f8082525b838110156126a7578160405160200161266f9190614c51565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529150600101612656565b505b6126b4600a87614bad565b5f036126cc576126c5600a87614b46565b95506126a9565b806126d68761417f565b6040516020016126e7929190614c89565b60405160208183030381529060405293505050505b5f6127068561417f565b90505f612713878b6149ab565b90505f8115612749576127258261417f565b6040516020016127359190614cc3565b604051602081830303815290604052612759565b60405180602001604052805f8152505b90505f856127755760405180602001604052805f8152506127ac565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b9050808486846040516020016127c59493929190614cf4565b6040516020818303038152906040529a505050505050505050505092915050565b60606103e882138061280157506127fe6103e8614bc0565b82125b1561283b576040517fe44c72b000000000000000000000000000000000000000000000000000000000815260048101839052602401610905565b5f8084129081156128565761284f85614bc0565b9050612859565b50835b5f6128638261420b565b80519091505f5b81811080156128e657508281612881600185614d14565b61288b9190614d14565b8151811061289b5761289b614d27565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f3000000000000000000000000000000000000000000000000000000000000000145b156128fd57806128f581614c03565b91505061286a565b5f6129088284614d14565b90505f612915838a6149ab565b90505f876129315760405180602001604052805f815250612968565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b90505f8212612ae257815f61297d8286614d54565b67ffffffffffffffff8111156129955761299561480c565b6040519080825280601f01601f1916602001820160405280156129bf576020820181803683370190505b5090505f5b85811015612a31578881815181106129de576129de614d27565b602001015160f81c60f81b8282815181106129fb576129fb614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053506001016129c4565b505f5b82811015612aae577f300000000000000000000000000000000000000000000000000000000000000082612a688389614d54565b81518110612a7857612a78614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612a34565b508281604051602001612ac2929190614d67565b6040516020818303038152906040529a50505050505050505050506106b1565b5f612aec83614bc0565b905080841115612d03575f612b018286614d14565b90505f612b0f866001614d54565b67ffffffffffffffff811115612b2757612b2761480c565b6040519080825280601f01601f191660200182016040528015612b51576020820181803683370190505b5090505f5b82811015612bc357898181518110612b7057612b70614d27565b602001015160f81c60f81b828281518110612b8d57612b8d614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612b56565b507f2e00000000000000000000000000000000000000000000000000000000000000818381518110612bf757612bf7614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b83811015612cce5789612c3a8285614d54565b81518110612c4a57612c4a614d27565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612c7e866001614d54565b612c889190614d54565b81518110612c9857612c98614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612c27565b508381604051602001612ce2929190614d67565b6040516020818303038152906040529b5050505050505050505050506106b1565b5f612d0e8583614d14565b90505f85612d1d836002614d54565b612d279190614d54565b67ffffffffffffffff811115612d3f57612d3f61480c565b6040519080825280601f01601f191660200182016040528015612d69576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000815f81518110612d9f57612d9f614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053507f2e0000000000000000000000000000000000000000000000000000000000000081600181518110612e0157612e01614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f5b82811015612eac577f300000000000000000000000000000000000000000000000000000000000000082612e66836002614d54565b81518110612e7657612e76614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612e31565b505f5b86811015612cce57898181518110612ec957612ec9614d27565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168282612efd866002614d54565b612f079190614d54565b81518110612f1757612f17614d27565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600101612eaf565b5f805f805f612f5c87876138d3565b92509250925080612fa3576040517fc47179660000000000000000000000000000000000000000000000000000000081526004810184905260248101839052604401610905565b50909590945092505050565b5f805f612fbc85856137cd565b9150915080611585576040517f22c9f7bb0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610905565b5f8061300d848461328b565b909450925061301e86868686613087565b9150915094509492505050565b5f805f61303986868661158d565b915091508061307e576040517f05e476780000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610905565b50949350505050565b5f80851584151780156130b357865f036130a757848492509250506124f6565b868692509250506124f6565b6130bd8787613b74565b90975095506130cc8585613b74565b9095509350858413156130e0579395929492935b838603604c8111156130f95787879350935050506124f6565b80600a0a868161310b5761310b61492b565b0595505086850180881860ff90811c151589881890911c151680156131a657877f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0361318d576040517fd556b111000000000000000000000000000000000000000000000000000000008152600481018a905260248101899052604401610905565b600a9687900596909805860197600197909701966131aa565b8198505b5096979596505050505050565b5f80851584151781871282861218178584141780156131dc57868592509250506124f6565b505f858413156131f0575092949193919260015b8386035f8112604c8213178015613223578215613215575f89945094505050506124f6565b885f945094505050506124f6565b600a82900a8981028a82828161323b5761323b61492b565b0514613267578415613257575f8b9650965050505050506124f6565b8a5f9650965050505050506124f6565b841561327c5788965094506124f69350505050565b95508794506124f69350505050565b5f807f80000000000000000000000000000000000000000000000000000000000000008403613324577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8303613317576040517fd556b1110000000000000000000000000000000000000000000000000000000081526004810185905260248101849052604401610905565b600a840593508260010192505b50505f9190910391565b5f8281806133438386652000000000006142c7565b9250858314158361335d81886703ff0000000000006142c7565b945080850361339757507f34bd20690000000000000000000000000000000000000000000000000000000094505f92508291506137c49050565b5f806133a38a886142f0565b90925090507fffffffff000000000000000000000000000000000000000000000000000000008216156133e1575095505f93508392506137c4915050565b86519095506540000000000060015f9290921a9190911b16151588871016915050801561367057506001909301925f8461342481896703ff0000000000006142c7565b955080860361345f57507f7bfa48af0000000000000000000000000000000000000000000000000000000095505f93508392506137c4915050565b855b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018051908a1160015f9290921a9190911b6601000000000000161515166001036134ce577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01613461565b818114613525575f806134e184846142f0565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615613521575097505f95508594506137c49350505050565b9350505b5f83121561356057507f7bfa48af0000000000000000000000000000000000000000000000000000000096505f94508493506137c492505050565b831561356c57825f0392505b80820394505f8513156135ac57507f013b2aaa0000000000000000000000000000000000000000000000000000000096505f94508493506137c492505050565b855f036135bb5782955061366d565b5f85900360438111156135fc57507f32b8b8be0000000000000000000000000000000000000000000000000000000097505f95508594506137c49350505050565b600a0a8681025f888383816136135761361361492b565b0514159050601b82900b82141581806136295750805b1561366557507f32b8b8be000000000000000000000000000000000000000000000000000000009a505f98508897506137c49650505050505050565b505084019650505b50505b84516c2000000020000000000000000060015f9290921a9190911b1615158786101680156137b557600190950194856136b0818a652000000000006142c7565b9650866136c6818b6703ff0000000000006142c7565b975080880361370357507f013b2aaa0000000000000000000000000000000000000000000000000000000097505f95508594506137c49350505050565b505f80613710838a6142f0565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615613750575097505f95508594506137c49350505050565b9250508482015f8313801561376457508581125b8061377857505f8312801561377857508581135b156137b157507fd556b1110000000000000000000000000000000000000000000000000000000097505f95508594506137c49350505050565b9450505b845f036137c0575f93505b5050505b92959194509250565b5f601b83900b8314838382613831577d90e40fbeea1d3a4abc8955e946fe31cdcf66f634e100000000000000000086051561381157620186a0860595506005850194505b8586601b0b1461382c57600a86059550846001019450613811565b613847565b855f0361384757505f9250600191506106da9050565b848560030b146138a3575f85121561386757505f92508291506106da9050565b6040517fd556b1110000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610905565b50507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660e084901b1791509250929050565b5f808060ff841681037f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561391e57600a860460018201600a88065f1493509350935050613928565b8593509150600190505b9250925092565b5f805f837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0361396757508391505f90506001613928565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8412156139f5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb08412156139c657505f9150508215156001613928565b5f846004015f03600a0a90505f8187816139e2576139e261492b565b0594505080840286141592509050613928565b5f8412613a0a57505f91508190506001613928565b50505060048101600a0a82025f6001613928565b5f80806064613a30600a612328614bf0565b613a3d600a612328614bf0565b613a48906002614d75565b613a53906001614d54565b613a5d9190614d54565b613a679190614d54565b9050613aab565b5f8052600280600a8504028301601e833c5f80516107d0840193505f80526001600a8606600a6064880402018501601f853c5f5101949350505050565b613ab6858288613a6e565b92508315611c3457613acc600186018288613a6e565b915050935093915050565b5f80888803613aea575083905081613b68565b5f805f80613afa8c8b8f8d613001565b915091505f80613b0c8a8a8d8c613001565b91509150613b1c84848484611dbc565b8096508197505050505050505f80613b368b8b8f8d613001565b915091505f80613b4886868686611edb565b915091505f80613b5a84848f8e613087565b909a50985050505050505050505b97509795505050505050565b5f805f805f613b838787613ede565b92509250925080612fa3576040517f05e51ecb0000000000000000000000000000000000000000000000000000000081526004810188905260248101879052604401610905565b5f80613bd9600a612328614bf0565b613be4906002614d75565b613bef906001614d54565b90505f613bff600a612328614bf0565b90506002600a8504026001015f8052600281601e883c505f51617fff81169350618000811615613c2e57918101915b505f80526001600a8506600a6064870402018301601f873c50505f510192915050565b5f80821215613cae577f80000000000000000000000000000000000000000000000000000000000000008203613ca857507f8000000000000000000000000000000000000000000000000000000000000000919050565b505f0390565b5090565b919050565b5f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83850993909202808410938190039390930393915050565b5f805f613cff8686613cb7565b91509150815f03613d2357838181613d1957613d1961492b565b04925050506106c2565b838210613d6d576040517f6c59da12000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610905565b5f84868809600186198101871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103025f82900382900490920185841190960395909502919093039390930492909217029150509392505050565b5f805f8587181215613ea1577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613e8e57613e367f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001614d54565b8403613e6657507f80000000000000000000000000000000000000000000000000000000000000009050816124f6565b613e71600a85614bf0565b613e7a90614bc0565b613e858460016149ab565b915091506124f6565b613e9784614bc0565b83915091506124f6565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115613ed457613e7a600a85614bf0565b50829050816124f6565b5f805f845f03613ef657505f91508190506001613928565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee800000000000000000085055f0361410a576f4b3b4ca85a86c47a098a2240000000008505158015613f5f57507f80000000000000000000000000000000000000000000000000000000000000268412155b15613f80576f4b3b4ca85a86c47a098a224000000000850294506026840393505b7728c87cb5c89a2571ebfdcb54864ada834a000000000000008505158015613fc857507f80000000000000000000000000000000000000000000000000000000000000138412155b15613fe157678ac7230489e80000850294506013840393505b7b097edd871cfda3a5697758bf0e3cbb5ac5741c640000000000000000850515801561402d57507f800000000000000000000000000000000000000000000000000000000000000a8412155b15614043576402540be40085029450600a840393505b7e3899162693736ac531a5a58f1fbb4b746504382ca7e4000000000000000000850515801561409257507f80000000000000000000000000000000000000000000000000000000000000028412155b156140a857606485029450600284039350614043565b7f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee800000000000000000085051580156140f857507f80000000000000000000000000000000000000000000000000000000000000018412155b1561410a57600a850294506001840393505b600a8086029081058614801561414057507f80000000000000000000000000000000000000000000000000000000000000018512155b1561414f578095506001850394505b50939492935050507f0235fadd81c2822bb3f07877973d50f28bf22a31be8ee80000000000000000008305151590565b60605f821261419c5760405180602001604052805f8152506141d3565b6040518060400160405280600181526020017f2d000000000000000000000000000000000000000000000000000000000000008152505b6141e460ff84901d8085011861420b565b6040516020016141f5929190614d67565b6040516020818303038152906040529050919050565b60605f6142178361441a565b60010190505f8167ffffffffffffffff8111156142365761423661480c565b6040519080825280601f01601f191660200182016040528015614260576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461426a57509392505050565b5f5b5f82600186515f1a1b161183851016156142e8576001840193506142c9565b509192915050565b81515f90819065200000000000600191831a9190911b1615158385101680850190828061431d84886144fb565b90925090507fffffffff00000000000000000000000000000000000000000000000000000000821615614358575093505f92506106da915050565b825f036143bb577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161438c575f6143ae565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b955093506106da92505050565b7f800000000000000000000000000000000000000000000000000000000000000081116143e8575f61440a565b7f0fdc2635000000000000000000000000000000000000000000000000000000005b95505f0393505050509250929050565b5f807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310614462577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef8100000000831061448e576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144ac57662386f26fc10000830492506010015b6305f5e10083106144c4576305f5e100830492506008015b61271083106144d857612710830492506004015b606483106144ea576064830492506002015b600a83106106b15760010192915050565b5f8082841061452e57507f34bd20690000000000000000000000000000000000000000000000000000000090505f6106da565b835f03614567576040517fda6966d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60305f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8501815b87821015801561459f5750604d83105b156145e257815160018401937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90930192600a0a5f9190911a859003020161458f565b8782106147085781515f1a849003600181111561462a57507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106da9350505050565b600a84900a810282810183111561466d57507f0fdc26350000000000000000000000000000000000000000000000000000000096505f95506106da945050505050565b9190910190507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b8782106147085781515f1a603081146146dd57507f0fdc26350000000000000000000000000000000000000000000000000000000095505f94506106da9350505050565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90910190614699565b5f9890975095505050505050565b5f60208284031215614726575f80fd5b5035919050565b5f806040838503121561473e575f80fd5b50508035926020909101359150565b803560ff81168114613cb2575f80fd5b5f806040838503121561476e575f80fd5b8235915061477e6020840161474d565b90509250929050565b5f8060408385031215614798575f80fd5b82359150602083013580151581146147ae575f80fd5b809150509250929050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215614849575f80fd5b813567ffffffffffffffff80821115614860575f80fd5b818401915084601f830112614873575f80fd5b8135818111156148855761488561480c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156148cb576148cb61480c565b816040528281528760208487010111156148e3575f80fd5b826020860160208301375f928101602001929092525095945050505050565b5f805f60608486031215614914575f80fd5b505081359360208301359350604090920135919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181035f8312801583831316838312821617156149a4576149a4614958565b5092915050565b8082018281125f8312801582168215821617156149ca576149ca614958565b505092915050565b8082025f82127f800000000000000000000000000000000000000000000000000000000000000084141615614a0957614a09614958565b81810583148215176106b1576106b1614958565b600181815b80851115614a7657817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614a5c57614a5c614958565b80851615614a6957918102915b93841c9390800290614a22565b509250929050565b5f82614a8c575060016106b1565b81614a9857505f6106b1565b8160018114614aae5760028114614ab857614ad4565b60019150506106b1565b60ff841115614ac957614ac9614958565b50506001821b6106b1565b5060208310610133831016604e8410600b8410161715614af7575081810a6106b1565b614b018383614a1d565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614b3357614b33614958565b029392505050565b5f6106c28383614a7e565b5f82614b5457614b5461492b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615614ba857614ba8614958565b500590565b5f82614bbb57614bbb61492b565b500790565b5f7f80000000000000000000000000000000000000000000000000000000000000008203613ca857613ca8614958565b5f82614bfe57614bfe61492b565b500490565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614c3357614c33614958565b5060010190565b5f81518060208401855e5f93019283525090919050565b5f614c5c8284614c3a565b7f300000000000000000000000000000000000000000000000000000000000000081526001019392505050565b7f2e0000000000000000000000000000000000000000000000000000000000000081525f61129c614cbd6001840186614c3a565b84614c3a565b7f650000000000000000000000000000000000000000000000000000000000000081525f6106c26001830184614c3a565b5f610a60614cbd614d0e614d08858a614c3a565b88614c3a565b86614c3a565b818103818111156106b1576106b1614958565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b808201808211156106b1576106b1614958565b5f61129c614cbd8386614c3a565b80820281158282048414176106b1576106b161495856" } } \ No newline at end of file diff --git a/crates/float/src/js_api.rs b/crates/float/src/js_api.rs index 68c47a0..eb61e64 100644 --- a/crates/float/src/js_api.rs +++ b/crates/float/src/js_api.rs @@ -752,6 +752,31 @@ impl Float { self.abs() } + /// Returns the canonical representative of the float's numeric value. + /// + /// Floats are non-canonical by design: multiple representations encode the + /// same number. `canonicalize` returns the single representative whose + /// magnitude is maximised within the type bounds, so two Floats are + /// numerically equal iff their canonical forms are byte-equal. Intended for + /// raw-byte equality use cases (map keys, hashing, set membership). + /// + /// # Returns + /// + /// * `Ok(Float)` - The canonical form. + /// * `Err(FloatError)` - If the operation fails. + /// + /// # Example + /// + /// ```typescript + /// const a = Float.parse("5").value!; + /// const b = Float.parse("5.0").value!; + /// assert(a.canonicalize().value.asHex() === b.canonicalize().value.asHex()); + /// ``` + #[wasm_export(js_name = "canonicalize", preserve_js_class)] + pub fn canonicalize_js(&self) -> Result { + self.canonicalize() + } + /// Adds two floats. /// /// # Returns diff --git a/crates/float/src/lib.rs b/crates/float/src/lib.rs index 994b8fd..d70b0d8 100644 --- a/crates/float/src/lib.rs +++ b/crates/float/src/lib.rs @@ -848,6 +848,41 @@ impl Float { }) } + /// Returns the canonical representative of the float's numeric value. + /// + /// Floats are non-canonical by design: multiple `(coefficient, exponent)` + /// pairs encode the same number. `canonicalize` returns the single + /// representative whose magnitude is maximised within the type bounds, so + /// two Floats are numerically equal iff their canonical forms are + /// byte-equal. Intended for raw-byte equality use cases (map keys, hashing, + /// set membership, content-addressed storage). + /// + /// # Returns + /// + /// * `Ok(Float)` - The canonical form. + /// * `Err(FloatError)` - If the operation fails. + /// + /// # Example + /// + /// ``` + /// use rain_math_float::Float; + /// + /// let a = Float::parse("5".to_string())?; + /// let b = Float::parse("5.0".to_string())?; + /// assert_eq!(a.canonicalize()?.as_hex(), b.canonicalize()?.as_hex()); + /// + /// anyhow::Ok(()) + /// ``` + pub fn canonicalize(self) -> Result { + let Float(a) = self; + let calldata = DecimalFloat::canonicalizeCall { a }.abi_encode(); + + execute_call(Bytes::from(calldata), |output| { + let decoded = DecimalFloat::canonicalizeCall::abi_decode_returns(output.as_ref())?; + Ok(Float(decoded)) + }) + } + /// Returns `true` if `self` is less than or equal to `b`. /// /// # Arguments diff --git a/src/concrete/DecimalFloat.sol b/src/concrete/DecimalFloat.sol index 516762e..0f843ae 100644 --- a/src/concrete/DecimalFloat.sol +++ b/src/concrete/DecimalFloat.sol @@ -282,6 +282,13 @@ contract DecimalFloat { return a.isZero(); } + /// Exposes `LibDecimalFloat.canonicalize` for offchain use. + /// @param a The float to canonicalize. + /// @return The canonical representative of the float's numeric value. + function canonicalize(Float a) external pure returns (Float) { + return a.canonicalize(); + } + /// Exposes `LibDecimalFloat.fromFixedDecimalLosslessPacked` for offchain /// use. /// @param value The fixed point decimal value to convert. diff --git a/src/lib/deploy/LibDecimalFloatDeploy.sol b/src/lib/deploy/LibDecimalFloatDeploy.sol index dd0f8bd..1ede21e 100644 --- a/src/lib/deploy/LibDecimalFloatDeploy.sol +++ b/src/lib/deploy/LibDecimalFloatDeploy.sol @@ -25,11 +25,11 @@ library LibDecimalFloatDeploy { /// @dev Address of the DecimalFloat contract deployed via Zoltu's /// deterministic deployment proxy. /// This address is the same across all EVM-compatible networks. - address constant ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS = address(0xBee0eEFaffD046c9602109eB30A858Be301CC926); + address constant ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS = address(0x6b7C246F02E67299b5801f8215d7f40abD82056d); /// @dev The expected codehash of the DecimalFloat contract deployed via /// Zoltu's deterministic deployment proxy. - bytes32 constant DECIMAL_FLOAT_CONTRACT_HASH = 0x7a93d0311f7782b44157ba40e94ec936085ebe001c7893bdd74911c8351d3def; + bytes32 constant DECIMAL_FLOAT_CONTRACT_HASH = 0x61bbc303586dc1b233644acdebe38dcc757907d7b39edcf6b1152c2081cf3197; /// Combines all log and anti-log tables into a single bytes array for /// deployment. These are using packed encoding to minimize size and remove diff --git a/test/src/concrete/DecimalFloat.canonicalize.t.sol b/test/src/concrete/DecimalFloat.canonicalize.t.sol new file mode 100644 index 0000000..3f59b7d --- /dev/null +++ b/test/src/concrete/DecimalFloat.canonicalize.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity =0.8.25; + +import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; +import {LogTest} from "test/abstract/LogTest.sol"; +import {DecimalFloat} from "src/concrete/DecimalFloat.sol"; + +contract DecimalFloatCanonicalizeTest is LogTest { + using LibDecimalFloat for Float; + + function canonicalizeExternal(Float a) external pure returns (Float) { + return a.canonicalize(); + } + + /// The `canonicalize` method exposed on the deployed concrete contract must + /// match the library result for any input (and revert identically if the + /// library reverts). + function testCanonicalizeDeployed(Float a) external { + DecimalFloat deployed = new DecimalFloat(); + + try this.canonicalizeExternal(a) returns (Float b) { + Float deployedB = deployed.canonicalize(a); + + assertEq(Float.unwrap(b), Float.unwrap(deployedB)); + } catch (bytes memory err) { + vm.expectRevert(err); + deployed.canonicalize(a); + } + } +} diff --git a/test/src/lib/LibDecimalFloat.canonicalize.t.sol b/test/src/lib/LibDecimalFloat.canonicalize.t.sol index dd5b0ba..02f0603 100644 --- a/test/src/lib/LibDecimalFloat.canonicalize.t.sol +++ b/test/src/lib/LibDecimalFloat.canonicalize.t.sol @@ -9,6 +9,10 @@ import {Test} from "forge-std-1.16.1/src/Test.sol"; contract LibDecimalFloatCanonicalizeTest is Test { using LibDecimalFloat for Float; + int224 constant INT224_MAX = type(int224).max; + int224 constant INT224_MIN = type(int224).min; + int32 constant INT32_MIN = type(int32).min; + function canonicalizeExternal(Float float) external pure returns (Float) { return float.canonicalize(); } @@ -94,4 +98,204 @@ contract LibDecimalFloatCanonicalizeTest is Test { Float.unwrap(a.canonicalize()), Float.unwrap(b.canonicalize()), "eq implies byte-equal after canonicalize" ); } + + // --------------------------------------------------------------------- + // Adversarial coverage that deliberately leaves the int128/uint8 "easy + // middle" the tests above stay inside. These target the int224 and int32 + // type boundaries and, crucially, assert the SAFETY property the tests + // above never check: numerically DISTINCT Floats must canonicalize to + // byte-UNEQUAL results (no collision). They also pin the exponent to the + // int32.min floor where the scaling loop is capped before the coefficient + // is maximised, and the int224.min/int224.max coefficient boundaries. + // --------------------------------------------------------------------- + + // --------------------------------------------------------------------- + // NO-COLLISION: numerically distinct -> byte-UNEQUAL after canonicalize. + // --------------------------------------------------------------------- + + /// Concrete no-collision at the int32.min exponent floor. Two tiny values + /// pinned at the floor that differ only in their (already minimal) + /// coefficient must NOT collapse to the same bytes. At the floor the loop + /// cannot scale, so this directly exercises whether distinct floor-pinned + /// coefficients survive distinct. + function testCanonicalizeNoCollisionAtFloorConcrete() external pure { + // 1e(int32.min) and 2e(int32.min): genuinely different numbers. + Float a = LibDecimalFloat.packLossless(1, INT32_MIN); + Float b = LibDecimalFloat.packLossless(2, INT32_MIN); + assertTrue(!a.eq(b), "precondition: distinct values"); + assertTrue( + Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), + "distinct floor-pinned values must not collide" + ); + } + + /// Concrete no-collision where one value's loop is capped by the int224 + /// bound and a neighbouring distinct value's is too. Maximised positive vs. + /// negative of nearly-equal magnitude must not collide. + function testCanonicalizeNoCollisionInt224CapConcrete() external pure { + Float a = LibDecimalFloat.packLossless(7, 0); + Float b = LibDecimalFloat.packLossless(7, 1); // 70, distinct value + Float c = LibDecimalFloat.packLossless(-7, 0); // distinct sign + bytes32 ca = Float.unwrap(a.canonicalize()); + bytes32 cb = Float.unwrap(b.canonicalize()); + bytes32 cc = Float.unwrap(c.canonicalize()); + assertTrue(ca != cb, "7e0 vs 70e0 must not collide"); + assertTrue(ca != cc, "7 vs -7 must not collide"); + assertTrue(cb != cc, "70 vs -7 must not collide"); + } + + /// Fuzz no-collision across the FULL int224/int32 box. Build two Floats from + /// raw boundary-capable inputs; if they are numerically distinct (`!eq`), + /// their canonical forms must be byte-distinct. `eq` is the library's own + /// numeric-equality oracle, so this is exactly the documented guarantee + /// "two Floats are numerically equal iff their canonical forms are + /// byte-equal" applied in the contrapositive. + function testCanonicalizeNoCollisionFuzz(int224 coefficientA, int32 exponentA, int224 coefficientB, int32 exponentB) + external + pure + { + Float a = LibDecimalFloat.packLossless(coefficientA, exponentA); + Float b = LibDecimalFloat.packLossless(coefficientB, exponentB); + if (!a.eq(b)) { + assertTrue( + Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), + "distinct values must canonicalize to distinct bytes" + ); + } + } + + /// Fuzz no-collision focused at the int32.min floor: both operands pinned to + /// a narrow band just above the floor so the loop terminates at the floor + /// for small coefficients, the regime the tests above never reach. + function testCanonicalizeNoCollisionNearFloorFuzz(int224 coefficientA, int224 coefficientB, uint8 lift) + external + pure + { + // Exponent within [int32.min, int32.min + 255]: the floor band. + int32 exponent = int32(int256(INT32_MIN) + int256(uint256(lift))); + Float a = LibDecimalFloat.packLossless(coefficientA, exponent); + Float b = LibDecimalFloat.packLossless(coefficientB, exponent); + if (!a.eq(b)) { + assertTrue( + Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), + "distinct near-floor values must not collide" + ); + } + } + + // --------------------------------------------------------------------- + // EQ -> BYTE-EQUAL across representations, driven at the FLOOR and the + // int224 cap (the (c) hunt: same value via different starting reps). + // --------------------------------------------------------------------- + + /// Same value reached two different ways, both bottoming out at the floor. + /// 10e(min) and 1e(min+1) are the same number; both must canonicalize to the + /// identical bytes even though one starts already-scaled. + function testCanonicalizeFloorCrossRepConcrete() external pure { + Float a = LibDecimalFloat.packLossless(10, INT32_MIN); + Float b = LibDecimalFloat.packLossless(1, int32(int256(INT32_MIN) + 1)); + assertTrue(a.eq(b), "precondition: same value"); + assertEq( + Float.unwrap(a.canonicalize()), + Float.unwrap(b.canonicalize()), + "same value at floor -> identical canonical bytes" + ); + } + + /// Fuzz: a value whose loop is capped by the floor, reached from two + /// different starting representations, canonicalizes identically. Start with + /// coefficient `base` at `int32.min + k`, and the pre-scaled `base*10^j` at + /// `int32.min + k - j`; both encode the same value and both must hit the + /// same floor-capped canonical form. + function testCanonicalizeFloorCrossRepFuzz(int64 base, uint8 k, uint8 j) external pure { + vm.assume(base != 0); + // Keep both exponents in the floor band and >= int32.min. + uint256 kk = uint256(k) % 30 + 1; // 1..30 + uint256 jj = uint256(j) % kk; // 0..kk-1 so exp stays >= floor + int32 expA = int32(int256(INT32_MIN) + int256(kk)); + // base * 10^jj still fits int224 comfortably (int64 * 10^29 < int224). + int256 scaled = int256(base) * int256(10 ** jj); + int32 expB = int32(int256(INT32_MIN) + int256(kk) - int256(jj)); + + Float a = LibDecimalFloat.packLossless(base, expA); + Float b = LibDecimalFloat.packLossless(scaled, expB); + assertTrue(a.eq(b), "precondition: same value"); + assertEq( + Float.unwrap(a.canonicalize()), Float.unwrap(b.canonicalize()), "cross-rep at floor must be byte-equal" + ); + } + + // --------------------------------------------------------------------- + // VALUE PRESERVATION + IDEMPOTENCE at the int224/int32 boundaries the + // tests above avoid. + // --------------------------------------------------------------------- + + /// Value preservation at the floor where the coefficient is NOT maximised + /// (the loop is capped by int32.min, leaving a small coefficient). 1e(min) + /// cannot scale at all; the canonical form must still be `eq` to the input + /// and idempotent. + function testCanonicalizeFloorValuePreservedConcrete() external pure { + Float f = LibDecimalFloat.packLossless(123, INT32_MIN); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved at floor"); + // At the floor with a tiny coefficient the loop cannot run, so the form + // is already canonical: the input bytes must equal the canonical bytes. + assertEq(Float.unwrap(c), Float.unwrap(f), "floor-pinned tiny coeff is already canonical"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent at floor"); + } + + /// int224.max / int224.min coefficients: already at the magnitude ceiling so + /// the loop cannot scale (×10 overflows int224). Value-preserving and + /// idempotent, and the input is already its own canonical form. + function testCanonicalizeInt224ExtremesConcrete() external pure { + int224[2] memory extremes = [INT224_MAX, INT224_MIN]; + for (uint256 i = 0; i < extremes.length; i++) { + Float f = LibDecimalFloat.packLossless(extremes[i], 3); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved at int224 extreme"); + assertEq(Float.unwrap(c), Float.unwrap(f), "int224 extreme already canonical"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent at int224 extreme"); + } + } + + /// Fuzz value-preservation + idempotence pinned to the int32.min floor band. + /// This is the regime the uniform-int32-exponent fuzz above almost never + /// samples. + function testCanonicalizeFloorValuePreservedFuzz(int224 coefficient, uint16 lift) external pure { + int32 exponent = int32(int256(INT32_MIN) + int256(uint256(lift))); + Float f = LibDecimalFloat.packLossless(coefficient, exponent); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved near floor"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent near floor"); + } + + /// Fuzz value-preservation + idempotence with coefficients pinned to the + /// top/bottom of the int224 range (where the loop terminates immediately or + /// after a single step), across arbitrary int32 exponents. + function testCanonicalizeInt224EdgeValuePreservedFuzz(uint64 delta, bool negative, int32 exponent) external pure { + // Coefficient within `delta` of an int224 extreme. + int256 coefficient = + negative ? int256(INT224_MIN) + int256(uint256(delta)) : int256(INT224_MAX) - int256(uint256(delta)); + Float f = LibDecimalFloat.packLossless(coefficient, exponent); + Float c = f.canonicalize(); + assertTrue(f.eq(c), "value preserved near int224 edge"); + assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent near int224 edge"); + } + + /// Fuzz the documented end-to-end guarantee at full boundary width: build two + /// Floats from raw int224/int32 inputs and assert `eq` <=> byte-equal after + /// canonicalize (both directions in one test). The eq->byte-equal fuzz above + /// only ever feeds equal values; this also feeds unequal ones. + function testCanonicalizeEqIffByteEqualFuzz( + int224 coefficientA, + int32 exponentA, + int224 coefficientB, + int32 exponentB + ) external pure { + Float a = LibDecimalFloat.packLossless(coefficientA, exponentA); + Float b = LibDecimalFloat.packLossless(coefficientB, exponentB); + bool numericallyEqual = a.eq(b); + bool byteEqual = Float.unwrap(a.canonicalize()) == Float.unwrap(b.canonicalize()); + assertEq(numericallyEqual, byteEqual, "eq iff byte-equal after canonicalize"); + } } diff --git a/test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol b/test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol deleted file mode 100644 index 821f69d..0000000 --- a/test/src/lib/LibDecimalFloat.canonicalizeAdversarial.t.sol +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity =0.8.25; - -import {LibDecimalFloat, Float} from "src/lib/LibDecimalFloat.sol"; - -import {Test} from "forge-std-1.16.1/src/Test.sol"; - -/// Adversarial tests for `canonicalize` that deliberately leave the int128/uint8 -/// "easy middle" the existing suite stays inside. These target the int224 and -/// int32 type boundaries and, crucially, assert the SAFETY property the existing -/// tests never check: numerically DISTINCT Floats must canonicalize to -/// byte-UNEQUAL results (no collision). They also pin the exponent to the -/// int32.min floor where the scaling loop is capped before the coefficient is -/// maximised, and the int224.min/int224.max coefficient boundaries. -contract LibDecimalFloatCanonicalizeAdversarialTest is Test { - using LibDecimalFloat for Float; - - int224 constant INT224_MAX = type(int224).max; - int224 constant INT224_MIN = type(int224).min; - int32 constant INT32_MIN = type(int32).min; - int32 constant INT32_MAX = type(int32).max; - - // --------------------------------------------------------------------- - // NO-COLLISION: numerically distinct -> byte-UNEQUAL after canonicalize. - // --------------------------------------------------------------------- - - /// Concrete no-collision at the int32.min exponent floor. Two tiny values - /// pinned at the floor that differ only in their (already minimal) - /// coefficient must NOT collapse to the same bytes. At the floor the loop - /// cannot scale, so this directly exercises whether distinct floor-pinned - /// coefficients survive distinct. - function testCanonicalizeNoCollisionAtFloorConcrete() external pure { - // 1e(int32.min) and 2e(int32.min): genuinely different numbers. - Float a = LibDecimalFloat.packLossless(1, INT32_MIN); - Float b = LibDecimalFloat.packLossless(2, INT32_MIN); - assertTrue(!a.eq(b), "precondition: distinct values"); - assertTrue( - Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), - "distinct floor-pinned values must not collide" - ); - } - - /// Concrete no-collision where one value's loop is capped by the int224 - /// bound and a neighbouring distinct value's is too. Maximised positive vs. - /// negative of nearly-equal magnitude must not collide. - function testCanonicalizeNoCollisionInt224CapConcrete() external pure { - Float a = LibDecimalFloat.packLossless(7, 0); - Float b = LibDecimalFloat.packLossless(7, 1); // 70, distinct value - Float c = LibDecimalFloat.packLossless(-7, 0); // distinct sign - bytes32 ca = Float.unwrap(a.canonicalize()); - bytes32 cb = Float.unwrap(b.canonicalize()); - bytes32 cc = Float.unwrap(c.canonicalize()); - assertTrue(ca != cb, "7e0 vs 70e0 must not collide"); - assertTrue(ca != cc, "7 vs -7 must not collide"); - assertTrue(cb != cc, "70 vs -7 must not collide"); - } - - /// Fuzz no-collision across the FULL int224/int32 box. Build two Floats from - /// raw boundary-capable inputs; if they are numerically distinct (`!eq`), - /// their canonical forms must be byte-distinct. `eq` is the library's own - /// numeric-equality oracle, so this is exactly the documented guarantee - /// "two Floats are numerically equal iff their canonical forms are - /// byte-equal" applied in the contrapositive. - function testCanonicalizeNoCollisionFuzz(int224 coefficientA, int32 exponentA, int224 coefficientB, int32 exponentB) - external - pure - { - Float a = LibDecimalFloat.packLossless(coefficientA, exponentA); - Float b = LibDecimalFloat.packLossless(coefficientB, exponentB); - if (!a.eq(b)) { - assertTrue( - Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), - "distinct values must canonicalize to distinct bytes" - ); - } - } - - /// Fuzz no-collision focused at the int32.min floor: both operands pinned to - /// a narrow band just above the floor so the loop terminates at the floor - /// for small coefficients, the regime the existing tests never reach. - function testCanonicalizeNoCollisionNearFloorFuzz(int224 coefficientA, int224 coefficientB, uint8 lift) - external - pure - { - // Exponent within [int32.min, int32.min + 255]: the floor band. - int32 exponent = int32(int256(INT32_MIN) + int256(uint256(lift))); - Float a = LibDecimalFloat.packLossless(coefficientA, exponent); - Float b = LibDecimalFloat.packLossless(coefficientB, exponent); - if (!a.eq(b)) { - assertTrue( - Float.unwrap(a.canonicalize()) != Float.unwrap(b.canonicalize()), - "distinct near-floor values must not collide" - ); - } - } - - // --------------------------------------------------------------------- - // EQ -> BYTE-EQUAL across representations, driven at the FLOOR and the - // int224 cap (the (c) hunt: same value via different starting reps). - // --------------------------------------------------------------------- - - /// Same value reached two different ways, both bottoming out at the floor. - /// 10e(min) and 1e(min+1) are the same number; both must canonicalize to the - /// identical bytes even though one starts already-scaled. - function testCanonicalizeFloorCrossRepConcrete() external pure { - Float a = LibDecimalFloat.packLossless(10, INT32_MIN); - Float b = LibDecimalFloat.packLossless(1, int32(int256(INT32_MIN) + 1)); - assertTrue(a.eq(b), "precondition: same value"); - assertEq( - Float.unwrap(a.canonicalize()), - Float.unwrap(b.canonicalize()), - "same value at floor -> identical canonical bytes" - ); - } - - /// Fuzz: a value whose loop is capped by the floor, reached from two - /// different starting representations, canonicalizes identically. Start with - /// coefficient `base` at `int32.min + k`, and the pre-scaled `base*10^j` at - /// `int32.min + k - j`; both encode the same value and both must hit the - /// same floor-capped canonical form. - function testCanonicalizeFloorCrossRepFuzz(int64 base, uint8 k, uint8 j) external pure { - vm.assume(base != 0); - // Keep both exponents in the floor band and >= int32.min. - uint256 kk = uint256(k) % 30 + 1; // 1..30 - uint256 jj = uint256(j) % kk; // 0..kk-1 so exp stays >= floor - int32 expA = int32(int256(INT32_MIN) + int256(kk)); - // base * 10^jj still fits int224 comfortably (int64 * 10^29 < int224). - int256 scaled = int256(base) * int256(10 ** jj); - int32 expB = int32(int256(INT32_MIN) + int256(kk) - int256(jj)); - - Float a = LibDecimalFloat.packLossless(base, expA); - Float b = LibDecimalFloat.packLossless(scaled, expB); - assertTrue(a.eq(b), "precondition: same value"); - assertEq( - Float.unwrap(a.canonicalize()), Float.unwrap(b.canonicalize()), "cross-rep at floor must be byte-equal" - ); - } - - // --------------------------------------------------------------------- - // VALUE PRESERVATION + IDEMPOTENCE at the int224/int32 boundaries the - // existing suite avoids. - // --------------------------------------------------------------------- - - /// Value preservation at the floor where the coefficient is NOT maximised - /// (the loop is capped by int32.min, leaving a small coefficient). 1e(min) - /// cannot scale at all; the canonical form must still be `eq` to the input - /// and idempotent. - function testCanonicalizeFloorValuePreservedConcrete() external pure { - Float f = LibDecimalFloat.packLossless(123, INT32_MIN); - Float c = f.canonicalize(); - assertTrue(f.eq(c), "value preserved at floor"); - // At the floor with a tiny coefficient the loop cannot run, so the form - // is already canonical: the input bytes must equal the canonical bytes. - assertEq(Float.unwrap(c), Float.unwrap(f), "floor-pinned tiny coeff is already canonical"); - assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent at floor"); - } - - /// int224.max / int224.min coefficients: already at the magnitude ceiling so - /// the loop cannot scale (×10 overflows int224). Value-preserving and - /// idempotent, and the input is already its own canonical form. - function testCanonicalizeInt224ExtremesConcrete() external pure { - int224[2] memory extremes = [INT224_MAX, INT224_MIN]; - for (uint256 i = 0; i < extremes.length; i++) { - Float f = LibDecimalFloat.packLossless(extremes[i], 3); - Float c = f.canonicalize(); - assertTrue(f.eq(c), "value preserved at int224 extreme"); - assertEq(Float.unwrap(c), Float.unwrap(f), "int224 extreme already canonical"); - assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent at int224 extreme"); - } - } - - /// Fuzz value-preservation + idempotence pinned to the int32.min floor band. - /// This is the regime the existing fuzz (uniform int32 exponent) almost - /// never samples. - function testCanonicalizeFloorValuePreservedFuzz(int224 coefficient, uint16 lift) external pure { - int32 exponent = int32(int256(INT32_MIN) + int256(uint256(lift))); - Float f = LibDecimalFloat.packLossless(coefficient, exponent); - Float c = f.canonicalize(); - assertTrue(f.eq(c), "value preserved near floor"); - assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent near floor"); - } - - /// Fuzz value-preservation + idempotence with coefficients pinned to the - /// top/bottom of the int224 range (where the loop terminates immediately or - /// after a single step), across arbitrary int32 exponents. - function testCanonicalizeInt224EdgeValuePreservedFuzz(uint64 delta, bool negative, int32 exponent) external pure { - // Coefficient within `delta` of an int224 extreme. - int256 coefficient = - negative ? int256(INT224_MIN) + int256(uint256(delta)) : int256(INT224_MAX) - int256(uint256(delta)); - Float f = LibDecimalFloat.packLossless(coefficient, exponent); - Float c = f.canonicalize(); - assertTrue(f.eq(c), "value preserved near int224 edge"); - assertEq(Float.unwrap(c.canonicalize()), Float.unwrap(c), "idempotent near int224 edge"); - } - - /// Fuzz the documented end-to-end guarantee at full boundary width: build two - /// Floats from raw int224/int32 inputs and assert `eq` <=> byte-equal after - /// canonicalize (both directions in one test). The existing eq->byte-equal - /// fuzz only ever feeds equal values; this also feeds unequal ones. - function testCanonicalizeEqIffByteEqualFuzz( - int224 coefficientA, - int32 exponentA, - int224 coefficientB, - int32 exponentB - ) external pure { - Float a = LibDecimalFloat.packLossless(coefficientA, exponentA); - Float b = LibDecimalFloat.packLossless(coefficientB, exponentB); - bool numericallyEqual = a.eq(b); - bool byteEqual = Float.unwrap(a.canonicalize()) == Float.unwrap(b.canonicalize()); - assertEq(numericallyEqual, byteEqual, "eq iff byte-equal after canonicalize"); - } -} diff --git a/test_js/float.test.ts b/test_js/float.test.ts index ca30a2c..25b3be2 100644 --- a/test_js/float.test.ts +++ b/test_js/float.test.ts @@ -151,6 +151,18 @@ describe("Test Float Bindings", () => { expect(a.add(b)?.value!.format()?.value!).toBe("0"); }); + it("should test canonicalize", () => { + // Two different representations of the same value canonicalize to + // byte-equal Floats, and the canonical form is numerically equal to the + // input. + const a = Float.parse("5")?.value!; + const b = Float.parse("5.0")?.value!; + const canonicalA = a.canonicalize()?.value!; + const canonicalB = b.canonicalize()?.value!; + expect(canonicalA.asHex()).toBe(canonicalB.asHex()); + expect(a.eq(canonicalA)?.value!).toBe(true); + }); + it("should test zero constant", () => { // Test the zero function const zeroResult = Float.zero();