diff --git a/.gitattributes b/.gitattributes index f3351d043eb..a447781cffc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ notebooks/** linguist-vendored Cargo.lock linguist-generated -**/Cargo.lock linguist-generated \ No newline at end of file +**/Cargo.lock linguist-generated +rust/lance-file/test_data/**/*.lance binary diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fada4784ef0..e4183586a8c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -12,6 +12,7 @@ on: - rust/** - protos/** - .github/workflows/rust.yml + - ci/check_file_version_boundaries.py - rust-toolchain.toml - Cargo.toml - Cargo.lock @@ -41,6 +42,10 @@ jobs: components: rustfmt - name: Check formatting run: cargo fmt -- --check + - name: Check file-version boundaries + run: | + python3 ci/check_file_version_boundaries.py --self-test + python3 ci/check_file_version_boundaries.py rustdoc: runs-on: ubuntu-24.04 diff --git a/.gitignore b/.gitignore index 8143d39eef6..b916613b9ce 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,7 @@ test_data/venv **/*.profraw *.lance !test_data/**/*.lance +!rust/lance-file/test_data/**/*.lance # Pytest benchmarks .benchmarks/ diff --git a/.typos.toml b/.typos.toml index 9285dd52a23..30f5fb4965f 100644 --- a/.typos.toml +++ b/.typos.toml @@ -23,6 +23,7 @@ nprob = "nprobe" extend-exclude = [ "notebooks/*.ipynb", "*_THIRD_PARTY_LICENSES.*", + "rust/lance-file/test_data/exact_versions/*.lance", "rust/lance-tokenizer/src/stop_word_filter/stopwords.rs", ] # If a line ends with # or // and has spellchecker:disable-line, ignore it diff --git a/Cargo.lock b/Cargo.lock index 2a4f5cba47f..89b1ce841d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4739,6 +4739,7 @@ dependencies = [ "arrow-arith", "arrow-array", "arrow-buffer", + "arrow-cast", "arrow-data", "arrow-schema", "arrow-select", @@ -4765,6 +4766,7 @@ dependencies = [ "prost-build", "prost-types", "protobuf-src", + "rand 0.9.5", "rstest", "test-log", "tokio", @@ -4891,14 +4893,8 @@ name = "lance-io" version = "9.1.0-beta.8" dependencies = [ "arrow", - "arrow-arith", "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", "arrow-schema", - "arrow-select", - "async-recursion", "async-trait", "aws-config", "aws-credential-types", diff --git a/ci/check_file_version_boundaries.py b/ci/check_file_version_boundaries.py new file mode 100644 index 00000000000..b43e6d6f3ef --- /dev/null +++ b/ci/check_file_version_boundaries.py @@ -0,0 +1,1302 @@ +#!/usr/bin/env python3 + +"""Enforce the exact Lance file-version composition boundaries.""" + +from __future__ import annotations + +import argparse +import re +import sys +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] + +EXACT_VERSION_MODULES = ("v1", "v2_0", "v2_1", "v2_2", "v2_3") + +VERSION_SELECTOR_ALLOWLIST = { + Path("rust/lance-file/src/version.rs"), + Path("rust/lance/src/dataset/write.rs"), + Path("rust/lance/src/dataset/write/insert.rs"), + Path("rust/lance/src/dataset/write/commit.rs"), +} + +EXACT_COMPARISON_ALLOWLIST = { + Path("rust/lance-file/src/reader.rs"), + Path("rust/lance-file/src/version.rs"), + Path("rust/lance-table/src/format/fragment.rs"), + Path("rust/lance-table/src/format/manifest.rs"), + Path("rust/lance/src/dataset/transaction.rs"), +} + +EXACT_IDENTITY_ALLOWLIST = { + # Persisted identity codecs and file open/create boundaries. + Path("rust/lance-file/src/lib.rs"), + Path("rust/lance-file/src/reader.rs"), + Path("rust/lance-file/src/version.rs"), + # Persisted manifest and DataFile identity ownership. + Path("rust/lance-table/src/format/fragment.rs"), + Path("rust/lance-table/src/format/manifest.rs"), + Path("rust/lance-table/src/io/manifest.rs"), + # Dataset API, persisted validation, and explicit physical-file boundaries. + Path("rust/lance/src/dataset/index.rs"), + Path("rust/lance/src/dataset/mem_wal/memtable/flush.rs"), + Path("rust/lance/src/dataset/optimize/binary_copy.rs"), + Path("rust/lance/src/dataset/transaction.rs"), + Path("rust/lance/src/dataset/write.rs"), + Path("rust/lance/src/dataset/write/commit.rs"), + Path("rust/lance/src/dataset/write/insert.rs"), + Path("rust/lance/src/index/vector/builder.rs"), + # lance-index owns explicit physical Lance files, not dataset format policy. + Path("rust/lance-index/src/scalar/lance_format.rs"), + Path("rust/lance-index/src/vector/distributed/index_merger.rs"), + Path("rust/lance-index/src/vector/ivf/shuffler.rs"), + Path("rust/lance-index/src/vector/v3/shuffler.rs"), +} + +LEGACY_READER_ESCAPE_HATCHES = ( + "as_legacy_opt", + "as_legacy_opt_mut", + "legacy_read_page_stats", + "legacy_read_batch_projected", + "legacy_row_group_size", +) + +PREVIOUS_FILE_IDENTITY_ALIASES = ( + "PreviousFileReader", + "PreviousFileWriter", + "PreviousFileWriterOptions", + "PreviousManifestProvider", + "PreviousIndexReader", + "previous_read_batch", + "previous_write_batch", +) + +VERSION_CAPABILITY_ESCAPE_HATCHES = ( + "BINARY_COPY_SUPPORTED", + "PhysicalColumnLayout", + "binary_copy_supported", + "has_legacy_files", + "is_legacy_file", + "is_external_metadata_structural_header", + "lance_supports_nulls", + "nulls_supported", + "physical_column_layout", + "scan_strategy", + "should_load_indexed_metadata", + "should_try_indexed_metadata", + "should_use_legacy_format", + "supports_indexed_metadata", + "supports_indexed_projection", +) + +FORBIDDEN_VERSION_POLICY_PROXIES = ( + "BlobWrite", + "IndexedMetadataSupport", + "ScanStrategy", + "WriteBatching", +) + +FORBIDDEN_SHARED_WRITER_PROFILE_FIELDS = ( + "format_version", + "version", + "encoding_strategy", + "footer_numbers", + "major_version", + "minor_version", + "column_layout", + "page_encoding", + "buffer_alignment", +) + +FORBIDDEN_SHARED_READER_PROFILE_FIELDS = ( + "version", + "encoding_strategy", + "footer_numbers", + "major_version", + "minor_version", + "column_layout", + "page_encoding", + "buffer_alignment", + "accepted_grammar", +) + +FORBIDDEN_EXACT_CONSTRUCTOR_INPUTS = ( + "LanceFileVersion", + "ConcreteFileVersion", + "footer_numbers", + "major_version", + "minor_version", + "column_layout", + "page_encoding", + "buffer_alignment", + "encoding_strategy", +) + +FORBIDDEN_ENCODING_PROFILE_TYPES = ( + "BlobEncoding", + "BlockCompression", + "ConstantEncoding", + "DefaultCompressionStrategy", + "FixedSizeListEncoding", + "MapEncoding", + "MinichunkSize", + "PackedStructEncoding", + "PrimitiveEncodingOptions", + "RleEncoding", + "SparseEncoding", +) + +FORBIDDEN_ENCODING_VERSION_MIRRORS = ( + "BenchFileVersion", + "TestFileVersion", +) + +FORBIDDEN_ORDERED_ENCODING_TEST_APIS = ( + "with_max_file_version", + "with_min_file_version", +) + +FORBIDDEN_COMPRESSION_SELECTION_ABSTRACTIONS = ( + "CompressionAtom", + "CompressionPipeline", +) + +FORBIDDEN_FIELD_SELECTION_ABSTRACTIONS = ( + "FieldEncodingAtom", + "StructuralEncodingStrategy", +) + +ENCODING_COMPOSITION_CONTAINERS = { + Path("rust/lance-encoding/src/encoder/structural.rs"): { + "PrimitiveFieldEncoding": {"page_encodings"}, + }, + Path("rust/lance-encoding/src/encodings/logical/primitive.rs"): { + "PrimitivePageEncoding": {"behavior"}, + }, + Path("rust/lance-file/src/versions/v2_1/compression.rs"): { + "Strategy": {"params"}, + }, + Path("rust/lance-file/src/versions/v2_2/compression.rs"): { + "Strategy": {"params"}, + }, + Path("rust/lance-file/src/versions/v2_3/compression.rs"): { + "Strategy": {"params"}, + }, +} + + +def is_test_source(path: Path) -> bool: + return ( + "tests" in path.parts + or "benches" in path.parts + or path.name in {"test.rs", "testing.rs"} + ) + + +def mask_span(source: str, start: int, end: int) -> str: + masked = "".join("\n" if char == "\n" else " " for char in source[start:end]) + return source[:start] + masked + source[end:] + + +def strip_cfg_test_items(source: str) -> str: + pattern = re.compile(r"#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]") + while match := pattern.search(source): + item_start = match.start() + cursor = match.end() + while cursor < len(source) and source[cursor].isspace(): + cursor += 1 + + brace = source.find("{", cursor) + semicolon = source.find(";", cursor) + if semicolon != -1 and (brace == -1 or semicolon < brace): + item_end = semicolon + 1 + elif brace != -1: + depth = 0 + item_end = len(source) + for index in range(brace, len(source)): + if source[index] == "{": + depth += 1 + elif source[index] == "}": + depth -= 1 + if depth == 0: + item_end = index + 1 + break + else: + item_end = len(source) + source = mask_span(source, item_start, item_end) + return source + + +def strip_comments_and_strings(source: str) -> str: + output = list(source) + index = 0 + block_depth = 0 + in_string = False + while index < len(source): + if block_depth: + if source.startswith("/*", index): + block_depth += 1 + output[index : index + 2] = " " + index += 2 + elif source.startswith("*/", index): + block_depth -= 1 + output[index : index + 2] = " " + index += 2 + else: + if source[index] != "\n": + output[index] = " " + index += 1 + continue + + if in_string: + if source[index] == "\\": + output[index] = " " + if index + 1 < len(source): + output[index + 1] = " " + index += 2 + else: + if source[index] == '"': + in_string = False + if source[index] != "\n": + output[index] = " " + index += 1 + continue + + if source.startswith("//", index): + line_end = source.find("\n", index) + if line_end == -1: + line_end = len(source) + output[index:line_end] = " " * (line_end - index) + index = line_end + elif source.startswith("/*", index): + block_depth = 1 + output[index : index + 2] = " " + index += 2 + elif source[index] == '"': + in_string = True + output[index] = " " + index += 1 + else: + index += 1 + return "".join(output) + + +def production_source(source: str) -> str: + return strip_comments_and_strings(strip_cfg_test_items(source)) + + +def line_number(source: str, offset: int) -> int: + return source.count("\n", 0, offset) + 1 + + +def enum_derives(source: str, enum_name: str) -> set[str]: + match = re.search( + rf"#\s*\[\s*derive\s*\((?P[^)]*)\)\s*\]\s*pub\s+enum\s+{enum_name}\b", + source, + flags=re.DOTALL, + ) + if match is None: + return set() + return {trait.strip().split("::")[-1] for trait in match.group("traits").split(",")} + + +def struct_body(source: str, struct_name: str) -> str: + match = re.search(rf"\bstruct\s+{struct_name}\b[^{{]*{{", source) + if match is None: + return "" + brace = source.find("{", match.start()) + depth = 0 + for index in range(brace, len(source)): + if source[index] == "{": + depth += 1 + elif source[index] == "}": + depth -= 1 + if depth == 0: + return source[brace + 1 : index] + return source[brace + 1 :] + + +def struct_fields(source: str, struct_name: str) -> set[str]: + body = struct_body(source, struct_name) + return { + match.group("name") + for match in re.finditer( + r"(?:^|,)\s*(?:pub(?:\s*\([^)]*\))?\s+)?" + r"(?P[A-Za-z_][A-Za-z0-9_]*)\s*:", + body, + flags=re.MULTILINE, + ) + } + + +def public_function_parameters(source: str) -> list[tuple[str, str, int]]: + functions = [] + pattern = re.compile( + r"\bpub(?:\s*\([^)]*\))?\s+(?:async\s+)?fn\s+" + r"(?P[A-Za-z_][A-Za-z0-9_]*)\s*" + ) + for match in pattern.finditer(source): + opening = source.find("(", match.end()) + if opening == -1: + continue + depth = 0 + for index in range(opening, len(source)): + if source[index] == "(": + depth += 1 + elif source[index] == ")": + depth -= 1 + if depth == 0: + functions.append( + ( + match.group("name"), + source[opening + 1 : index], + line_number(source, match.start()), + ) + ) + break + return functions + + +def exact_version_module(relative: Path) -> str | None: + prefix = ("rust", "lance-file", "src", "versions") + if relative.parts[: len(prefix)] != prefix or len(relative.parts) <= len(prefix): + return None + candidate = relative.parts[len(prefix)] + return candidate if candidate in EXACT_VERSION_MODULES else None + + +def sibling_version_references(source: str, current: str) -> list[tuple[int, str]]: + references = [] + version_pattern = "|".join(re.escape(version) for version in EXACT_VERSION_MODULES) + patterns = ( + re.compile(rf"\b(?:crate::)?versions::(?P{version_pattern})\b"), + re.compile(rf"\b(?:super::)+(?P{version_pattern})\b"), + re.compile( + r"\b(?:pub(?:\s*\([^)]*\))?\s+)?use\s+" + r"(?P[^;]*\bversions\b[^;]*);", + flags=re.DOTALL, + ), + ) + seen: set[tuple[int, str]] = set() + for pattern in patterns: + for match in pattern.finditer(source): + if "statement" in match.groupdict(): + for version_match in re.finditer( + rf"\b(?P{version_pattern})\b", + match.group("statement"), + ): + version = version_match.group("version") + offset = match.start("statement") + version_match.start() + if version != current: + seen.add((offset, version)) + else: + version = match.group("version") + if version != current: + seen.add((match.start("version"), version)) + for offset, version in sorted(seen): + references.append((line_number(source, offset), version)) + return references + + +def comparison_violations(source: str) -> list[tuple[int, str]]: + violations = [] + patterns = ( + re.compile( + r"(?:ConcreteFileVersion|LanceFileVersion)::[A-Za-z0-9_]+" + r"[^\n;]*(?:==|!=|>=|<=|\.max\s*\(|\.min\s*\()" + ), + re.compile( + r"(?:==|!=|>=|<=|\.max\s*\(|\.min\s*\()[^\n;]*" + r"(?:ConcreteFileVersion|LanceFileVersion)::[A-Za-z0-9_]+" + ), + ) + for pattern in patterns: + for match in pattern.finditer(source): + violations.append( + (line_number(source, match.start()), match.group(0).strip()) + ) + + aliases = {"ConcreteFileVersion", "LanceFileVersion"} + aliases.update( + match.group("alias") + for match in re.finditer( + r"\b(?:ConcreteFileVersion|LanceFileVersion)\s+as\s+(?P[A-Za-z_][A-Za-z0-9_]*)", + source, + ) + ) + type_pattern = "|".join(re.escape(alias) for alias in sorted(aliases)) + typed_variables = { + match.group("name") + for match in re.finditer( + rf"\b(?P[A-Za-z_][A-Za-z0-9_]*)\s*:\s*(?:&\s*)?(?:Option\s*<\s*)?(?:{type_pattern})\b", + source, + ) + } + typed_variables.update( + match.group("name") + for match in re.finditer( + r"\blet\s+(?:mut\s+)?(?P[A-Za-z_][A-Za-z0-9_]*)" + r"(?:\s*:\s*[^=;]+)?\s*=\s*[^;]*" + r"(?:" + r"\.lance_file_format\s*\(" + r"|\.file_version\s*\(" + r"|\bdetermine_file_version\s*\(" + r"|\bstable_file_version\s*\(" + r"|\.resolve\s*\(" + r")", + source, + ) + ) + for variable in typed_variables: + standalone_variable = rf"(?=|<=)"), + re.compile(rf"(?:==|!=|>=|<=)\s*\b{re.escape(variable)}\b"), + re.compile(rf"{standalone_variable}\s*\.\s*(?:max|min)\s*\("), + re.compile(rf"\bmatch\s+{re.escape(variable)}\b"), + ) + for pattern in variable_patterns: + for match in pattern.finditer(source): + violations.append( + (line_number(source, match.start()), match.group(0).strip()) + ) + return violations + + +def exact_identity_allowed(relative: Path) -> bool: + return ( + relative in EXACT_IDENTITY_ALLOWLIST + or relative.is_relative_to("rust/lance-file/src/versions") + or relative.is_relative_to("rust/lance/src/dataset/versions") + ) + + +def check_source(path: Path, source: str) -> list[str]: + relative = path if not path.is_absolute() else path.relative_to(REPO_ROOT) + source_without_comments = strip_comments_and_strings(source) + errors: list[str] = [] + + if ( + relative == Path("rust/lance-io/src/encodings.rs") + or relative.is_relative_to("rust/lance-io/src/encodings") + or re.search(r"\blance_io\s*::\s*encodings\b", source_without_comments) + or re.search( + r"\blance_io\s*::\s*\{[^}]*\bencodings\b", + source_without_comments, + flags=re.DOTALL, + ) + or ( + relative.is_relative_to("rust/lance-io/src") + and re.search( + r"\b(?:pub\s+)?mod\s+encodings\b" + r"|\b(?:crate|super)::encodings\b", + source_without_comments, + ) + ) + ): + errors.append( + f"{relative}: v1 encoding grammar leaked into lance-io; " + "use lance-file::versions::v1::encoding" + ) + + if ( + relative == Path("rust/lance-encoding/src/previous.rs") + or relative.is_relative_to("rust/lance-encoding/src/previous") + or re.search(r"\blance_encoding\s*::\s*previous\b", source_without_comments) + or re.search( + r"\blance_encoding\s*::\s*\{[^}]*\bprevious\b", + source_without_comments, + flags=re.DOTALL, + ) + or ( + relative.is_relative_to("rust/lance-encoding/src") + and re.search( + r"\b(?:pub\s+)?mod\s+previous\b" + r"|\b(?:crate|super)::previous\b", + source_without_comments, + ) + ) + ): + errors.append( + f"{relative}: time-relative lance-encoding facade remains; " + "use array_encoding or the shared encoder/decoder runtime" + ) + + if relative.is_relative_to("rust/lance-encoding"): + for name in FORBIDDEN_ENCODING_VERSION_MIRRORS: + if re.search(rf"\b{re.escape(name)}\b", source_without_comments): + errors.append( + f"{relative}: encoding test/bench mirrors file identity as {name}; " + "name the executable encoding semantics instead" + ) + for name in FORBIDDEN_ORDERED_ENCODING_TEST_APIS: + if re.search(rf"\b{re.escape(name)}\b", source_without_comments): + errors.append( + f"{relative}: ordered encoding test gate remains: {name}; " + "list the intended semantic encodings explicitly" + ) + for enum_name in ("BenchEncoding", "TestEncoding"): + forbidden = enum_derives(source_without_comments, enum_name) & { + "Ord", + "PartialOrd", + } + if forbidden: + errors.append( + f"{relative}: {enum_name} derives forbidden ordering trait(s): " + + ", ".join(sorted(forbidden)) + ) + + if is_test_source(relative): + return errors + + production = production_source(source) + + if ( + "ArrayFieldEncodingStrategy" in production + and not relative.is_relative_to("rust/lance-encoding") + and not relative.is_relative_to("rust/lance-file/src/versions/v2_0") + ): + errors.append( + f"{relative}: pb::ArrayEncoding field composition escaped the v2.0 " + "file-version owner" + ) + + for name in FORBIDDEN_COMPRESSION_SELECTION_ABSTRACTIONS: + if re.search(rf"\b{re.escape(name)}\b", production): + errors.append( + f"{relative}: redundant compression selection abstraction remains: {name}; " + "exact versions must implement CompressionStrategy directly" + ) + + for name in FORBIDDEN_FIELD_SELECTION_ABSTRACTIONS: + if re.search(rf"\b{re.escape(name)}\b", production): + errors.append( + f"{relative}: redundant field selection abstraction remains: {name}; " + "exact versions must implement FieldEncodingStrategy directly" + ) + + for name in FORBIDDEN_VERSION_POLICY_PROXIES: + if re.search(rf"\b{re.escape(name)}\b", production): + errors.append( + f"{relative}: version policy proxy remains: {name}; " + "dispatch the complete operation at the exact-version boundary" + ) + + if relative.parts[:3] == ("rust", "lance-encoding", "src"): + for name in FORBIDDEN_ENCODING_PROFILE_TYPES: + if re.search(rf"\b{re.escape(name)}\b", production): + errors.append( + f"{relative}: encoding capability/profile type remains: {name}; " + "compose exact executable strategies instead" + ) + + for struct_name, allowed_fields in ENCODING_COMPOSITION_CONTAINERS.get( + relative, {} + ).items(): + fields = struct_fields(production, struct_name) + if fields and fields != allowed_fields: + errors.append( + f"{relative}: {struct_name} must contain only " + f"{', '.join(sorted(allowed_fields))}; found " + f"{', '.join(sorted(fields))}" + ) + + dataset_versions = Path("rust/lance/src/dataset/versions") + if relative.parent == dataset_versions and relative.name != "mod.rs": + errors.append( + f"{relative}: dataset per-version mirror modules are forbidden; " + "dispatch each real operation-level difference from versions/mod.rs" + ) + + if relative.is_relative_to("rust/lance-file/src/previous"): + errors.append( + f"{relative}: the legacy file implementation must live under versions/v1" + ) + + if re.search(r"\blance_file::previous\b", production) or ( + relative.is_relative_to("rust/lance-file/src") + and re.search( + r"\b(?:pub\s+)?mod\s+previous\b|\b(?:crate|super)::previous\b", + production, + ) + ): + errors.append( + f"{relative}: legacy lance-file facade remains; use versions::v1 directly" + ) + + if relative.parts[:3] == ("rust", "lance-encoding", "src"): + for version_type in ("LanceFileVersion", "ConcreteFileVersion"): + if version_type in production: + errors.append( + f"{relative}: lance-encoding production code imports {version_type}" + ) + + if relative == Path("rust/lance-file/src/version.rs"): + for enum_name in ("LanceFileVersion", "ConcreteFileVersion"): + forbidden = enum_derives(production, enum_name) & {"Ord", "PartialOrd"} + if forbidden: + errors.append( + f"{relative}: {enum_name} derives forbidden ordering trait(s): " + + ", ".join(sorted(forbidden)) + ) + + if ( + "LanceFileVersion" in production + and relative not in VERSION_SELECTOR_ALLOWLIST + and not relative.is_relative_to("rust/lance-file/src/versions") + ): + errors.append( + f"{relative}: public selector leaked past an API or release-policy boundary" + ) + + if "ConcreteFileVersion" in production and not exact_identity_allowed(relative): + errors.append( + f"{relative}: exact file identity leaked past a persisted or dispatch boundary" + ) + + comparison_allowed = ( + relative in EXACT_COMPARISON_ALLOWLIST + or relative.is_relative_to("rust/lance-file/src/versions") + or relative.is_relative_to("rust/lance/src/dataset/versions") + ) + if not comparison_allowed: + for line, expression in comparison_violations(production): + errors.append( + f"{relative}:{line}: file-version comparison outside a dispatch boundary: " + f"{expression}" + ) + + for name in LEGACY_READER_ESCAPE_HATCHES: + if name in production: + errors.append( + f"{relative}: legacy GenericFileReader escape hatch remains: {name}" + ) + + for name in PREVIOUS_FILE_IDENTITY_ALIASES: + if re.search(rf"\b{re.escape(name)}\b", production): + errors.append( + f"{relative}: legacy file identity alias remains: {name}; use a v1 name" + ) + + if not ( + relative.is_relative_to("rust/lance-file/src/versions") + or relative.is_relative_to("rust/lance/src/dataset/versions") + ): + for name in VERSION_CAPABILITY_ESCAPE_HATCHES: + pattern = ( + rf"\b{re.escape(name)}\b" + if name + in {"BINARY_COPY_SUPPORTED", "PhysicalColumnLayout", "ScanStrategy"} + else rf"\b{re.escape(name)}\s*\(" + ) + if re.search(pattern, production): + errors.append( + f"{relative}: file-version capability escape hatch remains: {name}" + ) + + if current_version := exact_version_module(relative): + for line, referenced_version in sibling_version_references( + production, current_version + ): + errors.append( + f"{relative}:{line}: {current_version} implementation references " + f"sibling version module {referenced_version}" + ) + + if current_version != "v1": + for reader_type in ("Reader", "ProjectedReader"): + if re.search( + rf"\b(?:pub(?:\s*\([^)]*\))?\s+)?struct\s+{reader_type}\b", + production, + ): + errors.append( + f"{relative}: exact {current_version} reader wrapper " + f"{reader_type} is forbidden; return the shared runtime " + "after exact grammar selection" + ) + + for function_name, parameters, line in public_function_parameters(production): + if "FieldEncodingStrategy" in parameters: + errors.append( + f"{relative}:{line}: public exact-version API {function_name} " + "accepts a complete FieldEncodingStrategy override" + ) + if function_name.startswith( + ("create_writer", "create_lazy_writer", "try_new", "new_lazy") + ): + for forbidden in FORBIDDEN_EXACT_CONSTRUCTOR_INPUTS: + if re.search(rf"\b{re.escape(forbidden)}\b", parameters): + errors.append( + f"{relative}:{line}: exact-version constructor " + f"{function_name} accepts forbidden format input {forbidden}" + ) + + shared_writer_structs: tuple[str, ...] = () + if relative == Path("rust/lance-file/src/writer.rs"): + shared_writer_structs = ("FileWriterOptions", "FileWriter") + elif relative.is_relative_to("rust/lance-file/src/writer"): + shared_writer_structs = ( + "StructuralFileSink", + "EncodingPipeline", + "EncodedBatchBody", + ) + if shared_writer_structs: + for struct_name in shared_writer_structs: + body = struct_body(production, struct_name) + for field in FORBIDDEN_SHARED_WRITER_PROFILE_FIELDS: + if re.search(rf"\b{re.escape(field)}\s*:", body): + errors.append( + f"{relative}: shared {struct_name} carries format-profile " + f"field {field}" + ) + for function_name, parameters, line in public_function_parameters(production): + if "FieldEncodingStrategy" in parameters: + errors.append( + f"{relative}:{line}: shared writer API {function_name} accepts " + "a complete FieldEncodingStrategy override" + ) + if relative.is_relative_to("rust/lance-file/src/writer"): + for field in FORBIDDEN_SHARED_WRITER_PROFILE_FIELDS: + if re.search(rf"\b{re.escape(field)}\b", parameters): + errors.append( + f"{relative}:{line}: shared writer mechanism " + f"{function_name} accepts format-profile input {field}" + ) + + if relative == Path("rust/lance-file/src/reader.rs"): + for reader_type in ("FileReader", "ProjectedFileReader"): + if re.search(rf"\bpub\s+enum\s+{reader_type}\b", production): + errors.append( + f"{relative}: shared {reader_type} must not dispatch every " + "runtime method through exact-version variants" + ) + body = struct_body(production, "DecodeEngine") + for field in FORBIDDEN_SHARED_READER_PROFILE_FIELDS: + if re.search(rf"\b{re.escape(field)}\s*:", body): + errors.append( + f"{relative}: shared DecodeEngine carries format-profile field {field}" + ) + if body and not re.search( + r"\bread_projection\s*:\s*Arc\s*<\s*dyn\s+ReadProjection\s*>", + body, + ): + errors.append( + f"{relative}: shared DecodeEngine must carry only the executable " + "ReadProjection selected at the exact-version boundary" + ) + elif relative.is_relative_to("rust/lance-file/src/reader"): + for function_name, parameters, line in public_function_parameters(production): + for field in FORBIDDEN_SHARED_READER_PROFILE_FIELDS: + if re.search(rf"\b{re.escape(field)}\b", parameters): + errors.append( + f"{relative}:{line}: shared reader mechanism " + f"{function_name} accepts format-profile input {field}" + ) + + if relative == Path("rust/lance-encoding/src/encoder.rs"): + body = struct_body(production, "EncodingOptions") + if re.search(r"\bversion\s*:", body): + errors.append(f"{relative}: EncodingOptions still carries a file version") + + if relative == Path("rust/lance/src/dataset/write.rs"): + body = struct_body(production, "WriterGenerator") + if re.search(r"\b(?:ConcreteFileVersion|LanceFileVersion)\b", body): + errors.append( + f"{relative}: shared WriterGenerator stores file identity instead of " + "an already selected writer factory" + ) + for function_name, parameters, line in public_function_parameters(production): + if function_name == "do_write_fragments_impl" and re.search( + r"\b(?:ConcreteFileVersion|LanceFileVersion)\b", parameters + ): + errors.append( + f"{relative}:{line}: shared write loop accepts file identity instead " + "of already selected batching and writer behavior" + ) + + return errors + + +def run_checks() -> list[str]: + errors = [] + for path in sorted((REPO_ROOT / "rust").rglob("*.rs")): + errors.extend(check_source(path, path.read_text(encoding="utf-8"))) + return errors + + +def run_self_test() -> None: + illegal_comparison = """ +use lance_file::version::ConcreteFileVersion; +fn leaked(version: ConcreteFileVersion) { + if version >= ConcreteFileVersion::V2_2 {} +} +""" + errors = check_source(Path("rust/example/src/leak.rs"), illegal_comparison) + if not any("comparison outside a dispatch boundary" in error for error in errors): + raise AssertionError("illegal file-version comparison was not rejected") + + variable_comparison = """ +use lance_file::version::ConcreteFileVersion; +fn leaked(left: ConcreteFileVersion, right: ConcreteFileVersion) { + if left != right {} +} +""" + errors = check_source(Path("rust/example/src/leak.rs"), variable_comparison) + if not any("comparison outside a dispatch boundary" in error for error in errors): + raise AssertionError( + "variable-to-variable file-version comparison was not rejected" + ) + + inferred_variable_comparison = """ +fn leaked(manifest: &Manifest) { + let actual = determine_file_version(); + let expected = manifest.data_storage_format.lance_file_format(); + if actual != expected {} +} +""" + errors = check_source( + Path("rust/example/src/leak.rs"), inferred_variable_comparison + ) + if not any("comparison outside a dispatch boundary" in error for error in errors): + raise AssertionError("inferred file-version comparison was not rejected") + + typed_identity = """ +use lance_file::version::ConcreteFileVersion; +fn leaked(_version: ConcreteFileVersion) {} +""" + errors = check_source(Path("rust/example/src/leak.rs"), typed_identity) + if not any("exact file identity leaked" in error for error in errors): + raise AssertionError("deep exact file identity was not rejected") + + aliased_identity = """ +use lance_file::version::ConcreteFileVersion as FileVersion; +fn leaked(_version: FileVersion) {} +""" + errors = check_source(Path("rust/example/src/leak.rs"), aliased_identity) + if not any("exact file identity leaked" in error for error in errors): + raise AssertionError("aliased exact file identity was not rejected") + + exhaustive_match = """ +use lance_file::version::ConcreteFileVersion; +fn leaked(version: ConcreteFileVersion) { + match version { + ConcreteFileVersion::V1 => {} + _ => {} + } +} +""" + errors = check_source(Path("rust/example/src/leak.rs"), exhaustive_match) + if not any("comparison outside a dispatch boundary" in error for error in errors): + raise AssertionError("deep exact-version match was not rejected") + + illegal_selector = """ +use lance_file::version::LanceFileVersion; +fn leaked(_version: LanceFileVersion) {} +""" + errors = check_source(Path("rust/example/src/leak.rs"), illegal_selector) + if not any("public selector leaked" in error for error in errors): + raise AssertionError("deep public selector use was not rejected") + + test_only_selector = """ +#[cfg(test)] +mod tests { + use lance_file::version::LanceFileVersion; +} +""" + if check_source(Path("rust/example/src/lib.rs"), test_only_selector): + raise AssertionError("test-only selector use was rejected") + + encoding_profile = """ +pub struct DefaultCompressionStrategy { + block_compression: BlockCompression, +} +""" + errors = check_source( + Path("rust/lance-encoding/src/compression.rs"), encoding_profile + ) + if not any("encoding capability/profile type remains" in error for error in errors): + raise AssertionError("encoding capability profile was not rejected") + + test_version_mirror = """ +enum TestFileVersion { + V2_1, + V2_2, +} +""" + errors = check_source( + Path("rust/lance-encoding/src/testing.rs"), test_version_mirror + ) + if not any("mirrors file identity" in error for error in errors): + raise AssertionError("encoding test file-version mirror was not rejected") + + ordered_test_encoding = """ +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub enum TestEncoding { + Legacy, + Structural, +} +""" + errors = check_source( + Path("rust/lance-encoding/src/testing.rs"), ordered_test_encoding + ) + if not any("forbidden ordering trait" in error for error in errors): + raise AssertionError("ordered semantic test encoding was not rejected") + + ordered_test_gate = """ +fn with_min_file_version(version: TestEncoding) {} +""" + errors = check_source(Path("rust/lance-encoding/src/testing.rs"), ordered_test_gate) + if not any("ordered encoding test gate" in error for error in errors): + raise AssertionError("ordered encoding test API was not rejected") + + redundant_field_selection = """ +pub trait FieldEncodingAtom { + fn try_create_field_encoder(&self); +} +""" + errors = check_source( + Path("rust/lance-encoding/src/encoder/structural.rs"), + redundant_field_selection, + ) + if not any("redundant field selection abstraction" in error for error in errors): + raise AssertionError("redundant field selection abstraction was not rejected") + + primitive_capability_bag = """ +struct PrimitivePageEncoding { + behavior: Arc, + supports_constant: bool, +} +""" + errors = check_source( + Path("rust/lance-encoding/src/encodings/logical/primitive.rs"), + primitive_capability_bag, + ) + if not any("must contain only behavior" in error for error in errors): + raise AssertionError("primitive encoding capability bag was not rejected") + + redundant_compression_atom = """ +pub trait CompressionAtom { + fn try_create_block(&self); +} +""" + errors = check_source( + Path("rust/lance-encoding/src/compression.rs"), + redundant_compression_atom, + ) + if not any( + "redundant compression selection abstraction" in error for error in errors + ): + raise AssertionError("redundant compression atom was not rejected") + + exact_compression_capability_bag = """ +struct Strategy { + params: CompressionParams, + supports_block: bool, +} +""" + errors = check_source( + Path("rust/lance-file/src/versions/v2_2/compression.rs"), + exact_compression_capability_bag, + ) + if not any("must contain only params" in error for error in errors): + raise AssertionError("exact compression capability bag was not rejected") + + legacy_escape_hatch = """ +fn leaked(reader: &GenericFileReader) { + reader.as_legacy_opt(); +} +""" + errors = check_source(Path("rust/example/src/leak.rs"), legacy_escape_hatch) + if not any("legacy GenericFileReader escape hatch" in error for error in errors): + raise AssertionError("legacy reader escape hatch was not rejected") + + capability_lookup = """ +fn leaked(version: Version) { + let strategy = versions::scan_strategy(version); +} +""" + errors = check_source(Path("rust/example/src/leak.rs"), capability_lookup) + if not any("file-version capability escape hatch" in error for error in errors): + raise AssertionError("deep version capability lookup was not rejected") + + indexed_metadata_capability = """ +fn leaked(version: Version) { + if versions::supports_indexed_metadata(version) {} +} +""" + errors = check_source(Path("rust/example/src/leak.rs"), indexed_metadata_capability) + if not any("file-version capability escape hatch" in error for error in errors): + raise AssertionError("indexed metadata capability was not rejected") + + write_policy_proxy = """ +enum WriteBatching { + RowGroup, + File, +} +""" + errors = check_source( + Path("rust/lance/src/dataset/versions/mod.rs"), write_policy_proxy + ) + if not any("version policy proxy remains" in error for error in errors): + raise AssertionError("dataset write policy proxy was not rejected") + + legal_persisted_validation = """ +use lance_file::version::ConcreteFileVersion; +fn validate(actual: ConcreteFileVersion, expected: ConcreteFileVersion) -> bool { + actual == expected +} +""" + if check_source( + Path("rust/lance-table/src/format/fragment.rs"), legal_persisted_validation + ): + raise AssertionError("persisted identity validation was rejected") + + shared_writer_identity = """ +use lance_file::version::ConcreteFileVersion; +struct FileWriter { + version: ConcreteFileVersion, +} +""" + errors = check_source(Path("rust/lance-file/src/writer.rs"), shared_writer_identity) + if not any("exact file identity leaked" in error for error in errors): + raise AssertionError("shared writer exact identity was not rejected") + + shared_writer_generator_identity = """ +use lance_file::version::ConcreteFileVersion; +struct WriterGenerator { + version: ConcreteFileVersion, +} +""" + errors = check_source( + Path("rust/lance/src/dataset/write.rs"), shared_writer_generator_identity + ) + if not any( + "shared WriterGenerator stores file identity" in error for error in errors + ): + raise AssertionError("shared writer generator exact identity was not rejected") + + shared_write_loop_identity = """ +use lance_file::version::ConcreteFileVersion; +pub(super) async fn do_write_fragments_impl( + version: ConcreteFileVersion, +) {} +""" + errors = check_source( + Path("rust/lance/src/dataset/write.rs"), shared_write_loop_identity + ) + if not any("shared write loop accepts file identity" in error for error in errors): + raise AssertionError("shared write loop exact identity was not rejected") + + sibling_import = """ +use crate::versions::v2_2::reader::Reader; +""" + errors = check_source( + Path("rust/lance-file/src/versions/v2_3/writer.rs"), sibling_import + ) + if not any("references sibling version module" in error for error in errors): + raise AssertionError( + "sibling exact-version implementation import was not rejected" + ) + + sibling_reexport = """ +pub use super::v2_1::Writer; +""" + errors = check_source( + Path("rust/lance-file/src/versions/v2_2/mod.rs"), sibling_reexport + ) + if not any("references sibling version module" in error for error in errors): + raise AssertionError( + "sibling exact-version implementation re-export was not rejected" + ) + + shared_writer_profile = """ +struct FileWriterOptions { + encoding_strategy: Arc, +} +""" + errors = check_source(Path("rust/lance-file/src/writer.rs"), shared_writer_profile) + if not any("carries format-profile field" in error for error in errors): + raise AssertionError("shared writer format profile was not rejected") + + shared_writer_mechanism_profile = """ +struct StructuralFileSink { + minor_version: u16, +} +""" + errors = check_source( + Path("rust/lance-file/src/writer/structural.rs"), + shared_writer_mechanism_profile, + ) + if not any("carries format-profile field" in error for error in errors): + raise AssertionError("shared writer mechanism format profile was not rejected") + + shared_writer_mechanism_input = """ +pub fn finish(minor_version: u16) {} +""" + errors = check_source( + Path("rust/lance-file/src/writer/structural.rs"), + shared_writer_mechanism_input, + ) + if not any("accepts format-profile input" in error for error in errors): + raise AssertionError("shared writer mechanism profile input was not rejected") + + shared_reader_profile = """ +struct DecodeEngine { + page_encoding: PageEncoding, +} +""" + errors = check_source(Path("rust/lance-file/src/reader.rs"), shared_reader_profile) + if not any("carries format-profile field" in error for error in errors): + raise AssertionError("shared reader format profile was not rejected") + + shared_reader_mechanism_input = """ +pub fn parse(accepted_grammar: AcceptedGrammar) {} +""" + errors = check_source( + Path("rust/lance-file/src/reader/structural.rs"), + shared_reader_mechanism_input, + ) + if not any("accepts format-profile input" in error for error in errors): + raise AssertionError("shared reader mechanism profile input was not rejected") + + exact_reader_wrapper = """ +pub struct Reader { + state: FullReaderState, +} +""" + errors = check_source( + Path("rust/lance-file/src/versions/v2_2/reader.rs"), + exact_reader_wrapper, + ) + if not any("exact v2_2 reader wrapper" in error for error in errors): + raise AssertionError("exact reader runtime wrapper was not rejected") + + shared_reader_dispatch = """ +pub enum FileReader { + V2_1(V21Reader), + V2_2(V22Reader), +} +""" + errors = check_source( + Path("rust/lance-file/src/reader.rs"), + shared_reader_dispatch, + ) + if not any("must not dispatch every runtime method" in error for error in errors): + raise AssertionError("shared per-version reader dispatch was not rejected") + + executable_reader_atom = """ +struct DecodeEngine { + read_projection: Arc, +} +""" + if check_source(Path("rust/lance-file/src/reader.rs"), executable_reader_atom): + raise AssertionError("executable reader projection atom was rejected") + + complete_strategy_override = """ +pub fn create_writer( + strategy: Arc, +) -> Writer { + todo!() +} +""" + errors = check_source( + Path("rust/lance-file/src/versions/v2_1/mod.rs"), + complete_strategy_override, + ) + if not any("complete FieldEncodingStrategy override" in error for error in errors): + raise AssertionError( + "complete exact-version strategy override was not rejected" + ) + + constructor_format_input = """ +pub fn new_lazy( + version: ConcreteFileVersion, + options: FileWriterOptions, +) -> Writer { + todo!() +} +""" + errors = check_source( + Path("rust/lance-file/src/versions/v2_1/writer.rs"), + constructor_format_input, + ) + if not any("constructor new_lazy accepts forbidden" in error for error in errors): + raise AssertionError("exact-version constructor format input was not rejected") + + dataset_version_mirror = """ +pub fn scan() {} +""" + errors = check_source( + Path("rust/lance/src/dataset/versions/v2_2.rs"), dataset_version_mirror + ) + if not any("dataset per-version mirror modules" in error for error in errors): + raise AssertionError("dataset per-version mirror module was not rejected") + + legacy_file_facade = """ +pub mod previous; +""" + errors = check_source(Path("rust/lance-file/src/lib.rs"), legacy_file_facade) + if not any("legacy lance-file facade remains" in error for error in errors): + raise AssertionError("legacy lance-file facade was not rejected") + + previous_file_identity = """ +use lance_file::versions::v1::reader::FileReader as PreviousFileReader; +""" + errors = check_source(Path("rust/example/src/leak.rs"), previous_file_identity) + if not any("legacy file identity alias remains" in error for error in errors): + raise AssertionError("legacy file identity alias was not rejected") + + lance_io_encoding_facade = """ +use lance_io::encodings::plain::PlainEncoder; +""" + errors = check_source(Path("rust/example/src/leak.rs"), lance_io_encoding_facade) + if not any("v1 encoding grammar leaked into lance-io" in error for error in errors): + raise AssertionError("lance-io v1 encoding facade was not rejected") + + previous_encoding_facade = """ +pub mod previous; +""" + errors = check_source( + Path("rust/lance-encoding/src/lib.rs"), previous_encoding_facade + ) + if not any("time-relative lance-encoding facade" in error for error in errors): + raise AssertionError("lance-encoding previous facade was not rejected") + + array_strategy_leak = """ +use lance_encoding::array_encoding::ArrayFieldEncodingStrategy; +fn strategy() { + let _ = ArrayFieldEncodingStrategy::new(); +} +""" + errors = check_source( + Path("rust/lance-file/src/versions/v2_1/writer.rs"), array_strategy_leak + ) + if not any("field composition escaped the v2.0" in error for error in errors): + raise AssertionError("v2.0 array field composition leak was not rejected") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "--self-test", + action="store_true", + help="verify that representative architecture violations are rejected", + ) + args = parser.parse_args() + + if args.self_test: + run_self_test() + print("File-version boundary self-test passed.") + return 0 + + errors = run_checks() + if errors: + print("\n".join(errors), file=sys.stderr) + return 1 + print("File-version boundaries are valid.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/java/lance-jni/Cargo.lock b/java/lance-jni/Cargo.lock index 985ff5ea3ec..8230db447a1 100644 --- a/java/lance-jni/Cargo.lock +++ b/java/lance-jni/Cargo.lock @@ -3922,6 +3922,7 @@ dependencies = [ "arrow-arith", "arrow-array", "arrow-buffer", + "arrow-cast", "arrow-data", "arrow-schema", "arrow-select", @@ -4056,14 +4057,8 @@ name = "lance-io" version = "9.1.0-beta.8" dependencies = [ "arrow", - "arrow-arith", "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", "arrow-schema", - "arrow-select", - "async-recursion", "async-trait", "aws-config", "aws-credential-types", diff --git a/java/lance-jni/src/blocking_dataset.rs b/java/lance-jni/src/blocking_dataset.rs index caf837b371a..e2ab9b948d5 100644 --- a/java/lance-jni/src/blocking_dataset.rs +++ b/java/lance-jni/src/blocking_dataset.rs @@ -1976,12 +1976,13 @@ fn inner_get_lance_file_format_version<'local>( let version_string = { let dataset_guard = unsafe { env.get_rust_field::<_, _, BlockingDataset>(java_dataset, NATIVE_DATASET) }?; - let version = dataset_guard + dataset_guard .inner .manifest() .data_storage_format - .lance_file_version()?; - version.to_string() + .lance_file_format() + .to_manifest_string() + .to_string() }; Ok(env diff --git a/java/lance-jni/src/file_reader.rs b/java/lance-jni/src/file_reader.rs index 3df9766d066..3870f234c34 100644 --- a/java/lance-jni/src/file_reader.rs +++ b/java/lance-jni/src/file_reader.rs @@ -23,8 +23,8 @@ use lance::io::ObjectStore; use lance_core::cache::LanceCache; use lance_core::datatypes::{BlobHandling, OnMissing, Projection, Schema}; use lance_encoding::decoder::{DecoderPlugins, FilterExpression}; -use lance_encoding::version::LanceFileVersion; use lance_file::reader::{FileReader, FileReaderOptions, ReaderProjection}; +use lance_file::versions as file_versions; use lance_io::object_store::{ObjectStoreParams, ObjectStoreRegistry}; use lance_io::{ ReadBatchParams, @@ -261,19 +261,17 @@ pub extern "system" fn Java_org_lance_file_LanceFileReader_readAllNative( } let transformed_schema = projection.to_bare_schema(); - - let field_id_to_column_index = base_schema - .fields_pre_order() - .filter(|field| { - file_version < LanceFileVersion::V2_1 - || field.is_leaf() - || field.is_packed_struct() + let (field_ids, column_indices) = + file_versions::data_file_columns(file_version, &base_schema); + let field_id_to_column_index = field_ids + .into_iter() + .zip(column_indices) + .filter_map(|(field_id, column_index)| { + (column_index >= 0).then_some((field_id as u32, column_index as u32)) }) - .enumerate() - .map(|(idx, field)| (field.id as u32, idx as u32)) .collect::>(); - Some(ReaderProjection::from_field_ids( + Some(file_versions::reader_projection_from_field_ids( file_version, &transformed_schema, &field_id_to_column_index, diff --git a/java/lance-jni/src/file_writer.rs b/java/lance-jni/src/file_writer.rs index 40b48bd686b..6f607bd5da9 100644 --- a/java/lance-jni/src/file_writer.rs +++ b/java/lance-jni/src/file_writer.rs @@ -23,6 +23,7 @@ use jni::{ use lance::io::ObjectStore; use lance_file::{ version::LanceFileVersion, + versions as file_versions, writer::{FileWriter, FileWriterOptions}, }; use lance_io::object_store::{ObjectStoreParams, ObjectStoreRegistry}; @@ -108,15 +109,12 @@ fn inner_open<'local>( let obj_store = Arc::new(obj_store); let obj_writer = obj_store.create(&path).await?; - Result::Ok(FileWriter::new_lazy( - obj_writer, - FileWriterOptions { - format_version: data_storage_version_opt - .map(|v| v.parse::()) - .transpose()?, - ..Default::default() - }, - )) + let version = data_storage_version_opt + .map(|value| value.parse::()) + .transpose()? + .unwrap_or_default() + .resolve(); + file_versions::create_lazy_writer(version, obj_writer, FileWriterOptions::default()) })?; let writer = BlockingFileWriter::create(writer); diff --git a/java/lance-jni/src/utils.rs b/java/lance-jni/src/utils.rs index 94372ef27cc..0c2b2280415 100644 --- a/java/lance-jni/src/utils.rs +++ b/java/lance-jni/src/utils.rs @@ -12,7 +12,7 @@ use lance::dataset::optimize::{CompactionMode, CompactionOptions}; use lance::dataset::{WriteMode, WriteParams}; use lance::index::vector::{IndexFileVersion, StageParams, VectorIndexParams}; use lance::io::ObjectStoreParams; -use lance_encoding::version::LanceFileVersion; +use lance_file::version::LanceFileVersion; use lance_index::IndexParams; use lance_index::vector::bq::RQBuildParams; use lance_index::vector::hnsw::builder::HnswBuildParams; diff --git a/python/Cargo.lock b/python/Cargo.lock index e9c917a8ad9..fbb34b56e41 100644 --- a/python/Cargo.lock +++ b/python/Cargo.lock @@ -4247,6 +4247,7 @@ dependencies = [ "arrow-arith", "arrow-array", "arrow-buffer", + "arrow-cast", "arrow-data", "arrow-schema", "arrow-select", @@ -4382,14 +4383,8 @@ name = "lance-io" version = "9.1.0-beta.8" dependencies = [ "arrow", - "arrow-arith", "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", "arrow-schema", - "arrow-select", - "async-recursion", "async-trait", "aws-config", "aws-credential-types", diff --git a/python/src/dataset.rs b/python/src/dataset.rs index d163c65d6aa..c53ed1388d1 100644 --- a/python/src/dataset.rs +++ b/python/src/dataset.rs @@ -1120,7 +1120,13 @@ impl Dataset { #[getter(data_storage_version)] fn data_storage_version(&self) -> PyResult { - Ok(self.ds.manifest().data_storage_format.version.clone()) + Ok(self + .ds + .manifest() + .data_storage_format + .version + .to_manifest_string() + .to_string()) } #[getter(has_stable_row_ids)] diff --git a/python/src/file.rs b/python/src/file.rs index 2a3dd09e17f..865efc692c2 100644 --- a/python/src/file.rs +++ b/python/src/file.rs @@ -26,10 +26,9 @@ use lance_core::utils::path::LancePathExt; use lance_encoding::decoder::{DecoderPlugins, FilterExpression}; use lance_file::reader::{ BufferDescriptor, CachedFileMetadata, FileReader, FileReaderOptions, FileStatistics, - ReaderProjection, }; use lance_file::writer::{FileWriter, FileWriterOptions}; -use lance_file::{LanceEncodingsIo, version::LanceFileVersion}; +use lance_file::{LanceEncodingsIo, version::LanceFileVersion, versions as file_versions}; use lance_io::object_store::{LanceNamespaceStorageOptionsProvider, ObjectStoreParams}; use lance_io::{ ReadBatchParams, @@ -275,21 +274,23 @@ impl LanceFileWriter { max_page_bytes: Option, ) -> PyResult { let object_writer = object_store.create(&path).await.infer_error()?; + let version = version + .map(|value| value.parse::()) + .transpose() + .infer_error()? + .unwrap_or_default() + .resolve(); let options = FileWriterOptions { data_cache_bytes, keep_original_array, max_page_bytes, - format_version: version - .map(|v| v.parse::()) - .transpose() - .infer_error()?, - ..Default::default() }; let inner = if let Some(schema) = schema { let lance_schema = lance_core::datatypes::Schema::try_from(&schema.0).infer_error()?; - FileWriter::try_new(object_writer, lance_schema, options).infer_error() + file_versions::create_writer(version, object_writer, lance_schema, options) + .infer_error() } else { - Ok(FileWriter::new_lazy(object_writer, options)) + file_versions::create_lazy_writer(version, object_writer, options).infer_error() }?; Ok(Self { inner: Arc::new(Mutex::new(Box::new(inner))), @@ -817,7 +818,7 @@ impl LanceFileReader { let mut base_projection = None; if let Some(columns) = columns { base_projection = Some( - ReaderProjection::from_column_names( + file_versions::reader_projection_from_column_names( file_metadata.version(), &file_metadata.file_schema, &columns.iter().map(|s| s.as_str()).collect::>(), diff --git a/python/src/utils.rs b/python/src/utils.rs index edc376d35c8..5e04552a759 100644 --- a/python/src/utils.rs +++ b/python/src/utils.rs @@ -25,7 +25,7 @@ use arrow_schema::DataType; use lance::Result; use lance::datatypes::Schema; use lance_arrow::FixedSizeListArrayExt; -use lance_file::previous::writer::FileWriter as PreviousFileWriter; +use lance_file::versions::v1::writer::FileWriter as PreviousFileWriter; use lance_index::vector::hnsw::{HNSW, builder::HnswBuildParams}; use lance_index::vector::kmeans::{ KMeans as LanceKMeans, KMeansAlgoFloat, KMeansParams, compute_partitions, diff --git a/rust/lance-arrow/src/lib.rs b/rust/lance-arrow/src/lib.rs index 2ac962aa1aa..ae4a6228256 100644 --- a/rust/lance-arrow/src/lib.rs +++ b/rust/lance-arrow/src/lib.rs @@ -21,13 +21,13 @@ use std::{collections::HashMap, ptr::NonNull}; use arrow_array::{ Array, ArrayRef, ArrowNumericType, FixedSizeBinaryArray, FixedSizeListArray, GenericListArray, LargeListArray, ListArray, OffsetSizeTrait, PrimitiveArray, RecordBatch, StructArray, - UInt8Array, UInt32Array, cast::AsArray, + UInt8Array, UInt32Array, cast::AsArray, make_array, }; use arrow_array::{ Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Int64Array, new_null_array, }; -use arrow_buffer::MutableBuffer; -use arrow_data::ArrayDataBuilder; +use arrow_buffer::{Buffer, MutableBuffer}; +use arrow_data::{ArrayDataBuilder, BufferSpec, layout}; use arrow_schema::{ArrowError, DataType, Field, Fields, IntervalUnit, Schema, SortOptions}; use arrow_select::{interleave::interleave, take::take}; use rand::prelude::*; @@ -70,6 +70,48 @@ pub const BLOB_PACK_FILE_SIZE_THRESHOLD_META_KEY: &str = type Result = std::result::Result; +/// Build a null-free Arrow array over a fixed-width byte buffer. +/// +/// The input is reused without copying when its alignment and size satisfy the +/// Arrow layout. Otherwise, an aligned buffer is allocated and the missing +/// trailing bytes are zero-filled. +pub fn array_from_fixed_width_bytes( + data_type: &DataType, + bytes: bytes::Bytes, + len: usize, + offset: usize, +) -> Result { + let layout = layout(data_type); + if layout.buffers.len() != 1 { + return Err(ArrowError::InvalidArgumentError(format!( + "expected a data type with one fixed-width buffer, found {data_type}" + ))); + } + + let buffer = if let BufferSpec::FixedWidth { + byte_width, + alignment, + } = &layout.buffers[0] + { + let min_buffer_size = (len + offset).saturating_mul(*byte_width); + if bytes.len() < min_buffer_size { + Buffer::copy_bytes_bytes(bytes, min_buffer_size) + } else { + Buffer::from_bytes_bytes(bytes, *alignment as u64) + } + } else { + Buffer::from_slice_ref(bytes) + }; + + let data = ArrayDataBuilder::new(data_type.clone()) + .len(len) + .offset(offset) + .null_count(0) + .add_buffer(buffer) + .build()?; + Ok(make_array(data)) +} + pub trait DataTypeExt { /// Returns true if the data type is binary-like, such as Utf8, Binary, or the large and/or view variants. fn is_binary_like(&self) -> bool; @@ -1573,6 +1615,21 @@ mod tests { use arrow_array::{ListArray, StringArray, new_empty_array, new_null_array}; use arrow_buffer::OffsetBuffer; + #[test] + fn fixed_width_bytes_zero_pads_short_input() { + let bytes = bytes::Bytes::copy_from_slice(&42_i32.to_le_bytes()); + let array = array_from_fixed_width_bytes(&DataType::Int32, bytes, 2, 0).unwrap(); + let array = array.as_any().downcast_ref::().unwrap(); + assert_eq!(array.values(), &[42, 0]); + } + + #[test] + fn fixed_width_bytes_rejects_variable_width_type() { + let error = + array_from_fixed_width_bytes(&DataType::Utf8, bytes::Bytes::new(), 0, 0).unwrap_err(); + assert!(error.to_string().contains("one fixed-width buffer")); + } + #[test] fn test_convert_to_floating_point_preserves_inner_nulls() { // A FixedSizeList with a null inner element must convert to a diff --git a/rust/lance-encoding/benches/common/mod.rs b/rust/lance-encoding/benches/common/mod.rs new file mode 100644 index 00000000000..0ca46e250d7 --- /dev/null +++ b/rust/lance-encoding/benches/common/mod.rs @@ -0,0 +1,275 @@ +use std::sync::Arc; + +use arrow_schema::DataType; +use lance_core::{Error, Result, datatypes::Field}; +use lance_encoding::{ + compression::{ + BlockCompressor, CompressionStrategy, field_metadata_params, finalize_miniblock_compressor, + reject_packed_struct_per_value, try_bitpacking_block, try_bitpacking_miniblock, + try_byte_stream_split_miniblock, try_fixed_packed_struct_miniblock, try_fixed_u8_rle_block, + try_fixed_u8_rle_miniblock, try_general_block, try_raw_block, + try_raw_fixed_size_list_miniblock, try_raw_fixed_width_miniblock, try_raw_per_value, + try_uncompressed_fixed_width_miniblock, try_variable_packed_struct_per_value, + try_variable_width_miniblock, try_variable_width_per_value, + }, + compression_config::{CompressionFieldParams, CompressionParams}, + data::DataBlock, + encoder::{ + ColumnIndexSequence, FieldEncoder, FieldEncodingContext, FieldEncodingStrategy, + structural::{ + PrimitiveFieldEncoding, PrimitivePageEncoding, try_create_binary_blob, try_create_list, + try_create_map, try_create_struct, try_create_structural_blob, + try_create_structural_fixed_size_list, + }, + }, + encodings::logical::primitive::{fullzip::PerValueCompressor, miniblock::MiniBlockCompressor}, + format::pb21::CompressiveEncoding, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BenchEncoding { + Array, + StructuralU16, + StructuralU32, +} + +impl std::fmt::Display for BenchEncoding { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Array => "array", + Self::StructuralU16 => "structural-u16", + Self::StructuralU32 => "structural-u32", + }) + } +} + +#[derive(Debug, Clone)] +struct BenchCompressionStrategy { + encoding: BenchEncoding, + params: CompressionParams, +} + +impl BenchCompressionStrategy { + fn field_params(&self, field: &Field) -> CompressionFieldParams { + let mut params = self + .params + .get_field_params(&field.name, &field.data_type()); + let mut metadata = field_metadata_params(field); + if self.encoding == BenchEncoding::StructuralU16 + && metadata + .minichunk_size + .is_some_and(|size| size >= 32 * 1024) + { + metadata.minichunk_size = None; + } + params.merge(&metadata); + params + } +} + +impl CompressionStrategy for BenchCompressionStrategy { + fn create_miniblock_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + let compressor = + if let Some(compressor) = try_uncompressed_fixed_width_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_byte_stream_split_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_fixed_u8_rle_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_bitpacking_miniblock(data) { + compressor + } else if let Some(compressor) = try_raw_fixed_width_miniblock(data) { + compressor + } else if let Some(compressor) = try_variable_width_miniblock(field, data, ¶ms)? { + compressor + } else if let Some(compressor) = try_fixed_packed_struct_miniblock(data)? { + compressor + } else if let Some(compressor) = try_raw_fixed_size_list_miniblock(data) { + compressor + } else { + return Err(Error::not_supported_source( + format!( + "Mini-block compression not yet supported for block type {}", + data.name() + ) + .into(), + )); + }; + finalize_miniblock_compressor(data, compressor, ¶ms) + } + + fn create_per_value( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + if let Some(compressor) = try_raw_per_value(data) { + return Ok(compressor); + } + let packed = match self.encoding { + BenchEncoding::StructuralU16 => reject_packed_struct_per_value(field, data)?, + BenchEncoding::StructuralU32 => { + try_variable_packed_struct_per_value(Arc::new(self.clone()), field, data)? + } + BenchEncoding::Array => unreachable!(), + }; + if let Some(compressor) = packed { + return Ok(compressor); + } + if let Some(compressor) = try_variable_width_per_value(field, data, ¶ms)? { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Per-value compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } + + fn create_block_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result<(Box, CompressiveEncoding)> { + let params = self.field_params(field); + if self.encoding == BenchEncoding::StructuralU32 + && let Some(compressor) = try_fixed_u8_rle_block(data, ¶ms)? + { + return Ok(compressor); + } + if let Some(compressor) = try_bitpacking_block(data) { + return Ok(compressor); + } + if self.encoding == BenchEncoding::StructuralU32 + && let Some(compressor) = try_general_block(data, ¶ms)? + { + return Ok(compressor); + } + if let Some(compressor) = try_raw_block(data) { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Block compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } +} + +#[derive(Debug)] +struct BenchFieldEncodingStrategy { + encoding: BenchEncoding, + primitive: PrimitiveFieldEncoding, +} + +impl FieldEncodingStrategy for BenchFieldEncodingStrategy { + fn create_field_encoder( + &self, + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, + ) -> Result> { + if let Some(encoder) = + try_create_binary_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if self.encoding == BenchEncoding::StructuralU32 + && let Some(encoder) = + try_create_structural_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if field.is_blob() { + return Err(Error::invalid_input_source( + format!( + "Blob encoding is not available for field '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )); + } + if self.encoding == BenchEncoding::StructuralU32 { + if let Some(encoder) = try_create_map(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = + try_create_structural_fixed_size_list(field, column_index, context)? + { + return Ok(encoder); + } + } + if let Some(encoder) = self.primitive.try_create(field, column_index, context)? { + return Ok(encoder); + } + if self.encoding == BenchEncoding::StructuralU16 { + if matches!( + field.data_type(), + DataType::FixedSizeList(item, _) + if matches!(item.data_type(), DataType::Struct(_)) + ) { + return Err(Error::not_supported_source( + "FixedSizeList is not enabled by the selected file format".into(), + )); + } + if matches!(field.data_type(), DataType::Map(_, _)) { + return Err(Error::not_supported_source( + "Map data type is not enabled by the selected file format".into(), + )); + } + } + if let Some(encoder) = try_create_list(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_struct(field, column_index, context)? { + return Ok(encoder); + } + Err(Error::not_supported_source( + format!( + "{} has no field encoding for '{}' with data type {}", + self.encoding, + field.name, + field.data_type() + ) + .into(), + )) + } +} + +pub fn encoding_strategy(encoding: BenchEncoding) -> Box { + if encoding == BenchEncoding::Array { + return Box::new(lance_encoding::array_encoding::ArrayFieldEncodingStrategy::new()); + } + + let compression = Arc::new(BenchCompressionStrategy { + encoding, + params: CompressionParams::default(), + }); + let page_encodings = match encoding { + BenchEncoding::StructuralU16 => vec![ + PrimitivePageEncoding::reject_sparse(), + PrimitivePageEncoding::dense_u16(compression), + ], + BenchEncoding::StructuralU32 => vec![ + PrimitivePageEncoding::reject_sparse(), + PrimitivePageEncoding::constant(), + PrimitivePageEncoding::dense_u32(compression), + ], + BenchEncoding::Array => unreachable!(), + }; + Box::new(BenchFieldEncodingStrategy { + encoding, + primitive: PrimitiveFieldEncoding::new(page_encodings), + }) +} diff --git a/rust/lance-encoding/benches/decoder.rs b/rust/lance-encoding/benches/decoder.rs index cc0404e1bb3..21cc2334ece 100644 --- a/rust/lance-encoding/benches/decoder.rs +++ b/rust/lance-encoding/benches/decoder.rs @@ -11,15 +11,18 @@ use lance_core::cache::LanceCache; use lance_datagen::ArrayGeneratorExt; use lance_encoding::{ decoder::{ - DecodeBatchScheduler, DecoderConfig, DecoderPlugins, FilterExpression, create_decode_stream, + DecodeBatchScheduler, DecoderConfig, DecoderPlugins, EncodedBatchLayout, FilterExpression, + create_decode_stream, }, - encoder::{EncodingOptions, default_encoding_strategy, encode_batch}, - version::LanceFileVersion, + encoder::{EncodingOptions, encode_batch}, }; use tokio::sync::mpsc::unbounded_channel; use rand::Rng; +pub mod common; +use common::{BenchEncoding, encoding_strategy}; + const PRIMITIVE_TYPES: &[DataType] = &[ DataType::Date32, DataType::Date64, @@ -49,6 +52,15 @@ const PRIMITIVE_TYPES: &[DataType] = &[ // schema doesn't yet parse them in the context of a fixed size list. const PRIMITIVE_TYPES_FOR_FSL: &[DataType] = &[DataType::Int8, DataType::Float32]; +fn encoded_batch_layout(encoding: BenchEncoding) -> EncodedBatchLayout { + match encoding { + BenchEncoding::Array => EncodedBatchLayout::Array, + BenchEncoding::StructuralU16 | BenchEncoding::StructuralU32 => { + EncodedBatchLayout::Structural + } + } +} + fn bench_decode(c: &mut Criterion) { let rt = tokio::runtime::Runtime::new().unwrap(); let mut group = c.benchmark_group("decode_primitive"); @@ -64,7 +76,7 @@ fn bench_decode(c: &mut Criterion) { .unwrap(); let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(data.schema().as_ref()).unwrap()); - let encoding_strategy = default_encoding_strategy(LanceFileVersion::default()); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU16); let encoded = rt .block_on(encode_batch( &data, @@ -81,7 +93,7 @@ fn bench_decode(c: &mut Criterion) { &FilterExpression::no_filter(), Arc::::default(), false, - LanceFileVersion::default(), + EncodedBatchLayout::Structural, Some(Arc::new(LanceCache::no_cache())), )) .unwrap(); @@ -95,14 +107,14 @@ fn bench_decode_fsl(c: &mut Criterion) { let rt = tokio::runtime::Runtime::new().unwrap(); let mut group = c.benchmark_group("decode_fsl"); const NUM_BYTES: u64 = 1024 * 1024 * 128; - for version in [ - LanceFileVersion::V2_0, - LanceFileVersion::V2_1, - LanceFileVersion::V2_2, + for encoding in [ + BenchEncoding::Array, + BenchEncoding::StructuralU16, + BenchEncoding::StructuralU32, ] { for data_type in PRIMITIVE_TYPES_FOR_FSL { for dimension in [4, 16, 32, 64, 128] { - let nullable_choices: &[bool] = if version == LanceFileVersion::V2_0 { + let nullable_choices: &[bool] = if encoding == BenchEncoding::Array { &[false] } else { &[false, true] @@ -110,7 +122,7 @@ fn bench_decode_fsl(c: &mut Criterion) { for nullable in nullable_choices { let func_name = format!( "{:?}_{}_v{}_null{}", - data_type, dimension, version, nullable + data_type, dimension, encoding, nullable ) .to_lowercase(); group.throughput(criterion::Throughput::Bytes(NUM_BYTES)); @@ -133,7 +145,7 @@ fn bench_decode_fsl(c: &mut Criterion) { lance_core::datatypes::Schema::try_from(data.schema().as_ref()) .unwrap(), ); - let encoding_strategy = default_encoding_strategy(version); + let encoding_strategy = encoding_strategy(encoding); let encoded = rt .block_on(encode_batch( &data, @@ -149,7 +161,7 @@ fn bench_decode_fsl(c: &mut Criterion) { &FilterExpression::no_filter(), Arc::::default(), false, - version, + encoded_batch_layout(encoding), Some(Arc::new(LanceCache::no_cache())), )) .unwrap(); @@ -199,7 +211,7 @@ fn bench_decode_str_with_dict_encoding(c: &mut Criterion) { let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(data.schema().as_ref()).unwrap()); - let encoding_strategy = default_encoding_strategy(LanceFileVersion::default()); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU16); let encoded = rt .block_on(encode_batch( &data, @@ -215,7 +227,7 @@ fn bench_decode_str_with_dict_encoding(c: &mut Criterion) { &FilterExpression::no_filter(), Arc::::default(), false, - LanceFileVersion::default(), + EncodedBatchLayout::Structural, Some(Arc::new(LanceCache::no_cache())), )) .unwrap(); @@ -274,7 +286,7 @@ fn bench_decode_packed_struct(c: &mut Criterion) { RecordBatch::try_new(Arc::new(new_schema.clone()), data.columns().to_vec()).unwrap(); let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(&new_schema).unwrap()); - let encoding_strategy = default_encoding_strategy(LanceFileVersion::V2_2); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU32); let encoded = rt .block_on(encode_batch( &data, @@ -291,7 +303,7 @@ fn bench_decode_packed_struct(c: &mut Criterion) { &FilterExpression::no_filter(), Arc::::default(), false, - LanceFileVersion::V2_2, + EncodedBatchLayout::Structural, Some(Arc::new(LanceCache::no_cache())), )) .unwrap(); @@ -331,7 +343,7 @@ fn bench_decode_str_with_fixed_size_binary_encoding(c: &mut Criterion) { let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(data.schema().as_ref()).unwrap()); - let encoding_strategy = default_encoding_strategy(LanceFileVersion::default()); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU16); let encoded = rt .block_on(encode_batch( &data, @@ -347,7 +359,7 @@ fn bench_decode_str_with_fixed_size_binary_encoding(c: &mut Criterion) { &FilterExpression::no_filter(), Arc::::default(), false, - LanceFileVersion::default(), + EncodedBatchLayout::Structural, Some(Arc::new(LanceCache::no_cache())), )) .unwrap(); @@ -398,7 +410,7 @@ fn bench_decode_compressed(c: &mut Criterion) { let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(schema.as_ref()).unwrap()); // V2_2+ required for general compression - let encoding_strategy = default_encoding_strategy(LanceFileVersion::V2_2); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU32); // Encode once during setup let encoded = rt @@ -423,7 +435,7 @@ fn bench_decode_compressed(c: &mut Criterion) { &FilterExpression::no_filter(), Arc::::default(), false, - LanceFileVersion::V2_2, + EncodedBatchLayout::Structural, Some(Arc::new(LanceCache::no_cache())), )) .unwrap(); @@ -476,7 +488,7 @@ fn bench_decode_compressed_parallel(c: &mut Criterion) { let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(schema.as_ref()).unwrap()); - let encoding_strategy = default_encoding_strategy(LanceFileVersion::V2_2); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU32); let encoded = rt .block_on(encode_batch( diff --git a/rust/lance-encoding/benches/encoder.rs b/rust/lance-encoding/benches/encoder.rs index 08eb89d32fe..02ffd92083c 100644 --- a/rust/lance-encoding/benches/encoder.rs +++ b/rust/lance-encoding/benches/encoder.rs @@ -7,15 +7,15 @@ use arrow_array::{ArrayRef, BooleanArray, ListArray, RecordBatch}; use arrow_buffer::{OffsetBuffer, ScalarBuffer}; use arrow_schema::{DataType, Field, Schema}; use criterion::{Criterion, criterion_group, criterion_main}; -use lance_encoding::{ - encoder::{EncodingOptions, default_encoding_strategy, encode_batch}, - version::LanceFileVersion, -}; +use lance_encoding::encoder::{EncodingOptions, encode_batch}; + +pub mod common; +use common::{BenchEncoding, encoding_strategy}; fn encode_batch_sync(rt: &tokio::runtime::Runtime, data: &RecordBatch) { let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(data.schema().as_ref()).unwrap()); - let encoding_strategy = default_encoding_strategy(LanceFileVersion::V2_2); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU32); rt.block_on(encode_batch( data, @@ -68,7 +68,7 @@ fn bench_encode_compressed(c: &mut Criterion) { let lance_schema = Arc::new(lance_core::datatypes::Schema::try_from(schema.as_ref()).unwrap()); // V2_2+ required for general compression - let encoding_strategy = default_encoding_strategy(LanceFileVersion::V2_2); + let encoding_strategy = encoding_strategy(BenchEncoding::StructuralU32); group.throughput(criterion::Throughput::Elements( (NUM_ROWS * NUM_COLUMNS) as u64, diff --git a/rust/lance-encoding/src/array_encoding.rs b/rust/lance-encoding/src/array_encoding.rs new file mode 100644 index 00000000000..a76c6a186ec --- /dev/null +++ b/rust/lance-encoding/src/array_encoding.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +//! Encoding and decoding mechanisms described by [`crate::format::pb::ArrayEncoding`]. +//! +//! File versions decide which mechanisms to compose and accept. This module +//! contains only the reusable implementation of that persisted grammar. + +pub mod logical; +pub mod physical; +mod strategy; + +pub use strategy::ArrayFieldEncodingStrategy; diff --git a/rust/lance-encoding/src/previous/encodings/logical.rs b/rust/lance-encoding/src/array_encoding/logical.rs similarity index 100% rename from rust/lance-encoding/src/previous/encodings/logical.rs rename to rust/lance-encoding/src/array_encoding/logical.rs diff --git a/rust/lance-encoding/src/previous/encodings/logical/binary.rs b/rust/lance-encoding/src/array_encoding/logical/binary.rs similarity index 97% rename from rust/lance-encoding/src/previous/encodings/logical/binary.rs rename to rust/lance-encoding/src/array_encoding/logical/binary.rs index 00715f64511..697c1503e0a 100644 --- a/rust/lance-encoding/src/previous/encodings/logical/binary.rs +++ b/rust/lance-encoding/src/array_encoding/logical/binary.rs @@ -19,7 +19,7 @@ use crate::{ DecodeArrayTask, FilterExpression, MessageType, NextDecodeTask, PriorityRange, ScheduledScanLine, SchedulerContext, }, - previous::decoder::{DecoderReady, FieldScheduler, LogicalPageDecoder, SchedulingJob}, + decoder::{DecoderReady, FieldScheduler, LogicalPageDecoder, SchedulingJob}, }; /// Wraps a varbin scheduler and uses a BinaryPageDecoder to cast @@ -41,7 +41,7 @@ impl SchedulingJob for BinarySchedulingJob<'_> { .decoders .into_iter() .map(|message| { - let decoder = message.into_legacy(); + let decoder = message.into_array(); MessageType::DecoderReady(DecoderReady { path: decoder.path, decoder: Box::new(BinaryPageDecoder { @@ -179,7 +179,7 @@ impl DecodeArrayTask for BinaryArrayDecoder { DataType::LargeUtf8 => Self::from_list_array::(arr.as_list::()), _ => panic!("Binary decoder does not support this data type"), }; - // data_size is only tracked in the v2.1 structural decode path; the legacy + // data_size is only tracked in the v2.1 structural decode path; the v2.0 array // v2.0 path does not need it so we return 0. Ok((result, 0)) } diff --git a/rust/lance-encoding/src/previous/encodings/logical/blob.rs b/rust/lance-encoding/src/array_encoding/logical/blob.rs similarity index 96% rename from rust/lance-encoding/src/previous/encodings/logical/blob.rs rename to rust/lance-encoding/src/array_encoding/logical/blob.rs index 20f6afc5b36..0bf3a39fca1 100644 --- a/rust/lance-encoding/src/previous/encodings/logical/blob.rs +++ b/rust/lance-encoding/src/array_encoding/logical/blob.rs @@ -23,9 +23,9 @@ use crate::{ DecodeArrayTask, FilterExpression, MessageType, NextDecodeTask, PriorityRange, ScheduledScanLine, SchedulerContext, }, + decoder::{DecoderReady, FieldScheduler, LogicalPageDecoder, SchedulingJob}, encoder::{EncodeTask, FieldEncoder, OutOfLineBuffers}, format::pb::{Blob, ColumnEncoding, column_encoding}, - previous::decoder::{DecoderReady, FieldScheduler, LogicalPageDecoder, SchedulingJob}, repdef::RepDefBuilder, }; @@ -67,7 +67,7 @@ impl SchedulingJob for BlobFieldSchedulingJob<'_> { let next_descriptions = self.descriptions_job.schedule_next(context, priority)?; let mut priority = priority.current_priority(); let decoders = next_descriptions.decoders.into_iter().map(|decoder| { - let decoder = decoder.into_legacy(); + let decoder = decoder.into_array(); let path = decoder.path; let mut decoder = decoder.decoder; let num_rows = decoder.num_rows(); @@ -285,7 +285,7 @@ impl DecodeArrayTask for BlobArrayDecodeTask { buffer.extend_from_slice(&bytes); } let data_buf = Buffer::from_vec(buffer); - // data_size is only tracked in the v2.1 structural decode path; the legacy + // data_size is only tracked in the v2.1 structural decode path; the v2.0 array // v2.0 path does not need it so we return 0. Ok(( Arc::new(LargeBinaryArray::new(offsets, data_buf, self.validity)), @@ -417,10 +417,10 @@ mod tests { use super::BlobFieldDecoder; use crate::{ EncodingsIo, + decoder::LogicalPageDecoder, format::pb::column_encoding, - previous::decoder::LogicalPageDecoder, + testing::TestEncoding, testing::{TestCases, check_round_trip_encoding_of_data, check_specific_random}, - version::LanceFileVersion, }; static BLOB_META: LazyLock> = LazyLock::new(|| { @@ -433,11 +433,7 @@ mod tests { #[test_log::test(tokio::test)] async fn test_basic_blob() { let field = Field::new("", DataType::LargeBinary, false).with_metadata(BLOB_META.clone()); - check_specific_random( - field, - TestCases::basic().with_max_file_version(LanceFileVersion::V2_1), - ) - .await; + check_specific_random(field, TestCases::basic().with_array_and_u16_encodings()).await; } #[test_log::test(tokio::test)] @@ -446,10 +442,10 @@ mod tests { let val2: &[u8] = &[7, 8, 9]; let array = Arc::new(LargeBinaryArray::from(vec![Some(val1), None, Some(val2)])); let test_cases = TestCases::default() - .with_max_file_version(LanceFileVersion::V2_1) + .with_array_and_u16_encodings() .with_expected_encoding("packed_struct") - .with_verify_encoding(Arc::new(|cols, version| { - if version < &LanceFileVersion::V2_1 { + .with_verify_encoding(Arc::new(|cols, encoding| { + if *encoding == TestEncoding::Array { // In 2.0 we used a special "column encoding" to mark blob fields. In 2.1 we // don't do this and just rely on the regular page encoding. assert_eq!(cols.len(), 1); @@ -465,9 +461,9 @@ mod tests { .await; let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) - .with_verify_encoding(Arc::new(|cols, version| { - if version < &LanceFileVersion::V2_1 { + .with_structural_encodings() + .with_verify_encoding(Arc::new(|cols, encoding| { + if *encoding == TestEncoding::Array { assert_eq!(cols.len(), 1); let col = &cols[0]; assert!(!matches!( diff --git a/rust/lance-encoding/src/previous/encodings/logical/list.rs b/rust/lance-encoding/src/array_encoding/logical/list.rs similarity index 98% rename from rust/lance-encoding/src/previous/encodings/logical/list.rs rename to rust/lance-encoding/src/array_encoding/logical/list.rs index 3de886a21db..10ffb215798 100644 --- a/rust/lance-encoding/src/previous/encodings/logical/list.rs +++ b/rust/lance-encoding/src/array_encoding/logical/list.rs @@ -22,19 +22,19 @@ use tokio::task::JoinHandle; use crate::{ EncodingsIo, + array_encoding::logical::r#struct::{SimpleStructDecoder, SimpleStructScheduler}, buffer::LanceBuffer, data::{BlockInfo, DataBlock, FixedWidthDataBlock}, decoder::{ - DecodeArrayTask, DecodeBatchScheduler, FilterExpression, ListPriorityRange, MessageType, - NextDecodeTask, PageEncoding, PriorityRange, ScheduledScanLine, SchedulerContext, + DecodeArrayTask, DecodeBatchScheduler, FieldScheduler, FilterExpression, ListPriorityRange, + LogicalPageDecoder, MessageType, NextDecodeTask, PageEncoding, PriorityRange, + ScheduledScanLine, SchedulerContext, SchedulingJob, }, - encoder::{EncodeTask, EncodedColumn, EncodedPage, FieldEncoder, OutOfLineBuffers}, - format::pb, - previous::{ - decoder::{FieldScheduler, LogicalPageDecoder, SchedulingJob}, - encoder::{ArrayEncoder, EncodedArray}, - encodings::logical::r#struct::{SimpleStructDecoder, SimpleStructScheduler}, + encoder::{ + ArrayEncoder, EncodeTask, EncodedArray, EncodedColumn, EncodedPage, FieldEncoder, + OutOfLineBuffers, }, + format::pb, repdef::RepDefBuilder, utils::accumulation::AccumulationQueue, }; @@ -397,7 +397,7 @@ async fn indirect_schedule_task( for message in indirect_messages { for decoder in message.decoders { - let decoder = decoder.into_legacy(); + let decoder = decoder.into_array(); if !decoder.path.is_empty() { root_decoder.accept_child(decoder)?; } @@ -467,7 +467,7 @@ impl SchedulingJob for ListFieldSchedulingJob<'_> { .into_iter() .next() .unwrap() - .into_legacy() + .into_array() .decoder; let items_scheduler = self.scheduler.items_scheduler.clone(); @@ -690,7 +690,7 @@ impl DecodeArrayTask for ListDecodeTask { } _ => panic!("ListDecodeTask with data type that is not i32 or i64"), }; - // data_size is only tracked in the v2.1 structural decode path; the legacy + // data_size is only tracked in the v2.1 structural decode path; the v2.0 array // v2.0 path does not need it so we return 0. Ok((array, 0)) } @@ -958,10 +958,10 @@ impl ListOffsetsEncoder { let (data, description) = array.into_buffers(); Ok(EncodedPage { data, - description: PageEncoding::Legacy(description), + description: PageEncoding::Array(description), num_rows, column_idx, - row_number: 0, // Legacy encoders do not use + row_number: 0, // V2.0 encoders do not use }) }) .map(|res_res| res_res.unwrap()) diff --git a/rust/lance-encoding/src/previous/encodings/logical/primitive.rs b/rust/lance-encoding/src/array_encoding/logical/primitive.rs similarity index 97% rename from rust/lance-encoding/src/previous/encodings/logical/primitive.rs rename to rust/lance-encoding/src/array_encoding/logical/primitive.rs index d1debf3ef33..a35bc316a1a 100644 --- a/rust/lance-encoding/src/previous/encodings/logical/primitive.rs +++ b/rust/lance-encoding/src/array_encoding/logical/primitive.rs @@ -10,10 +10,10 @@ use futures::{FutureExt, future::BoxFuture}; use log::trace; use crate::decoder::{ColumnBuffers, PageBuffers}; -use crate::previous::decoder::{FieldScheduler, LogicalPageDecoder, SchedulingJob}; -use crate::previous::encoder::ArrayEncodingStrategy; +use crate::decoder::{FieldScheduler, LogicalPageDecoder, SchedulingJob}; +use crate::encoder::ArrayEncodingStrategy; use crate::utils::accumulation::AccumulationQueue; -use crate::{data::DataBlock, previous::encodings::physical::decoder_from_array_encoding}; +use crate::{array_encoding::physical::decoder_from_array_encoding, data::DataBlock}; use lance_core::{Error, Result, datatypes::Field}; use crate::{ @@ -74,7 +74,7 @@ impl PrimitiveFieldScheduler { positions_and_sizes: &page.buffer_offsets_and_sizes, }; let scheduler = decoder_from_array_encoding( - page.encoding.as_legacy(), + page.encoding.as_array(), &page_buffers, &data_type, ); @@ -316,7 +316,7 @@ impl DecodeArrayTask for PrimitiveFieldDecodeTask { return Ok((new_array, 0)); } } - // data_size is only tracked in the v2.1 structural decode path; the legacy + // data_size is only tracked in the v2.1 structural decode path; the v2.0 array // v2.0 path does not need it so we return 0. Ok((array, 0)) } @@ -436,10 +436,10 @@ impl PrimitiveFieldEncoder { let (data, description) = array.into_buffers(); Ok(EncodedPage { data, - description: PageEncoding::Legacy(description), + description: PageEncoding::Array(description), num_rows: num_values, column_idx, - row_number: 0, // legacy encoders do not use + row_number: 0, // v2.0 encoders do not use }) }) .map(|res_res| { diff --git a/rust/lance-encoding/src/previous/encodings/logical/struct.rs b/rust/lance-encoding/src/array_encoding/logical/struct.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/logical/struct.rs rename to rust/lance-encoding/src/array_encoding/logical/struct.rs index b117f74ce2b..045b3bca71d 100644 --- a/rust/lance-encoding/src/previous/encodings/logical/struct.rs +++ b/rust/lance-encoding/src/array_encoding/logical/struct.rs @@ -12,7 +12,7 @@ use crate::{ DecodeArrayTask, FilterExpression, MessageType, NextDecodeTask, PriorityRange, ScheduledScanLine, SchedulerContext, }, - previous::decoder::{DecoderReady, FieldScheduler, LogicalPageDecoder, SchedulingJob}, + decoder::{DecoderReady, FieldScheduler, LogicalPageDecoder, SchedulingJob}, }; use arrow_array::{ArrayRef, StructArray}; use arrow_schema::{DataType, Field, Fields}; @@ -57,7 +57,7 @@ struct EmptyStructDecodeTask { impl DecodeArrayTask for EmptyStructDecodeTask { fn decode(self: Box) -> Result<(ArrayRef, u64)> { - // data_size is only tracked in the v2.1 structural decode path; the legacy + // data_size is only tracked in the v2.1 structural decode path; the v2.0 array // v2.0 path does not need it so we return 0. Ok(( Arc::new(StructArray::new_empty_fields(self.num_rows as usize, None)), @@ -607,7 +607,7 @@ impl DecodeArrayTask for SimpleStructDecodeTask { .into_iter() .map(|child| child.decode()) .collect::>>()?; - // data_size is only tracked in the v2.1 structural decode path; the legacy + // data_size is only tracked in the v2.1 structural decode path; the v2.0 array // v2.0 path does not need it so we return 0. Ok(( Arc::new(StructArray::try_new(self.child_fields, child_arrays, None)?), diff --git a/rust/lance-encoding/src/previous/encodings/physical.rs b/rust/lance-encoding/src/array_encoding/physical.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical.rs rename to rust/lance-encoding/src/array_encoding/physical.rs index a3cb0adb4a7..66af9e806df 100644 --- a/rust/lance-encoding/src/previous/encodings/physical.rs +++ b/rust/lance-encoding/src/array_encoding/physical.rs @@ -5,16 +5,16 @@ use arrow_schema::DataType; use lance_arrow::DataTypeExt; use crate::{ - buffer::LanceBuffer, - decoder::{PageBuffers, PageScheduler}, - encodings::physical::block::{CompressionConfig, CompressionScheme}, - format::pb::{self, PackedStruct}, - previous::encodings::physical::{ + array_encoding::physical::{ basic::BasicPageScheduler, binary::BinaryPageScheduler, bitmap::DenseBitmapScheduler, dictionary::DictionaryPageScheduler, fixed_size_list::FixedListScheduler, fsst::FsstPageScheduler, packed_struct::PackedStructPageScheduler, value::ValuePageScheduler, }, + buffer::LanceBuffer, + decoder::{PageBuffers, PageScheduler}, + encodings::physical::block::{CompressionConfig, CompressionScheme}, + format::pb::{self, PackedStruct}, }; pub mod basic; @@ -293,9 +293,9 @@ pub fn decoder_from_array_encoding( #[cfg(test)] mod tests { + use crate::array_encoding::physical::get_buffer_decoder; use crate::decoder::{ColumnBuffers, FileBuffers, PageBuffers}; use crate::format::pb; - use crate::previous::encodings::physical::get_buffer_decoder; #[test] fn test_get_buffer_decoder_for_compressed_buffer() { diff --git a/rust/lance-encoding/src/previous/encodings/physical/basic.rs b/rust/lance-encoding/src/array_encoding/physical/basic.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/basic.rs rename to rust/lance-encoding/src/array_encoding/physical/basic.rs index ec098c3fff4..6dd7326a97f 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/basic.rs +++ b/rust/lance-encoding/src/array_encoding/physical/basic.rs @@ -11,8 +11,8 @@ use crate::{ EncodingsIo, data::{AllNullDataBlock, BlockInfo, DataBlock, NullableDataBlock}, decoder::{PageScheduler, PrimitivePageDecoder}, + encoder::{ArrayEncoder, EncodedArray}, format::ProtobufUtils, - previous::encoder::{ArrayEncoder, EncodedArray}, }; use lance_core::Result; diff --git a/rust/lance-encoding/src/previous/encodings/physical/binary.rs b/rust/lance-encoding/src/array_encoding/physical/binary.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/binary.rs rename to rust/lance-encoding/src/array_encoding/physical/binary.rs index 0bd4d96b45e..294b295def3 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/binary.rs +++ b/rust/lance-encoding/src/array_encoding/physical/binary.rs @@ -12,17 +12,17 @@ use futures::TryFutureExt; use futures::{FutureExt, future::BoxFuture}; +use crate::array_encoding::logical::primitive::PrimitiveFieldDecoder; use crate::buffer::LanceBuffer; use crate::data::{ BlockInfo, DataBlock, FixedWidthDataBlock, NullableDataBlock, VariableWidthBlock, }; +use crate::decoder::LogicalPageDecoder; +use crate::encoder::{ArrayEncoder, EncodedArray}; use crate::encodings::physical::block::{ BufferCompressor, CompressionConfig, GeneralBufferCompressor, }; use crate::format::ProtobufUtils; -use crate::previous::decoder::LogicalPageDecoder; -use crate::previous::encoder::{ArrayEncoder, EncodedArray}; -use crate::previous::encodings::logical::primitive::PrimitiveFieldDecoder; use crate::{ EncodingsIo, decoder::{PageScheduler, PrimitivePageDecoder}, diff --git a/rust/lance-encoding/src/previous/encodings/physical/bitmap.rs b/rust/lance-encoding/src/array_encoding/physical/bitmap.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/bitmap.rs rename to rust/lance-encoding/src/array_encoding/physical/bitmap.rs index fc6d295b0f9..2168aff24e2 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/bitmap.rs +++ b/rust/lance-encoding/src/array_encoding/physical/bitmap.rs @@ -131,9 +131,9 @@ mod tests { use bytes::Bytes; use std::{collections::HashMap, sync::Arc}; + use crate::array_encoding::physical::bitmap::BitmapData; use crate::data::{DataBlock, FixedWidthDataBlock}; use crate::decoder::PrimitivePageDecoder; - use crate::previous::encodings::physical::bitmap::BitmapData; use crate::testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data}; use super::BitmapDecoder; diff --git a/rust/lance-encoding/src/previous/encodings/physical/bitpack.rs b/rust/lance-encoding/src/array_encoding/physical/bitpack.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/bitpack.rs rename to rust/lance-encoding/src/array_encoding/physical/bitpack.rs index d80dec351d1..856af8eb8b4 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/bitpack.rs +++ b/rust/lance-encoding/src/array_encoding/physical/bitpack.rs @@ -23,8 +23,8 @@ use crate::buffer::LanceBuffer; use crate::data::BlockInfo; use crate::data::{DataBlock, FixedWidthDataBlock, NullableDataBlock}; use crate::decoder::{PageScheduler, PrimitivePageDecoder}; +use crate::encoder::{ArrayEncoder, EncodedArray}; use crate::format::ProtobufUtils; -use crate::previous::encoder::{ArrayEncoder, EncodedArray}; use bytemuck::cast_slice; const LOG_ELEMS_PER_CHUNK: u8 = 10; @@ -1146,7 +1146,6 @@ pub mod test { use crate::{ format::pb, testing::{ArrayGeneratorProvider, TestCases, check_round_trip_encoding_generated}, - version::LanceFileVersion, }; use super::*; @@ -1677,7 +1676,7 @@ pub mod test { for (data_type, array_gen_provider) in bitpacked_test_cases { let field = Field::new("", data_type.clone(), false); - let test_cases = TestCases::basic().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::basic().with_structural_encodings(); check_round_trip_encoding_generated(field, array_gen_provider.copy(), test_cases).await; } } diff --git a/rust/lance-encoding/src/previous/encodings/physical/block.rs b/rust/lance-encoding/src/array_encoding/physical/block.rs similarity index 97% rename from rust/lance-encoding/src/previous/encodings/physical/block.rs rename to rust/lance-encoding/src/array_encoding/physical/block.rs index 517bce4e3a7..3bbd966e24a 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/block.rs +++ b/rust/lance-encoding/src/array_encoding/physical/block.rs @@ -5,9 +5,9 @@ use arrow_schema::DataType; use crate::{ data::{BlockInfo, DataBlock, OpaqueBlock}, + encoder::{ArrayEncoder, EncodedArray}, encodings::physical::block::{CompressedBufferEncoder, CompressionConfig, CompressionScheme}, format::ProtobufUtils, - previous::encoder::{ArrayEncoder, EncodedArray}, }; use lance_core::Result; diff --git a/rust/lance-encoding/src/previous/encodings/physical/dictionary.rs b/rust/lance-encoding/src/array_encoding/physical/dictionary.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/dictionary.rs rename to rust/lance-encoding/src/array_encoding/physical/dictionary.rs index a4c01ff9ef5..08c5e5d8051 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/dictionary.rs +++ b/rust/lance-encoding/src/array_encoding/physical/dictionary.rs @@ -16,18 +16,18 @@ use lance_arrow::DataTypeExt; use lance_core::{Error, Result}; use std::collections::HashMap; +use crate::array_encoding::logical::primitive::PrimitiveFieldDecoder; use crate::buffer::LanceBuffer; use crate::data::{ BlockInfo, DataBlock, DictionaryDataBlock, FixedWidthDataBlock, NullableDataBlock, VariableWidthBlock, }; +use crate::decoder::LogicalPageDecoder; use crate::format::ProtobufUtils; -use crate::previous::decoder::LogicalPageDecoder; -use crate::previous::encodings::logical::primitive::PrimitiveFieldDecoder; use crate::{ EncodingsIo, decoder::{PageScheduler, PrimitivePageDecoder}, - previous::encoder::{ArrayEncoder, EncodedArray}, + encoder::{ArrayEncoder, EncodedArray}, }; #[derive(Debug)] diff --git a/rust/lance-encoding/src/previous/encodings/physical/fixed_size_binary.rs b/rust/lance-encoding/src/array_encoding/physical/fixed_size_binary.rs similarity index 98% rename from rust/lance-encoding/src/previous/encodings/physical/fixed_size_binary.rs rename to rust/lance-encoding/src/array_encoding/physical/fixed_size_binary.rs index 696edde8c9b..b655271e3aa 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/fixed_size_binary.rs +++ b/rust/lance-encoding/src/array_encoding/physical/fixed_size_binary.rs @@ -13,8 +13,8 @@ use crate::{ buffer::LanceBuffer, data::{BlockInfo, DataBlock, FixedWidthDataBlock, VariableWidthBlock}, decoder::{PageScheduler, PrimitivePageDecoder}, + encoder::{ArrayEncoder, EncodedArray}, format::ProtobufUtils, - previous::encoder::{ArrayEncoder, EncodedArray}, }; /// A scheduler for fixed size binary data @@ -173,9 +173,9 @@ mod tests { use arrow_data::ArrayData; use arrow_schema::{DataType, Field}; + use crate::array_encoding::physical::fixed_size_binary::FixedSizeBinaryDecoder; use crate::data::{DataBlock, FixedWidthDataBlock}; use crate::decoder::PrimitivePageDecoder; - use crate::previous::encodings::physical::fixed_size_binary::FixedSizeBinaryDecoder; use crate::testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data}; #[test_log::test(tokio::test)] diff --git a/rust/lance-encoding/src/previous/encodings/physical/fixed_size_list.rs b/rust/lance-encoding/src/array_encoding/physical/fixed_size_list.rs similarity index 95% rename from rust/lance-encoding/src/previous/encodings/physical/fixed_size_list.rs rename to rust/lance-encoding/src/array_encoding/physical/fixed_size_list.rs index e980301d117..a0f596fd8cf 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/fixed_size_list.rs +++ b/rust/lance-encoding/src/array_encoding/physical/fixed_size_list.rs @@ -12,8 +12,8 @@ use crate::{ EncodingsIo, data::{DataBlock, FixedSizeListBlock}, decoder::{PageScheduler, PrimitivePageDecoder}, + encoder::{ArrayEncoder, EncodedArray}, format::ProtobufUtils, - previous::encoder::{ArrayEncoder, EncodedArray}, }; /// A scheduler for fixed size lists of primitive values @@ -138,10 +138,7 @@ mod tests { use arrow_schema::{DataType, Field}; use lance_datagen::{ArrayGeneratorExt, RowCount, array, gen_array}; - use crate::{ - testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data}, - version::LanceFileVersion, - }; + use crate::testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data}; const PRIMITIVE_TYPES: &[DataType] = &[DataType::Int8, DataType::Float32, DataType::Float64]; @@ -182,7 +179,7 @@ mod tests { .with_indices(vec![0, 1, 2]) .with_indices(vec![1]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![list], &test_cases, HashMap::default()).await; } @@ -209,7 +206,7 @@ mod tests { .with_indices(vec![0, 1, 2]) .with_indices(vec![1]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![list], &test_cases, HashMap::default()).await; } @@ -260,7 +257,7 @@ mod tests { .with_range(1..3) .with_indices(vec![0, 1, 2]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![outer_list], &test_cases, HashMap::default()).await; } diff --git a/rust/lance-encoding/src/previous/encodings/physical/fsst.rs b/rust/lance-encoding/src/array_encoding/physical/fsst.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/fsst.rs rename to rust/lance-encoding/src/array_encoding/physical/fsst.rs index e9bb585ed73..fd1f11b860e 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/fsst.rs +++ b/rust/lance-encoding/src/array_encoding/physical/fsst.rs @@ -14,8 +14,8 @@ use crate::{ buffer::LanceBuffer, data::{BlockInfo, DataBlock, NullableDataBlock, VariableWidthBlock}, decoder::{PageScheduler, PrimitivePageDecoder}, + encoder::{ArrayEncoder, EncodedArray}, format::ProtobufUtils, - previous::encoder::{ArrayEncoder, EncodedArray}, }; #[derive(Debug)] diff --git a/rust/lance-encoding/src/previous/encodings/physical/packed_struct.rs b/rust/lance-encoding/src/array_encoding/physical/packed_struct.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/packed_struct.rs rename to rust/lance-encoding/src/array_encoding/physical/packed_struct.rs index 0f3a5fc3832..b608071e9b0 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/packed_struct.rs +++ b/rust/lance-encoding/src/array_encoding/physical/packed_struct.rs @@ -18,7 +18,7 @@ use crate::{ buffer::LanceBuffer, data::{DataBlock, FixedWidthDataBlock, StructDataBlock}, decoder::{PageScheduler, PrimitivePageDecoder}, - previous::encoder::{ArrayEncoder, EncodedArray}, + encoder::{ArrayEncoder, EncodedArray}, }; #[derive(Debug)] diff --git a/rust/lance-encoding/src/previous/encodings/physical/value.rs b/rust/lance-encoding/src/array_encoding/physical/value.rs similarity index 99% rename from rust/lance-encoding/src/previous/encodings/physical/value.rs rename to rust/lance-encoding/src/array_encoding/physical/value.rs index 92ec3240a14..9f594a00252 100644 --- a/rust/lance-encoding/src/previous/encodings/physical/value.rs +++ b/rust/lance-encoding/src/array_encoding/physical/value.rs @@ -18,7 +18,7 @@ use crate::format::ProtobufUtils; use crate::{ EncodingsIo, decoder::{PageScheduler, PrimitivePageDecoder}, - previous::encoder::{ArrayEncoder, EncodedArray}, + encoder::{ArrayEncoder, EncodedArray}, }; use lance_core::{Error, Result}; diff --git a/rust/lance-encoding/src/previous/encoder.rs b/rust/lance-encoding/src/array_encoding/strategy.rs similarity index 64% rename from rust/lance-encoding/src/previous/encoder.rs rename to rust/lance-encoding/src/array_encoding/strategy.rs index 9c314ae97e9..2eee3eb4e4a 100644 --- a/rust/lance-encoding/src/previous/encoder.rs +++ b/rust/lance-encoding/src/array_encoding/strategy.rs @@ -3,23 +3,14 @@ use std::{collections::HashMap, env, hash::RandomState, sync::Arc}; -use arrow_array::{ArrayRef, UInt8Array, cast::AsArray}; +#[cfg(test)] +use arrow_array::cast::AsArray; +use arrow_array::{ArrayRef, UInt8Array}; use arrow_schema::DataType; use hyperloglogplus::{HyperLogLog, HyperLogLogPlus}; use crate::{ - buffer::LanceBuffer, - data::DataBlock, - encoder::{ColumnIndexSequence, EncodingOptions, FieldEncoder, FieldEncodingStrategy}, - encodings::{ - logical::r#struct::StructFieldEncoder, - physical::{ - block::{CompressionConfig, CompressionScheme}, - value::ValueEncoder, - }, - }, - format::pb, - previous::encodings::{ + array_encoding::{ logical::{ blob::BlobFieldEncoder, list::ListFieldEncoder, primitive::PrimitiveFieldEncoder, }, @@ -27,98 +18,49 @@ use crate::{ basic::BasicEncoder, binary::BinaryEncoder, dictionary::{AlreadyDictionaryEncoder, DictionaryEncoder}, - fixed_size_binary::FixedSizeBinaryEncoder, fixed_size_list::FslEncoder, fsst::FsstArrayEncoder, packed_struct::PackedStructEncoder, }, }, - version::LanceFileVersion, -}; - -#[cfg(feature = "bitpacking")] -use crate::previous::encodings::physical::bitpack::{ - BitpackedForNonNegArrayEncoder, compute_compressed_bit_width_for_non_neg, -}; - -use crate::constants::{ - COMPRESSION_LEVEL_META_KEY, COMPRESSION_META_KEY, PACKED_STRUCT_LEGACY_META_KEY, - PACKED_STRUCT_META_KEY, + constants::{ + COMPRESSION_LEVEL_META_KEY, COMPRESSION_META_KEY, PACKED_STRUCT_LEGACY_META_KEY, + PACKED_STRUCT_META_KEY, + }, + encoder::{ + ArrayEncoder, ArrayEncodingStrategy, ColumnIndexSequence, FieldEncoder, + FieldEncodingContext, FieldEncodingStrategy, + }, + encodings::{ + logical::r#struct::StructFieldEncoder, + physical::{ + block::{CompressionConfig, CompressionScheme}, + value::ValueEncoder, + }, + }, }; use lance_arrow::BLOB_META_KEY; use lance_core::datatypes::{BLOB_DESC_FIELD, Field}; use lance_core::{Error, Result}; -/// An encoded array -/// -/// Maps to a single Arrow array -/// -/// This contains the encoded data as well as a description of the encoding that was applied which -/// can be used to decode the data later. +/// Field-to-column composition for the `pb::ArrayEncoding` grammar. #[derive(Debug)] -pub struct EncodedArray { - /// The encoded buffers - pub data: DataBlock, - /// A description of the encoding used to encode the array - pub encoding: pb::ArrayEncoding, +pub struct ArrayFieldEncodingStrategy { + array_encoding_strategy: Arc, } -impl EncodedArray { - pub fn new(data: DataBlock, encoding: pb::ArrayEncoding) -> Self { - Self { data, encoding } - } - - pub fn into_buffers(self) -> (Vec, pb::ArrayEncoding) { - let buffers = self.data.into_buffers(); - (buffers, self.encoding) - } -} - -/// Encodes data from one format to another (hopefully more compact or useful) format -/// -/// The array encoder must be Send + Sync. Encoding is always done on its own -/// thread task in the background and there could potentially be multiple encode -/// tasks running for a column at once. -pub trait ArrayEncoder: std::fmt::Debug + Send + Sync { - /// Encode data +impl ArrayFieldEncodingStrategy { + /// Create the field strategy for the `pb::ArrayEncoding` grammar. /// - /// The result should contain a description of the encoding that was chosen. - /// This can be used to decode the data later. - fn encode( - &self, - data: DataBlock, - data_type: &DataType, - buffer_index: &mut u32, - ) -> Result; -} - -/// A trait to pick which encoding strategy to use for a single page -/// of data -/// -/// Presumably, implementations will make encoding decisions based on -/// array statistics. -pub trait ArrayEncodingStrategy: Send + Sync + std::fmt::Debug { - fn create_array_encoder( - &self, - arrays: &[ArrayRef], - field: &Field, - ) -> Result>; -} - -/// The core field encoding strategy is a set of basic encodings that -/// are generally applicable in most scenarios. -#[derive(Debug)] -pub struct CoreFieldEncodingStrategy { - pub array_encoding_strategy: Arc, - pub version: LanceFileVersion, -} - -impl CoreFieldEncodingStrategy { - pub fn new(version: LanceFileVersion) -> Self { + /// ``` + /// use lance_encoding::array_encoding::ArrayFieldEncodingStrategy; + /// + /// let strategy = ArrayFieldEncodingStrategy::new(); + /// ``` + pub fn new() -> Self { Self { - array_encoding_strategy: Arc::new(CoreArrayEncodingStrategy::new(version)), - version, + array_encoding_strategy: Arc::new(ArrayStrategy), } } @@ -157,14 +99,20 @@ impl CoreFieldEncodingStrategy { } } -impl FieldEncodingStrategy for CoreFieldEncodingStrategy { +impl Default for ArrayFieldEncodingStrategy { + fn default() -> Self { + Self::new() + } +} + +impl FieldEncodingStrategy for ArrayFieldEncodingStrategy { fn create_field_encoder( &self, - encoding_strategy_root: &dyn FieldEncodingStrategy, field: &Field, column_index: &mut ColumnIndexSequence, - options: &EncodingOptions, + context: &FieldEncodingContext<'_>, ) -> Result> { + let options = context.options; let data_type = field.data_type(); if Self::is_primitive_type(&data_type) { let column_index = column_index.next_column_index(field.id as u32); @@ -192,11 +140,10 @@ impl FieldEncodingStrategy for CoreFieldEncodingStrategy { match data_type { DataType::List(_child) | DataType::LargeList(_child) => { let list_idx = column_index.next_column_index(field.id as u32); - let inner_encoding = encoding_strategy_root.create_field_encoder( - encoding_strategy_root, + let inner_encoding = context.strategy.create_field_encoder( &field.children[0], column_index, - options, + context, )?; let offsets_encoder = Arc::new(BasicEncoder::new(Box::new(ValueEncoder::default()))); @@ -227,12 +174,9 @@ impl FieldEncodingStrategy for CoreFieldEncodingStrategy { .children .iter() .map(|field| { - self.create_field_encoder( - encoding_strategy_root, - field, - column_index, - options, - ) + context + .strategy + .create_field_encoder(field, column_index, context) }) .collect::>>()?; Ok(Box::new(StructFieldEncoder::new( @@ -265,33 +209,11 @@ impl FieldEncodingStrategy for CoreFieldEncodingStrategy { } } -/// The core array encoding strategy is a set of basic encodings that -/// are generally applicable in most scenarios. +/// Page-encoding selection for the `pb::ArrayEncoding` grammar. #[derive(Debug)] -pub struct CoreArrayEncodingStrategy { - pub version: LanceFileVersion, -} - -const BINARY_DATATYPES: [DataType; 4] = [ - DataType::Binary, - DataType::LargeBinary, - DataType::Utf8, - DataType::LargeUtf8, -]; - -impl CoreArrayEncodingStrategy { - fn new(version: LanceFileVersion) -> Self { - Self { version } - } -} - -impl CoreArrayEncodingStrategy { - fn can_use_fsst(data_type: &DataType, data_size: u64, version: LanceFileVersion) -> bool { - version >= LanceFileVersion::V2_1 - && matches!(data_type, DataType::Utf8 | DataType::Binary) - && data_size > 4 * 1024 * 1024 - } +struct ArrayStrategy; +impl ArrayStrategy { fn get_field_compression(field_meta: &HashMap) -> Option { let compression = field_meta.get(COMPRESSION_META_KEY)?; let compression_scheme = compression.parse::(); @@ -308,16 +230,14 @@ impl CoreArrayEncodingStrategy { fn default_binary_encoder( arrays: &[ArrayRef], - data_type: &DataType, field_meta: Option<&HashMap>, data_size: u64, - version: LanceFileVersion, ) -> Result> { let bin_indices_encoder = - Self::choose_array_encoder(arrays, &DataType::UInt64, data_size, false, version, None)?; + Self::choose_array_encoder(arrays, &DataType::UInt64, data_size, false, None)?; if let Some(compression) = field_meta.and_then(Self::get_field_compression) { - if compression.scheme == CompressionScheme::Fsst { + if compression.scheme() == CompressionScheme::Fsst { // User requested FSST let raw_encoder = Box::new(BinaryEncoder::try_new(bin_indices_encoder, None)?); Ok(Box::new(FsstArrayEncoder::new(raw_encoder))) @@ -329,13 +249,7 @@ impl CoreArrayEncodingStrategy { )?)) } } else { - // No user-specified compression, use FSST if we can - let bin_encoder = Box::new(BinaryEncoder::try_new(bin_indices_encoder, None)?); - if Self::can_use_fsst(data_type, data_size, version) { - Ok(Box::new(FsstArrayEncoder::new(bin_encoder))) - } else { - Ok(bin_encoder) - } + Ok(Box::new(BinaryEncoder::try_new(bin_indices_encoder, None)?)) } } @@ -344,7 +258,6 @@ impl CoreArrayEncodingStrategy { data_type: &DataType, data_size: u64, use_dict_encoding: bool, - version: LanceFileVersion, field_meta: Option<&HashMap>, ) -> Result> { match data_type { @@ -355,7 +268,6 @@ impl CoreArrayEncodingStrategy { inner.data_type(), data_size, use_dict_encoding, - version, None, )?, *dimension as u32, @@ -363,10 +275,9 @@ impl CoreArrayEncodingStrategy { } DataType::Dictionary(key_type, value_type) => { let key_encoder = - Self::choose_array_encoder(arrays, key_type, data_size, false, version, None)?; - let value_encoder = Self::choose_array_encoder( - arrays, value_type, data_size, false, version, None, - )?; + Self::choose_array_encoder(arrays, key_type, data_size, false, None)?; + let value_encoder = + Self::choose_array_encoder(arrays, value_type, data_size, false, None)?; Ok(Box::new(AlreadyDictionaryEncoder::new( key_encoder, @@ -384,7 +295,6 @@ impl CoreArrayEncodingStrategy { &DataType::UInt8, data_size, false, - version, None, )?; let dict_items_encoder = Self::choose_array_encoder( @@ -392,7 +302,6 @@ impl CoreArrayEncodingStrategy { &DataType::Utf8, data_size, false, - version, None, )?; @@ -400,31 +309,8 @@ impl CoreArrayEncodingStrategy { dict_indices_encoder, dict_items_encoder, ))) - } - // The parent datatype should be binary or utf8 to use the fixed size encoding - // The variable 'data_type' is passed through recursion so comparing with it would be incorrect - else if BINARY_DATATYPES.contains(arrays[0].data_type()) { - if let Some(byte_width) = check_fixed_size_encoding(arrays, version) { - // use FixedSizeBinaryEncoder - let bytes_encoder = Self::choose_array_encoder( - arrays, - &DataType::UInt8, - data_size, - false, - version, - None, - )?; - - Ok(Box::new(BasicEncoder::new(Box::new( - FixedSizeBinaryEncoder::new(bytes_encoder, byte_width as usize), - )))) - } else { - Self::default_binary_encoder( - arrays, data_type, field_meta, data_size, version, - ) - } } else { - Self::default_binary_encoder(arrays, data_type, field_meta, data_size, version) + Self::default_binary_encoder(arrays, field_meta, data_size) } } DataType::Struct(fields) => { @@ -438,7 +324,6 @@ impl CoreArrayEncodingStrategy { inner_datatype, data_size, use_dict_encoding, - version, None, )?; inner_encoders.push(inner_encoder); @@ -446,54 +331,16 @@ impl CoreArrayEncodingStrategy { Ok(Box::new(PackedStructEncoder::new(inner_encoders))) } - DataType::UInt8 | DataType::UInt16 | DataType::UInt32 | DataType::UInt64 => { - if version >= LanceFileVersion::V2_1 && arrays[0].data_type() == data_type { - #[cfg(feature = "bitpacking")] - { - let compressed_bit_width = compute_compressed_bit_width_for_non_neg(arrays); - Ok(Box::new(BitpackedForNonNegArrayEncoder::new( - compressed_bit_width as usize, - data_type.clone(), - ))) - } - #[cfg(not(feature = "bitpacking"))] - { - Ok(Box::new(BasicEncoder::new(Box::new( - ValueEncoder::default(), - )))) - } - } else { - Ok(Box::new(BasicEncoder::new(Box::new( - ValueEncoder::default(), - )))) - } - } + DataType::UInt8 | DataType::UInt16 | DataType::UInt32 | DataType::UInt64 => Ok( + Box::new(BasicEncoder::new(Box::new(ValueEncoder::default()))), + ), // TODO: for signed integers, I intend to make it a cascaded encoding, a sparse array for the negative values and very wide(bit-width) values, // then a bitpacked array for the narrow(bit-width) values, I need `BitpackedForNeg` to be merged first, I am // thinking about putting this sparse array in the metadata so bitpacking remain using one page buffer only. - DataType::Int8 | DataType::Int16 | DataType::Int32 | DataType::Int64 => { - if version >= LanceFileVersion::V2_1 && arrays[0].data_type() == data_type { - #[cfg(feature = "bitpacking")] - { - let compressed_bit_width = compute_compressed_bit_width_for_non_neg(arrays); - Ok(Box::new(BitpackedForNonNegArrayEncoder::new( - compressed_bit_width as usize, - data_type.clone(), - ))) - } - #[cfg(not(feature = "bitpacking"))] - { - Ok(Box::new(BasicEncoder::new(Box::new( - ValueEncoder::default(), - )))) - } - } else { - Ok(Box::new(BasicEncoder::new(Box::new( - ValueEncoder::default(), - )))) - } - } + DataType::Int8 | DataType::Int16 | DataType::Int32 | DataType::Int64 => Ok(Box::new( + BasicEncoder::new(Box::new(ValueEncoder::default())), + )), _ => Ok(Box::new(BasicEncoder::new(Box::new( ValueEncoder::default(), )))), @@ -539,8 +386,9 @@ fn check_dict_encoding(arrays: &[ArrayRef], threshold: u64) -> bool { true } -fn check_fixed_size_encoding(arrays: &[ArrayRef], version: LanceFileVersion) -> Option { - if version < LanceFileVersion::V2_1 || arrays.is_empty() { +#[cfg(test)] +fn check_fixed_size_encoding(arrays: &[ArrayRef]) -> Option { + if arrays.is_empty() { return None; } @@ -612,7 +460,7 @@ fn check_fixed_size_encoding(arrays: &[ArrayRef], version: LanceFileVersion) -> } } -impl ArrayEncodingStrategy for CoreArrayEncodingStrategy { +impl ArrayEncodingStrategy for ArrayStrategy { fn create_array_encoder( &self, arrays: &[ArrayRef], @@ -632,7 +480,6 @@ impl ArrayEncodingStrategy for CoreArrayEncodingStrategy { data_type, data_size, use_dict_encoding, - self.version, Some(&field.metadata), ) } @@ -640,12 +487,10 @@ impl ArrayEncodingStrategy for CoreArrayEncodingStrategy { #[cfg(test)] mod tests { - use crate::constants::{COMPRESSION_LEVEL_META_KEY, COMPRESSION_META_KEY}; - use crate::previous::encoder::{ - ArrayEncodingStrategy, CoreArrayEncodingStrategy, check_dict_encoding, - check_fixed_size_encoding, + use super::{ + ArrayEncodingStrategy, ArrayStrategy, check_dict_encoding, check_fixed_size_encoding, }; - use crate::version::LanceFileVersion; + use crate::constants::{COMPRESSION_LEVEL_META_KEY, COMPRESSION_META_KEY}; use arrow_array::{ArrayRef, StringArray}; use arrow_schema::Field; use std::collections::HashMap; @@ -691,10 +536,7 @@ mod tests { assert!(!is_dict_encoding_applicable(vec![Some("a"), Some("a")], 3)); } - fn is_fixed_size_encoding_applicable( - arrays: Vec>>, - version: LanceFileVersion, - ) -> bool { + fn is_fixed_size_encoding_applicable(arrays: Vec>>) -> bool { let mut final_arrays = Vec::new(); for arr in arrays { let arr = StringArray::from(arr); @@ -702,82 +544,78 @@ mod tests { final_arrays.push(arr); } - check_fixed_size_encoding(&final_arrays.clone(), version).is_some() + check_fixed_size_encoding(&final_arrays).is_some() } #[test] fn test_fixed_size_binary_encoding_applicable() { - assert!(!is_fixed_size_encoding_applicable( - vec![vec![]], - LanceFileVersion::V2_1 - )); - - assert!(is_fixed_size_encoding_applicable( - vec![vec![Some("a"), Some("b")]], - LanceFileVersion::V2_1 - )); - - assert!(!is_fixed_size_encoding_applicable( - vec![vec![Some("abc"), Some("de")]], - LanceFileVersion::V2_1 - )); - - assert!(is_fixed_size_encoding_applicable( - vec![vec![Some("pqr"), None]], - LanceFileVersion::V2_1 - )); - - assert!(!is_fixed_size_encoding_applicable( - vec![vec![Some("pqr"), Some("")]], - LanceFileVersion::V2_1 - )); - - assert!(!is_fixed_size_encoding_applicable( - vec![vec![Some(""), Some("")]], - LanceFileVersion::V2_1 - )); + assert!(!is_fixed_size_encoding_applicable(vec![vec![]])); + + assert!(is_fixed_size_encoding_applicable(vec![vec![ + Some("a"), + Some("b") + ]])); + + assert!(!is_fixed_size_encoding_applicable(vec![vec![ + Some("abc"), + Some("de") + ]])); + + assert!(is_fixed_size_encoding_applicable(vec![vec![ + Some("pqr"), + None + ]])); + + assert!(!is_fixed_size_encoding_applicable(vec![vec![ + Some("pqr"), + Some("") + ]])); + + assert!(!is_fixed_size_encoding_applicable(vec![vec![ + Some(""), + Some("") + ]])); } #[test] fn test_fixed_size_binary_encoding_applicable_multiple_arrays() { - assert!(is_fixed_size_encoding_applicable( - vec![vec![Some("a"), Some("b")], vec![Some("c"), Some("d")]], - LanceFileVersion::V2_1 - )); - - assert!(!is_fixed_size_encoding_applicable( - vec![vec![Some("ab"), Some("bc")], vec![Some("c"), Some("d")]], - LanceFileVersion::V2_1 - )); - - assert!(!is_fixed_size_encoding_applicable( - vec![vec![Some("ab"), None], vec![None, Some("d")]], - LanceFileVersion::V2_1 - )); - - assert!(is_fixed_size_encoding_applicable( - vec![vec![Some("a"), None], vec![None, Some("d")]], - LanceFileVersion::V2_1 - )); - - assert!(!is_fixed_size_encoding_applicable( - vec![vec![Some(""), None], vec![None, Some("")]], - LanceFileVersion::V2_1 - )); - - assert!(!is_fixed_size_encoding_applicable( - vec![vec![None, None], vec![None, None]], - LanceFileVersion::V2_1 - )); + assert!(is_fixed_size_encoding_applicable(vec![ + vec![Some("a"), Some("b")], + vec![Some("c"), Some("d")] + ])); + + assert!(!is_fixed_size_encoding_applicable(vec![ + vec![Some("ab"), Some("bc")], + vec![Some("c"), Some("d")] + ])); + + assert!(!is_fixed_size_encoding_applicable(vec![ + vec![Some("ab"), None], + vec![None, Some("d")] + ])); + + assert!(is_fixed_size_encoding_applicable(vec![ + vec![Some("a"), None], + vec![None, Some("d")] + ])); + + assert!(!is_fixed_size_encoding_applicable(vec![ + vec![Some(""), None], + vec![None, Some("")] + ])); + + assert!(!is_fixed_size_encoding_applicable(vec![ + vec![None, None], + vec![None, None] + ])); } fn verify_array_encoder( array: ArrayRef, field_meta: Option>, - version: LanceFileVersion, expected_encoder: &str, ) { - let encoding_strategy = CoreArrayEncodingStrategy { version }; + let encoding_strategy = ArrayStrategy; let mut field = Field::new("test_field", array.data_type().clone(), true); if let Some(field_meta) = field_meta { field.set_metadata(field_meta); @@ -797,7 +635,6 @@ mod tests { COMPRESSION_META_KEY.to_string(), "zstd".to_string(), )])), - LanceFileVersion::V2_1, "BinaryEncoder { indices_encoder: BasicEncoder { values_encoder: ValueEncoder }, compression_config: Some(CompressionConfig { scheme: Zstd, level: None }), buffer_compressor: Some(ZstdBufferCompressor { compression_level: 0 }) }", ); } @@ -810,7 +647,6 @@ mod tests { (COMPRESSION_META_KEY.to_string(), "zstd".to_string()), (COMPRESSION_LEVEL_META_KEY.to_string(), "22".to_string()), ])), - LanceFileVersion::V2_1, "BinaryEncoder { indices_encoder: BasicEncoder { values_encoder: ValueEncoder }, compression_config: Some(CompressionConfig { scheme: Zstd, level: Some(22) }), buffer_compressor: Some(ZstdBufferCompressor { compression_level: 22 }) }", ); } diff --git a/rust/lance-encoding/src/compression.rs b/rust/lance-encoding/src/compression.rs index 8d2d28f21b0..f4817e76a3e 100644 --- a/rust/lance-encoding/src/compression.rs +++ b/rust/lance-encoding/src/compression.rs @@ -20,7 +20,7 @@ use crate::encodings::physical::bitpacking::{InlineBitpacking, OutOfLineBitpacking}; use crate::{ buffer::LanceBuffer, - compression_config::{BssMode, CompressionFieldParams, CompressionParams}, + compression_config::{BssMode, CompressionFieldParams}, constants::{ BSS_META_KEY, COMPRESSION_LEVEL_META_KEY, COMPRESSION_META_KEY, RLE_THRESHOLD_META_KEY, }, @@ -66,7 +66,6 @@ use crate::{ pb21::{CompressiveEncoding, compressive_encoding::Compression}, }, statistics::{GetStat, Stat}, - version::LanceFileVersion, }; use arrow_array::{cast::AsArray, types::UInt64Type}; @@ -141,14 +140,6 @@ pub trait CompressionStrategy: Send + Sync + std::fmt::Debug { ) -> Result>; } -#[derive(Debug, Default, Clone)] -pub struct DefaultCompressionStrategy { - /// User-configured compression parameters - params: CompressionParams, - /// The lance file version for compatibilities. - version: LanceFileVersion, -} - fn try_bss_for_mini_block( data: &FixedWidthDataBlock, params: &CompressionFieldParams, @@ -169,12 +160,7 @@ fn try_bss_for_mini_block( None } -fn try_rle_for_mini_block( - data: &FixedWidthDataBlock, - version: LanceFileVersion, - params: &CompressionFieldParams, - use_rle_v2: bool, -) -> Option> { +fn rle_is_applicable(data: &FixedWidthDataBlock, params: &CompressionFieldParams) -> Option { let bits = data.bits_per_value; if !matches!(bits, 8 | 16 | 32 | 64) { return None; @@ -197,40 +183,59 @@ fn try_rle_for_mini_block( return None; } - let num_values = data.num_values; - let raw_bytes = (num_values as u128) * (type_size as u128); - let (run_length_width, rle_bytes) = if use_rle_v2 { - estimate_rle_width_and_size_from_data(data, Some(*MAX_MINIBLOCK_VALUES)).ok()? - } else { - ( - RunLengthWidth::U8, - estimate_rle_size_for_width_from_data( - data, - Some(*MAX_MINIBLOCK_VALUES), - RunLengthWidth::U8, - ) - .ok()?, - ) - }; + Some((data.num_values as u128) * (type_size as u128)) +} - let use_child_encodings = version.resolve() >= LanceFileVersion::V2_3; - let child_compression = if use_child_encodings { - rle_child_compression_config(params) - } else { - None - }; - let use_child_bitpacking = use_child_encodings; - let rle_encoder = || { - if use_child_encodings { - RleEncoder::with_child_encoding( - run_length_width, - child_compression, - child_compression, - use_child_bitpacking, - ) - } else { - RleEncoder::with_run_length_width(run_length_width) +fn rle_beats_raw_and_bitpacking( + data: &FixedWidthDataBlock, + encoded_bytes: u128, + raw_bytes: u128, +) -> bool { + if encoded_bytes >= raw_bytes { + return false; + } + + #[cfg(feature = "bitpacking")] + { + if let Some(bitpack_bytes) = estimate_inline_bitpacking_bytes(data).map(u128::from) + && bitpack_bytes < encoded_bytes + { + return false; } + } + true +} + +fn try_fixed_u8_rle_for_mini_block( + data: &FixedWidthDataBlock, + params: &CompressionFieldParams, +) -> Option> { + let raw_bytes = rle_is_applicable(data, params)?; + let rle_bytes = estimate_rle_size_for_width_from_data( + data, + Some(*MAX_MINIBLOCK_VALUES), + RunLengthWidth::U8, + ) + .ok()?; + rle_beats_raw_and_bitpacking(data, rle_bytes, raw_bytes) + .then(|| Box::new(RleEncoder::with_run_length_width(RunLengthWidth::U8)) as _) +} + +fn try_child_rle_for_mini_block( + data: &FixedWidthDataBlock, + params: &CompressionFieldParams, +) -> Option> { + let raw_bytes = rle_is_applicable(data, params)?; + let (run_length_width, estimated_bytes) = + estimate_rle_width_and_size_from_data(data, Some(*MAX_MINIBLOCK_VALUES)).ok()?; + let child_compression = rle_child_compression_config(params); + let encoder = || { + RleEncoder::with_child_encoding( + run_length_width, + child_compression, + child_compression, + true, + ) }; #[cfg(feature = "bitpacking")] @@ -238,23 +243,16 @@ fn try_rle_for_mini_block( #[cfg(not(feature = "bitpacking"))] let bitpack_bytes = None::; - let mut selected_rle_bytes = rle_bytes; - let should_estimate_child_size = use_child_encodings - && (child_compression.is_some() || cfg!(feature = "bitpacking")) - && (rle_bytes >= raw_bytes || bitpack_bytes.is_some_and(|bytes| bytes < rle_bytes)); - if should_estimate_child_size { - selected_rle_bytes = rle_encoder().selected_payload_size(data).ok()?; - } + let should_measure_children = (child_compression.is_some() || cfg!(feature = "bitpacking")) + && (estimated_bytes >= raw_bytes + || bitpack_bytes.is_some_and(|bytes| bytes < estimated_bytes)); + let selected_bytes = if should_measure_children { + encoder().selected_payload_size(data).ok()? + } else { + estimated_bytes + }; - if selected_rle_bytes < raw_bytes { - if let Some(bitpack_bytes) = bitpack_bytes - && bitpack_bytes < selected_rle_bytes - { - return None; - } - return Some(Box::new(rle_encoder())); - } - None + rle_beats_raw_and_bitpacking(data, selected_bytes, raw_bytes).then(|| Box::new(encoder()) as _) } fn rle_child_compression_config(params: &CompressionFieldParams) -> Option { @@ -266,16 +264,12 @@ fn rle_child_compression_config(params: &CompressionFieldParams) -> Option Result, CompressiveEncoding)>> { - if version < LanceFileVersion::V2_2 { - return Ok(None); - } - let bits = data.bits_per_value; if !matches!(bits, 8 | 16 | 32 | 64) { return Ok(None); @@ -296,14 +290,6 @@ fn try_rle_for_block( } let raw_bytes = (data.num_values as u128) * ((bits / 8) as u128); - let (run_length_width, rle_payload_bytes) = if use_rle_v2 { - estimate_rle_width_and_size_from_data(data, None)? - } else { - ( - RunLengthWidth::U8, - estimate_rle_size_for_width_from_data(data, None, RunLengthWidth::U8)?, - ) - }; let rle_bytes = rle_payload_bytes.saturating_add(RLE_BLOCK_HEADER_BYTES); if rle_bytes >= raw_bytes { @@ -327,6 +313,28 @@ fn try_rle_for_block( Ok(Some((compressor, encoding))) } +fn try_fixed_u8_rle_for_block( + data: &FixedWidthDataBlock, + params: &CompressionFieldParams, +) -> Result, CompressiveEncoding)>> { + if !matches!(data.bits_per_value, 8 | 16 | 32 | 64) { + return Ok(None); + } + let encoded_bytes = estimate_rle_size_for_width_from_data(data, None, RunLengthWidth::U8)?; + try_rle_for_block_with_width(data, params, RunLengthWidth::U8, encoded_bytes) +} + +fn try_variable_rle_for_block( + data: &FixedWidthDataBlock, + params: &CompressionFieldParams, +) -> Result, CompressiveEncoding)>> { + if !matches!(data.bits_per_value, 8 | 16 | 32 | 64) { + return Ok(None); + } + let (width, encoded_bytes) = estimate_rle_width_and_size_from_data(data, None)?; + try_rle_for_block_with_width(data, params, width, encoded_bytes) +} + fn estimate_rle_width_and_size_from_data( data: &FixedWidthDataBlock, max_segment_values: Option, @@ -512,7 +520,6 @@ fn maybe_wrap_general_for_mini_block( } fn try_general_compression( - version: LanceFileVersion, field_params: &CompressionFieldParams, data: &DataBlock, ) -> Result, CompressionConfig)>> { @@ -523,9 +530,7 @@ fn try_general_compression( // User-requested compression (unused today but perhaps still used // in the future someday) - if let Some(compression_scheme) = &field_params.compression - && version >= LanceFileVersion::V2_2 - { + if let Some(compression_scheme) = &field_params.compression { let scheme: CompressionScheme = compression_scheme.parse()?; let config = CompressionConfig::new(scheme, field_params.compression_level); let compressor = Box::new(CompressedBufferEncoder::try_new(config)?); @@ -533,9 +538,7 @@ fn try_general_compression( } // Automatic compression for large blocks - if data.data_size() > MIN_BLOCK_SIZE_FOR_GENERAL_COMPRESSION - && version >= LanceFileVersion::V2_2 - { + if data.data_size() > MIN_BLOCK_SIZE_FOR_GENERAL_COMPRESSION { let compressor = Box::new(CompressedBufferEncoder::default()); let config = compressor.compressor.config(); return Ok(Some((compressor, config))); @@ -544,355 +547,339 @@ fn try_general_compression( Ok(None) } -impl DefaultCompressionStrategy { - /// Create a new compression strategy with default behavior - pub fn new() -> Self { - Self::default() - } +/// Parse field-level compression metadata without applying format-specific constraints. +pub fn field_metadata_params(field: &Field) -> CompressionFieldParams { + let mut params = CompressionFieldParams::default(); - /// Create a new compression strategy with user-configured parameters - pub fn with_params(params: CompressionParams) -> Self { - Self { - params, - version: LanceFileVersion::default(), - } + if let Some(compression) = field.metadata.get(COMPRESSION_META_KEY) { + params.compression = Some(compression.clone()); } - - /// Override the file version used to make compression decisions - pub fn with_version(mut self, version: LanceFileVersion) -> Self { - self.version = version; - self + if let Some(level) = field.metadata.get(COMPRESSION_LEVEL_META_KEY) { + params.compression_level = level.parse().ok(); } - - fn use_rle_v2(&self) -> bool { - self.version.resolve() >= LanceFileVersion::V2_3 + if let Some(threshold) = field.metadata.get(RLE_THRESHOLD_META_KEY) { + params.rle_threshold = threshold.parse().ok(); } - - /// Parse compression parameters from field metadata - fn parse_field_metadata(field: &Field, version: &LanceFileVersion) -> CompressionFieldParams { - let mut params = CompressionFieldParams::default(); - - // Parse compression method - if let Some(compression) = field.metadata.get(COMPRESSION_META_KEY) { - params.compression = Some(compression.clone()); - } - - // Parse compression level - if let Some(level) = field.metadata.get(COMPRESSION_LEVEL_META_KEY) { - params.compression_level = level.parse().ok(); + if let Some(bss_str) = field.metadata.get(BSS_META_KEY) { + match BssMode::parse(bss_str) { + Some(mode) => params.bss = Some(mode), + None => log::warn!("Invalid BSS mode '{}', using default", bss_str), } - - // Parse RLE threshold - if let Some(threshold) = field.metadata.get(RLE_THRESHOLD_META_KEY) { - params.rle_threshold = threshold.parse().ok(); - } - - // Parse BSS mode - if let Some(bss_str) = field.metadata.get(BSS_META_KEY) { - match BssMode::parse(bss_str) { - Some(mode) => params.bss = Some(mode), - None => { - log::warn!("Invalid BSS mode '{}', using default", bss_str); - } - } - } - - // Parse minichunk size - if let Some(minichunk_size_str) = field - .metadata - .get(super::constants::MINICHUNK_SIZE_META_KEY) - { - if let Ok(minichunk_size) = minichunk_size_str.parse::() { - // for lance v2.1, only 32kb or smaller is supported - if minichunk_size >= 32 * 1024 && *version <= LanceFileVersion::V2_1 { - log::warn!( - "minichunk_size '{}' too large for version '{}', using default", - minichunk_size, - version - ); - } else { - params.minichunk_size = Some(minichunk_size); - } - } else { - log::warn!("Invalid minichunk_size '{}', skipping", minichunk_size_str); - } - } - - params } - - fn build_fixed_width_compressor( - &self, - params: &CompressionFieldParams, - data: &FixedWidthDataBlock, - ) -> Result> { - if params.compression.as_deref() == Some("none") { - return Ok(Box::new(ValueEncoder::default())); + if let Some(minichunk_size_str) = field + .metadata + .get(super::constants::MINICHUNK_SIZE_META_KEY) + { + if let Ok(minichunk_size) = minichunk_size_str.parse::() { + params.minichunk_size = Some(minichunk_size); + } else { + log::warn!("Invalid minichunk_size '{}', skipping", minichunk_size_str); } + } - let base = try_bss_for_mini_block(data, params) - .or_else(|| try_rle_for_mini_block(data, self.version, params, self.use_rle_v2())) - .or_else(|| try_bitpack_for_mini_block(data)) - .unwrap_or_else(|| Box::new(ValueEncoder::default())); + params +} - maybe_wrap_general_for_mini_block(base, params) +/// Apply general-purpose compression requested for a fixed-width miniblock. +pub fn finalize_miniblock_compressor( + data: &DataBlock, + compressor: Box, + params: &CompressionFieldParams, +) -> Result> { + if matches!(data, DataBlock::FixedWidth(_)) { + maybe_wrap_general_for_mini_block(compressor, params) + } else { + Ok(compressor) } +} - /// Build compressor based on parameters for variable-width data - fn build_variable_width_compressor( - &self, - field: &Field, - data: &VariableWidthBlock, - ) -> Result> { - let params = self.get_merged_field_params(field); - let compression = params.compression.as_deref(); - if data.bits_per_offset != 32 && data.bits_per_offset != 64 { - return Err(Error::invalid_input(format!( - "Variable width compression not supported for {} bit offsets", - data.bits_per_offset - ))); - } +/// Honor an explicit `compression = none` request for fixed-width miniblocks. +pub fn try_uncompressed_fixed_width_miniblock( + data: &DataBlock, + params: &CompressionFieldParams, +) -> Option> { + (matches!(data, DataBlock::FixedWidth(_)) && params.compression.as_deref() == Some("none")) + .then(|| Box::new(ValueEncoder::default()) as _) +} - // Get statistics - let data_size = data.expect_single_stat::(Stat::DataSize); - let max_len = data.expect_single_stat::(Stat::MaxLength); +/// Select byte-stream-split compression for an applicable fixed-width miniblock. +pub fn try_byte_stream_split_miniblock( + data: &DataBlock, + params: &CompressionFieldParams, +) -> Option> { + let DataBlock::FixedWidth(data) = data else { + return None; + }; + try_bss_for_mini_block(data, params) +} - // Explicitly disable all compression. - if compression == Some("none") { - return Ok(Box::new(BinaryMiniBlockEncoder::new(params.minichunk_size))); - } +/// Select the original fixed-u8 RLE miniblock grammar. +pub fn try_fixed_u8_rle_miniblock( + data: &DataBlock, + params: &CompressionFieldParams, +) -> Option> { + let DataBlock::FixedWidth(data) = data else { + return None; + }; + try_fixed_u8_rle_for_mini_block(data, params) +} - let use_fsst = compression == Some("fsst") - || (compression.is_none() - && !matches!(field.data_type(), DataType::Binary | DataType::LargeBinary) - && max_len >= FSST_LEAST_INPUT_MAX_LENGTH - && data_size >= FSST_LEAST_INPUT_SIZE as u64); +/// Select variable-width RLE with independently encoded children. +pub fn try_child_rle_miniblock( + data: &DataBlock, + params: &CompressionFieldParams, +) -> Option> { + let DataBlock::FixedWidth(data) = data else { + return None; + }; + try_child_rle_for_mini_block(data, params) +} - // Choose base encoder (FSST or Binary) once. - let mut base_encoder: Box = if use_fsst { - Box::new(FsstMiniBlockEncoder::new(params.minichunk_size)) - } else { - Box::new(BinaryMiniBlockEncoder::new(params.minichunk_size)) - }; +/// Select inline bitpacking for applicable fixed-width miniblocks. +pub fn try_bitpacking_miniblock(data: &DataBlock) -> Option> { + let DataBlock::FixedWidth(data) = data else { + return None; + }; + try_bitpack_for_mini_block(data) +} - // Wrap with general compression when configured (except FSST / none). - if let Some(compression_scheme) = compression.filter(|scheme| *scheme != "fsst") { - let scheme: CompressionScheme = compression_scheme.parse()?; - let config = CompressionConfig::new(scheme, params.compression_level); - base_encoder = Box::new(GeneralMiniBlockCompressor::new(base_encoder, config)); - } +/// Store fixed-width miniblock values without a value codec. +pub fn try_raw_fixed_width_miniblock(data: &DataBlock) -> Option> { + matches!(data, DataBlock::FixedWidth(_)).then(|| Box::new(ValueEncoder::default()) as _) +} - Ok(base_encoder) +/// Encode variable-width miniblocks with binary or FSST encoding. +pub fn try_variable_width_miniblock( + field: &Field, + data: &DataBlock, + params: &CompressionFieldParams, +) -> Result>> { + let DataBlock::VariableWidth(data) = data else { + return Ok(None); + }; + if data.bits_per_offset != 32 && data.bits_per_offset != 64 { + return Err(Error::invalid_input(format!( + "Variable width compression not supported for {} bit offsets", + data.bits_per_offset + ))); } - /// Merge user-configured parameters with field metadata - /// Field metadata has highest priority - fn get_merged_field_params(&self, field: &Field) -> CompressionFieldParams { - let mut field_params = self - .params - .get_field_params(&field.name, &field.data_type()); - - // Override with field metadata if present (highest priority) - let metadata_params = Self::parse_field_metadata(field, &self.version); - field_params.merge(&metadata_params); + let compression = params.compression.as_deref(); + let data_size = data.expect_single_stat::(Stat::DataSize); + let max_len = data.expect_single_stat::(Stat::MaxLength); + if compression == Some("none") { + return Ok(Some(Box::new(BinaryMiniBlockEncoder::new( + params.minichunk_size, + )))); + } - field_params + let use_fsst = compression == Some("fsst") + || (compression.is_none() + && !matches!(field.data_type(), DataType::Binary | DataType::LargeBinary) + && max_len >= FSST_LEAST_INPUT_MAX_LENGTH + && data_size >= FSST_LEAST_INPUT_SIZE as u64); + let mut encoder: Box = if use_fsst { + Box::new(FsstMiniBlockEncoder::new(params.minichunk_size)) + } else { + Box::new(BinaryMiniBlockEncoder::new(params.minichunk_size)) + }; + if let Some(compression_scheme) = compression.filter(|scheme| *scheme != "fsst") { + let scheme: CompressionScheme = compression_scheme.parse()?; + let config = CompressionConfig::new(scheme, params.compression_level); + encoder = Box::new(GeneralMiniBlockCompressor::new(encoder, config)); } + Ok(Some(encoder)) } -impl CompressionStrategy for DefaultCompressionStrategy { - fn create_miniblock_compressor( - &self, - field: &Field, - data: &DataBlock, - ) -> Result> { - match data { - DataBlock::FixedWidth(fixed_width_data) => { - let field_params = self.get_merged_field_params(field); - self.build_fixed_width_compressor(&field_params, fixed_width_data) - } - DataBlock::VariableWidth(variable_width_data) => { - self.build_variable_width_compressor(field, variable_width_data) - } - DataBlock::Struct(struct_data_block) => { - // this condition is actually checked at `PrimitiveStructuralEncoder::do_flush`, - // just being cautious here. - if struct_data_block.has_variable_width_child() { - return Err(Error::invalid_input( - "Packed struct mini-block encoding supports only fixed-width children", - )); - } - Ok(Box::new(PackedStructFixedWidthMiniBlockEncoder::default())) - } - DataBlock::FixedSizeList(_) => { - // Ideally we would compress the list items but this creates something of a challenge. - // We don't want to break lists across chunks and we need to worry about inner validity - // layers. If we try and use a compression scheme then it is unlikely to respect these - // constraints. - // - // For now, we just don't compress. In the future, we might want to consider a more - // sophisticated approach. - Ok(Box::new(ValueEncoder::default())) - } - _ => Err(Error::not_supported_source( - format!( - "Mini-block compression not yet supported for block type {}", - data.name() - ) - .into(), - )), - } +/// Encode fixed-width packed structs as miniblocks. +pub fn try_fixed_packed_struct_miniblock( + data: &DataBlock, +) -> Result>> { + let DataBlock::Struct(data) = data else { + return Ok(None); + }; + if data.has_variable_width_child() { + return Err(Error::invalid_input( + "Packed struct mini-block encoding supports only fixed-width children", + )); } + Ok(Some(Box::new( + PackedStructFixedWidthMiniBlockEncoder::default(), + ))) +} - fn create_per_value( - &self, - field: &Field, - data: &DataBlock, - ) -> Result> { - let field_params = self.get_merged_field_params(field); - - match data { - DataBlock::FixedWidth(_) => Ok(Box::new(ValueEncoder::default())), - DataBlock::FixedSizeList(_) => Ok(Box::new(ValueEncoder::default())), - DataBlock::Struct(struct_block) => { - if field.children.len() != struct_block.children.len() { - return Err(Error::invalid_input( - "Struct field metadata does not match data block children", - )); - } - let has_variable_child = struct_block.has_variable_width_child(); - if has_variable_child { - if self.version < LanceFileVersion::V2_2 { - return Err(Error::not_supported_source("Variable packed struct encoding requires Lance file version 2.2 or later".into())); - } - Ok(Box::new(PackedStructVariablePerValueEncoder::new( - self.clone(), - field.children.clone(), - ))) - } else { - Err(Error::invalid_input( - "Packed struct per-value compression should not be used for fixed-width-only structs", - )) - } - } - DataBlock::VariableWidth(variable_width) => { - let compression = field_params.compression.as_deref(); - // Check for explicit "none" compression - if compression == Some("none") { - return Ok(Box::new(VariableEncoder::default())); - } +/// Store fixed-size-list miniblocks without a value codec. +pub fn try_raw_fixed_size_list_miniblock(data: &DataBlock) -> Option> { + matches!(data, DataBlock::FixedSizeList(_)).then(|| Box::new(ValueEncoder::default()) as _) +} - let max_len = variable_width.expect_single_stat::(Stat::MaxLength); - let data_size = variable_width.expect_single_stat::(Stat::DataSize); +/// Store fixed-width and fixed-size-list values directly in full-zip pages. +pub fn try_raw_per_value(data: &DataBlock) -> Option> { + matches!(data, DataBlock::FixedWidth(_) | DataBlock::FixedSizeList(_)) + .then(|| Box::new(ValueEncoder::default()) as _) +} - // If values are very large then use block compression on a per-value basis - // - // TODO: Could maybe use median here +fn validate_packed_struct(field: &Field, data: &DataBlock) -> Result> { + let DataBlock::Struct(data) = data else { + return Ok(None); + }; + if field.children.len() != data.children.len() { + return Err(Error::invalid_input( + "Struct field metadata does not match data block children", + )); + } + Ok(Some(data.has_variable_width_child())) +} - let per_value_requested = - compression.is_some_and(|compression| compression != "fsst"); +/// Reject variable-width packed structs while preserving the fixed-width error. +pub fn reject_packed_struct_per_value( + field: &Field, + data: &DataBlock, +) -> Result>> { + let Some(has_variable_child) = validate_packed_struct(field, data)? else { + return Ok(None); + }; + if has_variable_child { + return Err(Error::not_supported_source( + "Variable packed struct encoding is not enabled by the selected file format".into(), + )); + } + Err(Error::invalid_input( + "Packed struct per-value compression should not be used for fixed-width-only structs", + )) +} - if (max_len > 32 * 1024 || per_value_requested) - && data_size >= FSST_LEAST_INPUT_SIZE as u64 - { - if compression == Some("zstd") { - let config = CompressionConfig::new( - CompressionScheme::Zstd, - field_params.compression_level, - ); - return Ok(Box::new(CompressedBufferEncoder::try_new(config)?)); - } - return Ok(Box::new(CompressedBufferEncoder::default())); - } +/// Encode variable-width packed structs with the exact strategy recursively. +pub fn try_variable_packed_struct_per_value( + strategy: Arc, + field: &Field, + data: &DataBlock, +) -> Result>> { + let Some(has_variable_child) = validate_packed_struct(field, data)? else { + return Ok(None); + }; + if !has_variable_child { + return Err(Error::invalid_input( + "Packed struct per-value compression should not be used for fixed-width-only structs", + )); + } + Ok(Some(Box::new(PackedStructVariablePerValueEncoder::new( + strategy, + field.children.clone(), + )))) +} - if variable_width.bits_per_offset == 32 || variable_width.bits_per_offset == 64 { - let variable_compression = Box::new(VariableEncoder::default()); - let use_fsst = compression == Some("fsst") - || (compression.is_none() - && !matches!( - field.data_type(), - DataType::Binary | DataType::LargeBinary - ) - && max_len >= FSST_LEAST_INPUT_MAX_LENGTH - && data_size >= FSST_LEAST_INPUT_SIZE as u64); - - // Use FSST if explicitly requested or if data characteristics warrant it. - if use_fsst { - Ok(Box::new(FsstPerValueEncoder::new(variable_compression))) - } else { - Ok(variable_compression) - } - } else { - panic!( - "Does not support MiniBlockCompression for VariableWidth DataBlock with {} bits offsets.", - variable_width.bits_per_offset - ); - } - } - _ => unreachable!( - "Per-value compression not yet supported for block type: {}", - data.name() - ), +/// Encode variable-width values directly, with FSST or per-value compression +/// when applicable. +pub fn try_variable_width_per_value( + field: &Field, + data: &DataBlock, + params: &CompressionFieldParams, +) -> Result>> { + let DataBlock::VariableWidth(data) = data else { + return Ok(None); + }; + let compression = params.compression.as_deref(); + if compression == Some("none") { + return Ok(Some(Box::new(VariableEncoder::default()))); + } + + let max_len = data.expect_single_stat::(Stat::MaxLength); + let data_size = data.expect_single_stat::(Stat::DataSize); + let per_value_requested = compression.is_some_and(|compression| compression != "fsst"); + if (max_len > 32 * 1024 || per_value_requested) && data_size >= FSST_LEAST_INPUT_SIZE as u64 { + if compression == Some("zstd") { + let config = CompressionConfig::new(CompressionScheme::Zstd, params.compression_level); + return Ok(Some(Box::new(CompressedBufferEncoder::try_new(config)?))); } + return Ok(Some(Box::new(CompressedBufferEncoder::default()))); } - fn create_block_compressor( - &self, - field: &Field, - data: &DataBlock, - ) -> Result<(Box, CompressiveEncoding)> { - let field_params = self.get_merged_field_params(field); + if data.bits_per_offset != 32 && data.bits_per_offset != 64 { + return Err(Error::invalid_input(format!( + "Per-value compression does not support variable-width data with {}-bit offsets", + data.bits_per_offset + ))); + } + let encoder = Box::new(VariableEncoder::default()); + let use_fsst = compression == Some("fsst") + || (compression.is_none() + && !matches!(field.data_type(), DataType::Binary | DataType::LargeBinary) + && max_len >= FSST_LEAST_INPUT_MAX_LENGTH + && data_size >= FSST_LEAST_INPUT_SIZE as u64); + Ok(Some(if use_fsst { + Box::new(FsstPerValueEncoder::new(encoder)) + } else { + encoder + })) +} - match data { - DataBlock::FixedWidth(fixed_width) => { - if let Some((compressor, encoding)) = - try_rle_for_block(fixed_width, self.version, &field_params, self.use_rle_v2())? - { - return Ok((compressor, encoding)); - } - if let Some((compressor, encoding)) = try_bitpack_for_block(fixed_width) { - return Ok((compressor, encoding)); - } +/// Select fixed-u8 RLE for block compression. +pub fn try_fixed_u8_rle_block( + data: &DataBlock, + params: &CompressionFieldParams, +) -> Result, CompressiveEncoding)>> { + let DataBlock::FixedWidth(data) = data else { + return Ok(None); + }; + try_fixed_u8_rle_for_block(data, params) +} - // Try general compression (user-requested or automatic over MIN_BLOCK_SIZE_FOR_GENERAL_COMPRESSION) - if let Some((compressor, config)) = - try_general_compression(self.version, &field_params, data)? - { - let encoding = ProtobufUtils21::wrapped( - config, - ProtobufUtils21::flat(fixed_width.bits_per_value, None), - )?; - return Ok((compressor, encoding)); - } +/// Select variable-width RLE for block compression. +pub fn try_variable_rle_block( + data: &DataBlock, + params: &CompressionFieldParams, +) -> Result, CompressiveEncoding)>> { + let DataBlock::FixedWidth(data) = data else { + return Ok(None); + }; + try_variable_rle_for_block(data, params) +} - let encoder = Box::new(ValueEncoder::default()); - let encoding = ProtobufUtils21::flat(fixed_width.bits_per_value, None); - Ok((encoder, encoding)) - } - DataBlock::VariableWidth(variable_width) => { - // Try general compression - if let Some((compressor, config)) = - try_general_compression(self.version, &field_params, data)? - { - let encoding = ProtobufUtils21::wrapped( - config, - ProtobufUtils21::variable( - ProtobufUtils21::flat(variable_width.bits_per_offset as u64, None), - None, - ), - )?; - return Ok((compressor, encoding)); - } +/// Select block bitpacking for applicable fixed-width values. +pub fn try_bitpacking_block( + data: &DataBlock, +) -> Option<(Box, CompressiveEncoding)> { + let DataBlock::FixedWidth(data) = data else { + return None; + }; + try_bitpack_for_block(data) +} - let encoder = Box::new(VariableEncoder::default()); - let encoding = ProtobufUtils21::variable( - ProtobufUtils21::flat(variable_width.bits_per_offset as u64, None), - None, - ); - Ok((encoder, encoding)) - } - _ => unreachable!(), - } +/// Select explicitly requested or automatic general-purpose block compression. +pub fn try_general_block( + data: &DataBlock, + params: &CompressionFieldParams, +) -> Result, CompressiveEncoding)>> { + let Some((compressor, config)) = try_general_compression(params, data)? else { + return Ok(None); + }; + let inner = match data { + DataBlock::FixedWidth(data) => ProtobufUtils21::flat(data.bits_per_value, None), + DataBlock::VariableWidth(data) => ProtobufUtils21::variable( + ProtobufUtils21::flat(data.bits_per_offset as u64, None), + None, + ), + _ => return Ok(None), + }; + Ok(Some((compressor, ProtobufUtils21::wrapped(config, inner)?))) +} + +/// Store fixed- and variable-width block values without block compression. +pub fn try_raw_block(data: &DataBlock) -> Option<(Box, CompressiveEncoding)> { + match data { + DataBlock::FixedWidth(data) => Some(( + Box::new(ValueEncoder::default()) as Box, + ProtobufUtils21::flat(data.bits_per_value, None), + )), + DataBlock::VariableWidth(data) => Some(( + Box::new(VariableEncoder::default()) as Box, + ProtobufUtils21::variable( + ProtobufUtils21::flat(data.bits_per_offset as u64, None), + None, + ), + )), + _ => None, } } @@ -1366,12 +1353,21 @@ fn compression_name(compression: &Compression) -> &'static str { mod tests { use super::*; use crate::buffer::LanceBuffer; + use crate::compression_config::CompressionParams; use crate::data::{BlockInfo, DataBlock, FixedWidthDataBlock}; use crate::statistics::ComputeStat; - use crate::testing::extract_array_encoding_chain; + use crate::testing::{TestEncoding, extract_array_encoding_chain, test_compression_strategy}; use arrow_schema::{DataType, Field as ArrowField}; use std::collections::HashMap; + fn strategy(encoding: TestEncoding, params: CompressionParams) -> Arc { + test_compression_strategy(encoding, params) + } + + fn baseline_strategy(params: CompressionParams) -> Arc { + strategy(TestEncoding::StructuralU16, params) + } + fn create_test_field(name: &str, data_type: DataType) -> Field { let arrow_field = ArrowField::new(name, data_type, true); let mut field = Field::try_from(&arrow_field).unwrap(); @@ -1560,7 +1556,7 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); let field = create_test_field("user_id", DataType::Int32); // Create data with low run count for RLE @@ -1592,7 +1588,7 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); let field = create_test_field("some_column", DataType::Int32); // Create data with very low run count (50 runs for 1000 values = 0.05 ratio) let data = create_fixed_width_block_with_stats(32, 1000, 50); @@ -1607,7 +1603,7 @@ mod tests { #[test] #[cfg(feature = "bitpacking")] fn test_block_bitpacks_with_zero_segment() { - let strategy = DefaultCompressionStrategy::new(); + let strategy = baseline_strategy(CompressionParams::default()); let field = create_test_field("levels", DataType::UInt16); // First 1024 zeros, then 1024 ones; max bit width is 1. @@ -1632,7 +1628,7 @@ mod tests { #[test] fn test_rle_block_accounts_for_header_before_selecting() { - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let field = create_test_field("small_constant", DataType::Int32); let values = vec![42i32; 2]; let mut block = FixedWidthDataBlock { @@ -1656,7 +1652,7 @@ mod tests { #[test] #[cfg(feature = "bitpacking")] fn test_rle_block_prefers_bitpacking_when_smaller() { - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let field = create_test_field("levels", DataType::UInt16); let mut values = Vec::with_capacity(2048); @@ -1687,7 +1683,7 @@ mod tests { #[test] #[cfg(feature = "bitpacking")] fn test_low_cardinality_prefers_bitpacking_over_rle() { - let strategy = DefaultCompressionStrategy::new(); + let strategy = baseline_strategy(CompressionParams::default()); let field = create_test_field("int_score", DataType::Int64); // Low cardinality values (3/4/5) but with moderate run count: @@ -1750,7 +1746,7 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); let field = create_test_field("embeddings", DataType::Float32); let fixed_data = create_fixed_width_block(32, 1000); let variable_data = create_variable_width_block(32, 10, 32 * 1024); @@ -1785,7 +1781,7 @@ mod tests { arrow_field = arrow_field.with_metadata(metadata); let field = Field::try_from(&arrow_field).unwrap(); - let strategy = DefaultCompressionStrategy::with_params(CompressionParams::new()); + let strategy = baseline_strategy(CompressionParams::new()); // Test miniblock let fixed_data = create_fixed_width_block(32, 1000); @@ -1815,7 +1811,7 @@ mod tests { #[test] fn test_auto_fsst_disabled_for_binary_fields() { - let strategy = DefaultCompressionStrategy::new(); + let strategy = baseline_strategy(CompressionParams::default()); let field = create_test_field("bytes", DataType::Binary); let variable_data = create_fsst_candidate_variable_width_block(); @@ -1846,7 +1842,7 @@ mod tests { #[test] fn test_auto_fsst_still_enabled_for_utf8_fields() { - let strategy = DefaultCompressionStrategy::new(); + let strategy = baseline_strategy(CompressionParams::default()); let field = create_test_field("text", DataType::Utf8); let variable_data = create_fsst_candidate_variable_width_block(); @@ -1878,7 +1874,7 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); let field = create_test_field("bytes", DataType::Binary); let variable_data = create_fsst_candidate_variable_width_block(); @@ -1911,7 +1907,7 @@ mod tests { ..Default::default() }, ); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); let field = create_test_field("html", DataType::Utf8); let large = create_variable_width_block(32, 64, 40 * 1024); @@ -1949,12 +1945,8 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); - // Get merged params - let merged = strategy - .params - .get_field_params("user_id", &DataType::Int32); + let merged = params.get_field_params("user_id", &DataType::Int32); // Column params should override type params assert_eq!(merged.rle_threshold, Some(0.2)); @@ -1962,9 +1954,7 @@ mod tests { assert_eq!(merged.compression_level, Some(6)); // Test field with only type params - let merged = strategy - .params - .get_field_params("other_field", &DataType::Int32); + let merged = params.get_field_params("other_field", &DataType::Int32); assert_eq!(merged.rle_threshold, Some(0.5)); assert_eq!(merged.compression, Some("lz4".to_string())); assert_eq!(merged.compression_level, None); @@ -1984,26 +1974,20 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); - // Should match pattern - let merged = strategy - .params - .get_field_params("log_messages", &DataType::Utf8); + let merged = params.get_field_params("log_messages", &DataType::Utf8); assert_eq!(merged.compression, Some("zstd".to_string())); assert_eq!(merged.compression_level, Some(6)); // Should not match - let merged = strategy - .params - .get_field_params("messages_log", &DataType::Utf8); + let merged = params.get_field_params("messages_log", &DataType::Utf8); assert_eq!(merged.compression, None); } #[test] fn test_legacy_metadata_support() { let params = CompressionParams::new(); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); // Test field with "none" compression metadata let mut metadata = HashMap::new(); @@ -2022,7 +2006,7 @@ mod tests { fn test_default_behavior() { // Empty params should fall back to default behavior let params = CompressionParams::new(); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); let field = create_test_field("random_column", DataType::Int32); // Create data with high run count that won't trigger RLE (600 runs for 1000 values = 0.6 ratio) @@ -2037,7 +2021,7 @@ mod tests { #[test] fn test_field_metadata_compression() { let params = CompressionParams::new(); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); // Test field with compression metadata let mut metadata = HashMap::new(); @@ -2057,7 +2041,7 @@ mod tests { #[test] fn test_field_metadata_rle_threshold() { let params = CompressionParams::new(); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); // Test field with RLE threshold metadata let mut metadata = HashMap::new(); @@ -2095,7 +2079,7 @@ mod tests { data.compute_stat(); let data = DataBlock::FixedWidth(data); - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let compressor = strategy.create_miniblock_compressor(&field, &data).unwrap(); let (_compressed, encoding) = compressor.compress(data).unwrap(); assert_eq!(rle_run_length_bits(&encoding), 16); @@ -2103,7 +2087,7 @@ mod tests { #[test] fn test_rle_v2_miniblock_keeps_u8_run_lengths_before_v2_3() { - for version in [LanceFileVersion::V2_1, LanceFileVersion::V2_2] { + for version in [TestEncoding::StructuralU16, TestEncoding::StructuralU32] { let mut metadata = HashMap::new(); metadata.insert(RLE_THRESHOLD_META_KEY.to_string(), "1.0".to_string()); metadata.insert(BSS_META_KEY.to_string(), "off".to_string()); @@ -2120,7 +2104,7 @@ mod tests { data.compute_stat(); let data = DataBlock::FixedWidth(data); - let strategy = DefaultCompressionStrategy::new().with_version(version); + let strategy = strategy(version, CompressionParams::default()); let compressor = strategy.create_miniblock_compressor(&field, &data).unwrap(); let (_compressed, encoding) = compressor.compress(data).unwrap(); assert_eq!(rle_run_length_bits(&encoding), 8, "version={version}"); @@ -2145,7 +2129,7 @@ mod tests { data.compute_stat(); let data = DataBlock::FixedWidth(data); - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let compressor = strategy.create_miniblock_compressor(&field, &data).unwrap(); let debug_str = format!("{compressor:?}"); assert!(debug_str.contains("RleEncoder")); @@ -2171,7 +2155,7 @@ mod tests { data.compute_stat(); let data = DataBlock::FixedWidth(data); - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let compressor = strategy.create_miniblock_compressor(&field, &data).unwrap(); let (_compressed, encoding) = compressor.compress(data).unwrap(); assert_eq!(rle_run_length_bits(&encoding), 16); @@ -2194,7 +2178,7 @@ mod tests { data.compute_stat(); let data = DataBlock::FixedWidth(data); - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let compressor = strategy.create_miniblock_compressor(&field, &data).unwrap(); let (_compressed, encoding) = compressor.compress(data).unwrap(); assert_eq!(rle_run_length_bits(&encoding), 8); @@ -2203,7 +2187,7 @@ mod tests { #[test] #[cfg(any(feature = "lz4", feature = "zstd"))] fn test_rle_miniblock_released_versions_keep_flat_children_when_compression_requested() { - for version in [LanceFileVersion::V2_1, LanceFileVersion::V2_2] { + for version in [TestEncoding::StructuralU16, TestEncoding::StructuralU32] { let mut params = CompressionParams::new(); params.columns.insert( "dict_indices".to_string(), @@ -2216,7 +2200,7 @@ mod tests { ..Default::default() }, ); - let strategy = DefaultCompressionStrategy::with_params(params).with_version(version); + let strategy = strategy(version, params); let field = create_test_field("dict_indices", DataType::UInt32); let mut values = Vec::with_capacity(8192 * 4); @@ -2276,7 +2260,7 @@ mod tests { data.compute_stat(); let data = DataBlock::FixedWidth(data); - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let compressor = strategy.create_miniblock_compressor(&field, &data).unwrap(); let debug_str = format!("{compressor:?}"); assert!(debug_str.contains("RleEncoder")); @@ -2323,7 +2307,7 @@ mod tests { data.compute_stat(); let data = DataBlock::FixedWidth(data); - let strategy = DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::default()); let compressor = strategy.create_miniblock_compressor(&field, &data).unwrap(); let debug_str = format!("{compressor:?}"); assert!( @@ -2363,7 +2347,7 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); // Field metadata should override params let mut metadata = HashMap::new(); @@ -2391,7 +2375,7 @@ mod tests { }, ); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); // Field metadata provides partial override let mut metadata = HashMap::new(); @@ -2410,7 +2394,7 @@ mod tests { #[test] fn test_bss_field_metadata() { let params = CompressionParams::new(); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); // Test BSS "on" mode with compression enabled (BSS requires compression to be effective) let mut metadata = HashMap::new(); @@ -2431,7 +2415,7 @@ mod tests { #[test] fn test_bss_with_compression() { let params = CompressionParams::new(); - let strategy = DefaultCompressionStrategy::with_params(params); + let strategy = baseline_strategy(params); // Test BSS with LZ4 compression let mut metadata = HashMap::new(); @@ -2464,8 +2448,7 @@ mod tests { }, ); - let mut strategy = DefaultCompressionStrategy::with_params(params); - strategy.version = LanceFileVersion::V2_2; + let strategy = strategy(TestEncoding::StructuralU32, params); let field = create_test_field("dict_values", DataType::FixedSizeBinary(3)); let data = create_fixed_width_block(24, 1024); @@ -2518,8 +2501,7 @@ mod tests { }, ); - let strategy = - DefaultCompressionStrategy::with_params(params).with_version(LanceFileVersion::V2_1); + let strategy = strategy(TestEncoding::StructuralU16, params); let field = create_test_field("dict_values", DataType::FixedSizeBinary(3)); let data = create_fixed_width_block(24, 1024); @@ -2544,8 +2526,7 @@ mod tests { }, ); - let strategy = - DefaultCompressionStrategy::with_params(params).with_version(LanceFileVersion::V2_2); + let strategy = strategy(TestEncoding::StructuralU32, params); let field = create_test_field("dict_values", DataType::FixedSizeBinary(3)); let data = create_fixed_width_block(24, 20_000); @@ -2577,8 +2558,7 @@ mod tests { block.compute_stat(); let data = DataBlock::FixedWidth(block); - let strategy = DefaultCompressionStrategy::with_params(CompressionParams::new()) - .with_version(LanceFileVersion::V2_3); + let strategy = strategy(TestEncoding::StructuralSparse, CompressionParams::new()); let (compressor, encoding) = strategy.create_block_compressor(&field, &data).unwrap(); assert_eq!(rle_run_length_bits(&encoding), 32); @@ -2612,8 +2592,7 @@ mod tests { block.compute_stat(); let data = DataBlock::FixedWidth(block); - let strategy = DefaultCompressionStrategy::with_params(CompressionParams::new()) - .with_version(LanceFileVersion::V2_2); + let strategy = strategy(TestEncoding::StructuralU32, CompressionParams::new()); let (_compressor, encoding) = strategy.create_block_compressor(&field, &data).unwrap(); assert_eq!(rle_run_length_bits(&encoding), 8); } @@ -2642,8 +2621,7 @@ mod tests { let data_block = DataBlock::FixedWidth(block); - let strategy = DefaultCompressionStrategy::with_params(CompressionParams::new()) - .with_version(LanceFileVersion::V2_2); + let strategy = strategy(TestEncoding::StructuralU32, CompressionParams::new()); let (compressor, _) = strategy .create_block_compressor(&field, &data_block) @@ -2677,8 +2655,7 @@ mod tests { let data_block = DataBlock::FixedWidth(block); - let strategy = DefaultCompressionStrategy::with_params(CompressionParams::new()) - .with_version(LanceFileVersion::V2_1); + let strategy = strategy(TestEncoding::StructuralU16, CompressionParams::new()); let (compressor, _) = strategy .create_block_compressor(&field, &data_block) diff --git a/rust/lance-encoding/src/decoder.rs b/rust/lance-encoding/src/decoder.rs index 108a848db64..dca8fe49dd0 100644 --- a/rust/lance-encoding/src/decoder.rs +++ b/rust/lance-encoding/src/decoder.rs @@ -240,6 +240,12 @@ use lance_core::error::LanceOptionExt; use lance_core::{ArrowResult, Error, Result}; use tracing::instrument; +use crate::array_encoding::logical::list::OffsetPageInfo; +use crate::array_encoding::logical::r#struct::{SimpleStructDecoder, SimpleStructScheduler}; +use crate::array_encoding::logical::{ + binary::BinaryFieldScheduler, blob::BlobFieldScheduler, list::ListFieldScheduler, + primitive::PrimitiveFieldScheduler, +}; use crate::compression::{DecompressionStrategy, DefaultDecompressionStrategy}; use crate::data::DataBlock; use crate::encoder::EncodedBatch; @@ -250,17 +256,72 @@ use crate::encodings::logical::primitive::StructuralPrimitiveFieldScheduler; use crate::encodings::logical::r#struct::{StructuralStructDecoder, StructuralStructScheduler}; use crate::format::pb::{self, column_encoding}; use crate::format::pb21; -use crate::previous::decoder::LogicalPageDecoder; -use crate::previous::encodings::logical::list::OffsetPageInfo; -use crate::previous::encodings::logical::r#struct::{SimpleStructDecoder, SimpleStructScheduler}; -use crate::previous::encodings::logical::{ - binary::BinaryFieldScheduler, blob::BlobFieldScheduler, list::ListFieldScheduler, - primitive::PrimitiveFieldScheduler, -}; use crate::repdef::{CompositeRepDefUnraveler, RepDefUnraveler}; -use crate::version::LanceFileVersion; use crate::{BufferScheduler, EncodingsIo}; +pub trait SchedulingJob: std::fmt::Debug { + fn schedule_next( + &mut self, + context: &mut SchedulerContext, + priority: &dyn PriorityRange, + ) -> Result; + + fn num_rows(&self) -> u64; +} + +/// Schedules the I/O needed to decode one field. +pub trait FieldScheduler: Send + Sync + std::fmt::Debug { + fn initialize<'a>( + &'a self, + filter: &'a FilterExpression, + context: &'a SchedulerContext, + ) -> BoxFuture<'a, Result<()>>; + + fn schedule_ranges<'a>( + &'a self, + ranges: &[Range], + filter: &FilterExpression, + ) -> Result>; + + fn num_rows(&self) -> u64; +} + +#[derive(Debug)] +pub struct DecoderReady { + pub decoder: Box, + pub path: VecDeque, +} + +/// Stateful decoder for one logical page. +pub trait LogicalPageDecoder: std::fmt::Debug + Send { + fn accept_child(&mut self, _child: DecoderReady) -> Result<()> { + Err(Error::internal(format!( + "The decoder {:?} does not expect children but received a child", + self + ))) + } + + fn wait_for_loaded(&'_ mut self, loaded_need: u64) -> BoxFuture<'_, Result<()>>; + + fn rows_loaded(&self) -> u64; + + fn rows_unloaded(&self) -> u64 { + self.num_rows() - self.rows_loaded() + } + + fn num_rows(&self) -> u64; + + fn rows_drained(&self) -> u64; + + fn rows_left(&self) -> u64 { + self.num_rows() - self.rows_drained() + } + + fn drain(&mut self, num_rows: u64) -> Result; + + fn data_type(&self) -> &DataType; +} + // If users are getting batches over 10MiB large then it's time to reduce the batch size const BATCH_SIZE_BYTES_WARNING: u64 = 10 * 1024 * 1024; const ENV_LANCE_STRUCTURAL_BATCH_DECODE_SPAWN_MODE: &str = @@ -288,39 +349,39 @@ fn inline_scheduling_threshold() -> u64 { }) } -/// Top-level encoding message for a page. Wraps both the -/// legacy pb::ArrayEncoding and the newer pb::PageLayout +/// Top-level encoding message for a page. Wraps both the v2.0 +/// [`pb::ArrayEncoding`] grammar and the structural [`pb21::PageLayout`] grammar. /// /// A file should only use one or the other and never both. /// 2.0 decoders can always assume this is pb::ArrayEncoding /// and 2.1+ decoders can always assume this is pb::PageLayout -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum PageEncoding { - Legacy(pb::ArrayEncoding), + Array(pb::ArrayEncoding), Structural(pb21::PageLayout), } impl DeepSizeOf for PageEncoding { fn deep_size_of_children(&self, _context: &mut Context) -> usize { match self { - Self::Legacy(encoding) => encoding.encoded_len() * 4, + Self::Array(encoding) => encoding.encoded_len() * 4, Self::Structural(encoding) => encoding.encoded_len() * 4, } } } impl PageEncoding { - pub fn as_legacy(&self) -> &pb::ArrayEncoding { + pub fn as_array(&self) -> &pb::ArrayEncoding { match self { - Self::Legacy(enc) => enc, - Self::Structural(_) => panic!("Expected a legacy encoding"), + Self::Array(enc) => enc, + Self::Structural(_) => panic!("Expected an array encoding"), } } pub fn as_structural(&self) -> &pb21::PageLayout { match self { Self::Structural(enc) => enc, - Self::Legacy(_) => panic!("Expected a structural encoding"), + Self::Array(_) => panic!("Expected a structural encoding"), } } @@ -402,21 +463,21 @@ impl ColumnInfo { enum RootScheduler { Structural(Box), - Legacy(Arc), + Array(Arc), } impl RootScheduler { - fn as_legacy(&self) -> &Arc { + fn as_array(&self) -> &Arc { match self { - Self::Structural(_) => panic!("Expected a legacy scheduler"), - Self::Legacy(s) => s, + Self::Structural(_) => panic!("Expected an array scheduler"), + Self::Array(s) => s, } } fn as_structural(&self) -> &dyn StructuralFieldScheduler { match self { Self::Structural(s) => s.as_ref(), - Self::Legacy(_) => panic!("Expected a structural scheduler"), + Self::Array(_) => panic!("Expected a structural scheduler"), } } } @@ -606,14 +667,14 @@ impl CoreFieldDecoderStrategy { } } - fn is_primitive_legacy(data_type: &DataType) -> bool { + fn is_array_primitive(data_type: &DataType) -> bool { if data_type.is_primitive() { true } else { match data_type { // DataType::is_primitive doesn't consider these primitive but we do DataType::Boolean | DataType::Null | DataType::FixedSizeBinary(_) => true, - DataType::FixedSizeList(inner, _) => Self::is_primitive_legacy(inner.data_type()), + DataType::FixedSizeList(inner, _) => Self::is_array_primitive(inner.data_type()), _ => false, } } @@ -624,7 +685,7 @@ impl CoreFieldDecoderStrategy { field: &Field, column: &ColumnInfo, buffers: FileBuffers, - ) -> Result> { + ) -> Result> { Self::ensure_values_encoded(column, &field.name)?; // Primitive fields map to a single column let column_buffers = ColumnBuffers { @@ -647,7 +708,7 @@ impl CoreFieldDecoderStrategy { return Err(Error::invalid_input_source(format!("Due to schema we expected a struct column but we received a column with {} pages and right now we only support struct columns with 1 page", column_info.page_infos.len()).into())); } let encoding = &column_info.page_infos[0].encoding; - match encoding.as_legacy().array_encoding.as_ref().unwrap() { + match encoding.as_array().array_encoding.as_ref().unwrap() { pb::array_encoding::ArrayEncoding::Struct(_) => Ok(()), _ => Err(Error::invalid_input_source(format!("Expected a struct encoding because we have a struct field in the schema but got the encoding {:?}", encoding).into())), } @@ -656,7 +717,7 @@ impl CoreFieldDecoderStrategy { fn check_packed_struct(column_info: &ColumnInfo) -> bool { let encoding = &column_info.page_infos[0].encoding; matches!( - encoding.as_legacy().array_encoding.as_ref().unwrap(), + encoding.as_array().array_encoding.as_ref().unwrap(), pb::array_encoding::ArrayEncoding::PackedStruct(_) ) } @@ -667,14 +728,14 @@ impl CoreFieldDecoderStrategy { column_infos: &mut ColumnInfoIter, buffers: FileBuffers, offsets_column: &ColumnInfo, - ) -> Result> { + ) -> Result> { Self::ensure_values_encoded(offsets_column, &list_field.name)?; let offsets_column_buffers = ColumnBuffers { file_buffers: buffers, positions_and_sizes: &offsets_column.buffer_offsets_and_sizes, }; let items_scheduler = - self.create_legacy_field_scheduler(&list_field.children[0], column_infos, buffers)?; + self.create_array_field_scheduler(&list_field.children[0], column_infos, buffers)?; let (inner_infos, null_offset_adjustments): (Vec<_>, Vec<_>) = offsets_column .page_infos @@ -682,11 +743,11 @@ impl CoreFieldDecoderStrategy { .filter(|offsets_page| offsets_page.num_rows > 0) .map(|offsets_page| { if let Some(pb::array_encoding::ArrayEncoding::List(list_encoding)) = - &offsets_page.encoding.as_legacy().array_encoding + &offsets_page.encoding.as_array().array_encoding { let inner = PageInfo { buffer_offsets_and_sizes: offsets_page.buffer_offsets_and_sizes.clone(), - encoding: PageEncoding::Legacy( + encoding: PageEncoding::Array( list_encoding.offsets.as_ref().unwrap().as_ref().clone(), ), num_rows: offsets_page.num_rows, @@ -712,7 +773,7 @@ impl CoreFieldDecoderStrategy { Arc::from(inner_infos.into_boxed_slice()), offsets_column_buffers, self.validate_data, - )) as Arc; + )) as Arc; let items_field = match list_field.data_type() { DataType::List(inner) => inner, DataType::LargeList(inner) => inner, @@ -853,15 +914,15 @@ impl CoreFieldDecoderStrategy { } } - fn create_legacy_field_scheduler( + fn create_array_field_scheduler( &self, field: &Field, column_infos: &mut ColumnInfoIter, buffers: FileBuffers, - ) -> Result> { + ) -> Result> { let data_type = field.data_type(); validate_fixed_size_list_dimensions(&field.name, &data_type)?; - if Self::is_primitive_legacy(&data_type) { + if Self::is_array_primitive(&data_type) { let column_info = column_infos.expect_next()?; let scheduler = self.create_primitive_scheduler(field, column_info, buffers)?; return Ok(scheduler); @@ -876,7 +937,7 @@ impl CoreFieldDecoderStrategy { } if let Some(page_info) = column_info.page_infos.first() { if matches!( - page_info.encoding.as_legacy(), + page_info.encoding.as_array(), pb::ArrayEncoding { array_encoding: Some(pb::array_encoding::ArrayEncoding::List(..)) } @@ -920,7 +981,7 @@ impl CoreFieldDecoderStrategy { DataType::FixedSizeList(inner, _dimension) => { // A fixed size list column could either be a physical or a logical decoder // depending on the child data type. - if Self::is_primitive_legacy(inner.data_type()) { + if Self::is_array_primitive(inner.data_type()) { let primitive_col = column_infos.expect_next()?; let scheduler = self.create_primitive_scheduler(field, primitive_col, buffers)?; @@ -930,7 +991,7 @@ impl CoreFieldDecoderStrategy { } } DataType::Dictionary(_key_type, value_type) => { - if Self::is_primitive_legacy(value_type) || value_type.is_binary_like() { + if Self::is_array_primitive(value_type) || value_type.is_binary_like() { let primitive_col = column_infos.expect_next()?; let scheduler = self.create_primitive_scheduler(field, primitive_col, buffers)?; @@ -974,7 +1035,7 @@ impl CoreFieldDecoderStrategy { for field in &field.children { column_infos.next_top_level(); let field_scheduler = - self.create_legacy_field_scheduler(field, column_infos, buffers)?; + self.create_array_field_scheduler(field, column_infos, buffers)?; child_schedulers.push(Arc::from(field_scheduler)); } @@ -1003,12 +1064,12 @@ fn root_column(num_rows: u64) -> ColumnInfo { } else { u64::MAX }, - encoding: PageEncoding::Legacy(pb::ArrayEncoding { + encoding: PageEncoding::Array(pb::ArrayEncoding { array_encoding: Some(pb::array_encoding::ArrayEncoding::Struct( pb::SimpleStruct {}, )), }), - priority: 0, // not used in legacy scheduler + priority: 0, // not used by the array scheduler buffer_offsets_and_sizes: Arc::new([]), }) .collect::>(); @@ -1024,21 +1085,21 @@ fn root_column(num_rows: u64) -> ColumnInfo { pub enum RootDecoder { Structural(StructuralStructDecoder), - Legacy(SimpleStructDecoder), + Array(SimpleStructDecoder), } impl RootDecoder { pub fn into_structural(self) -> StructuralStructDecoder { match self { Self::Structural(decoder) => decoder, - Self::Legacy(_) => panic!("Expected a structural decoder"), + Self::Array(_) => panic!("Expected a structural decoder"), } } - pub fn into_legacy(self) -> SimpleStructDecoder { + pub fn into_array(self) -> SimpleStructDecoder { match self { - Self::Legacy(decoder) => decoder, - Self::Structural(_) => panic!("Expected a legacy decoder"), + Self::Array(decoder) => decoder, + Self::Structural(_) => panic!("Expected an array decoder"), } } } @@ -1104,27 +1165,27 @@ impl DecodeBatchScheduler { let mut column_iter = ColumnInfoIter::new(columns, &adjusted_column_indices); let strategy = CoreFieldDecoderStrategy::from_decoder_config(decoder_config); let root_scheduler = - strategy.create_legacy_field_scheduler(&root_field, &mut column_iter, buffers)?; + strategy.create_array_field_scheduler(&root_field, &mut column_iter, buffers)?; let context = SchedulerContext::new(io, cache.clone()); root_scheduler.initialize(filter, &context).await?; Ok(Self { - root_scheduler: RootScheduler::Legacy(root_scheduler.into()), + root_scheduler: RootScheduler::Array(root_scheduler.into()), root_fields, cache, }) } } - #[deprecated(since = "0.29.1", note = "This is for legacy 2.0 paths")] + #[deprecated(since = "0.29.1", note = "This is for v2.0 array-encoding paths")] pub fn from_scheduler( - root_scheduler: Arc, + root_scheduler: Arc, root_fields: Fields, cache: Arc, ) -> Self { Self { - root_scheduler: RootScheduler::Legacy(root_scheduler), + root_scheduler: RootScheduler::Array(root_scheduler), root_fields, cache, } @@ -1174,7 +1235,7 @@ impl DecodeBatchScheduler { } } - fn do_schedule_ranges_legacy( + fn do_schedule_ranges_array( &mut self, ranges: &[Range], filter: &FilterExpression, @@ -1185,7 +1246,7 @@ impl DecodeBatchScheduler { // tasks are scheduled at the same top level row. priority: Option>, ) { - let root_scheduler = self.root_scheduler.as_legacy(); + let root_scheduler = self.root_scheduler.as_array(); let rows_requested = ranges.iter().map(|r| r.end - r.start).sum::(); trace!( "Scheduling {} ranges across {}..{} ({} rows){}", @@ -1249,8 +1310,8 @@ impl DecodeBatchScheduler { priority: Option>, ) { match &self.root_scheduler { - RootScheduler::Legacy(_) => { - self.do_schedule_ranges_legacy(ranges, filter, io, schedule_action, priority) + RootScheduler::Array(_) => { + self.do_schedule_ranges_array(ranges, filter, io, schedule_action, priority) } RootScheduler::Structural(_) => { self.do_schedule_ranges_structural(ranges, filter, io, schedule_action) @@ -1422,7 +1483,7 @@ impl BatchDecodeStream { } } - fn accept_decoder(&mut self, decoder: crate::previous::decoder::DecoderReady) -> Result<()> { + fn accept_decoder(&mut self, decoder: DecoderReady) -> Result<()> { if decoder.path.is_empty() { // The root decoder we can ignore Ok(()) @@ -1443,7 +1504,7 @@ impl BatchDecodeStream { let scan_line = scan_line?; self.rows_scheduled = scan_line.scheduled_so_far; for message in scan_line.decoders { - self.accept_decoder(message.into_legacy())?; + self.accept_decoder(message.into_array())?; } } None => { @@ -1552,7 +1613,7 @@ impl BatchDecodeStream { // we can have a single implementation of the batch decode iterator enum RootDecoderMessage { LoadedPage(LoadedPageShard), - LegacyPage(crate::previous::decoder::DecoderReady), + ArrayPage(DecoderReady), } trait RootDecoderType { fn accept_message(&mut self, message: RootDecoderMessage) -> Result<()>; @@ -1576,10 +1637,10 @@ impl RootDecoderType for StructuralStructDecoder { } impl RootDecoderType for SimpleStructDecoder { fn accept_message(&mut self, message: RootDecoderMessage) -> Result<()> { - let RootDecoderMessage::LegacyPage(legacy_page) = message else { + let RootDecoderMessage::ArrayPage(array_page) = message else { unreachable!() }; - self.accept_child(legacy_page) + self.accept_child(array_page) } fn drain_batch(&mut self, num_rows: u64) -> Result { self.drain(num_rows) @@ -1663,7 +1724,7 @@ impl BatchDecodeIterator { // The root decoder we can ignore if !decoder_ready.path.is_empty() { self.root_decoder - .accept_message(RootDecoderMessage::LegacyPage(decoder_ready))?; + .accept_message(RootDecoderMessage::ArrayPage(decoder_ready))?; } } } @@ -2631,17 +2692,14 @@ impl SchedulerContext { VecDeque::from_iter(self.path.iter().copied()) } - #[deprecated(since = "0.29.1", note = "This is for legacy 2.0 paths")] - pub fn locate_decoder( - &mut self, - decoder: Box, - ) -> crate::previous::decoder::DecoderReady { + #[deprecated(since = "0.29.1", note = "This is for v2.0 array-encoding paths")] + pub fn locate_decoder(&mut self, decoder: Box) -> DecoderReady { trace!( "Scheduling decoder of type {:?} for {:?}", decoder.data_type(), self.path, ); - crate::previous::decoder::DecoderReady { + DecoderReady { decoder, path: self.current_path(), } @@ -2762,7 +2820,7 @@ pub enum MessageType { // decoder itself. The messages were not sent in priority order and the decoder // had to wait for I/O, figuring out the correct priority. This was a lot of // complexity. - DecoderReady(crate::previous::decoder::DecoderReady), + DecoderReady(DecoderReady), // Starting in 2.1 we use a simpler scheme where the scheduling happens in priority // order and the message is an unloaded decoder. These can be awaited, in order, and // the decoder does not have to worry about waiting for I/O. @@ -2770,7 +2828,7 @@ pub enum MessageType { } impl MessageType { - pub fn into_legacy(self) -> crate::previous::decoder::DecoderReady { + pub fn into_array(self) -> DecoderReady { match self { Self::DecoderReady(decoder) => decoder, Self::UnloadedPage(_) => { @@ -2870,13 +2928,22 @@ pub trait StructuralFieldDecoder: std::fmt::Debug + Send { #[derive(Debug, Default)] pub struct DecoderPlugins {} +/// The top-level column layout used by an in-memory encoded batch. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EncodedBatchLayout { + /// Array pages include structural columns. + Array, + /// Structural pages include only leaf columns. + Structural, +} + /// Decodes a batch of data from an in-memory structure created by [`crate::encoder::encode_batch`] pub async fn decode_batch( batch: &EncodedBatch, filter: &FilterExpression, decoder_plugins: Arc, should_validate: bool, - version: LanceFileVersion, + layout: EncodedBatchLayout, cache: Option>, ) -> Result { // The io is synchronous so it shouldn't be possible for any async stuff to still be in progress @@ -2906,7 +2973,7 @@ pub async fn decode_batch( .await?; let (tx, rx) = unbounded_channel(); decode_scheduler.schedule_range(0..batch.num_rows, filter, tx, io_scheduler); - let is_structural = version >= LanceFileVersion::V2_1; + let is_structural = layout == EncodedBatchLayout::Structural; let mode = std::env::var(ENV_LANCE_STRUCTURAL_BATCH_DECODE_SPAWN_MODE); let spawn_structural_batch_decode_tasks = !matches!(mode.ok().as_deref(), Some("never")); let mut decode_stream = create_decode_stream( @@ -2926,7 +2993,6 @@ pub async fn decode_batch( // test coalesce indices to ranges mod tests { use super::*; - use crate::previous::decoder::{DecoderReady, LogicalPageDecoder}; use std::collections::VecDeque; #[derive(Debug)] @@ -3028,11 +3094,11 @@ mod tests { err ); - let mut legacy_columns = ColumnInfoIter::new(vec![], &[]); + let mut array_columns = ColumnInfoIter::new(vec![], &[]); let err = strategy - .create_legacy_field_scheduler( + .create_array_field_scheduler( &field, - &mut legacy_columns, + &mut array_columns, FileBuffers { positions_and_sizes: &[], }, @@ -3047,7 +3113,7 @@ mod tests { } #[tokio::test] - async fn test_legacy_stream_stops_on_load_error() { + async fn test_array_stream_stops_on_load_error() { use arrow_schema::Field as ArrowField; let rows_per_batch = 1; @@ -3081,7 +3147,7 @@ mod tests { let err = batches .next() .await - .expect("stream should emit the legacy page-load error") + .expect("stream should emit the array page-load error") .unwrap_err(); assert!( err.to_string().contains(load_error_message), @@ -3090,7 +3156,7 @@ mod tests { ); assert!( batches.next().await.is_none(), - "stream should stop after the legacy page-load error" + "stream should stop after the array page-load error" ); } @@ -3186,15 +3252,14 @@ mod tests { batch_size: u32, batch_size_bytes: Option, ) -> Vec { - use crate::encoder::{EncodingOptions, default_encoding_strategy, encode_batch}; - use crate::version::LanceFileVersion; - - let version = LanceFileVersion::V2_1; - let options = EncodingOptions { - version, - ..Default::default() + use crate::{ + encoder::{EncodingOptions, encode_batch}, + testing::{TestEncoding, test_encoding_strategy}, }; - let strategy = default_encoding_strategy(version); + + let version = TestEncoding::StructuralU16; + let options = EncodingOptions::default(); + let strategy = test_encoding_strategy(version); let schema = Schema::try_from(batch.schema().as_ref()).unwrap(); let encoded = encode_batch(batch, Arc::new(schema.clone()), strategy.as_ref(), &options) .await diff --git a/rust/lance-encoding/src/encoder.rs b/rust/lance-encoding/src/encoder.rs index cd0731fd06f..92fcb7f539c 100644 --- a/rust/lance-encoding/src/encoder.rs +++ b/rust/lance-encoding/src/encoder.rs @@ -6,44 +6,70 @@ //! Lance files are encoded using a [`FieldEncodingStrategy`] which choose //! what encoder to use for each field. //! -//! The current strategy is the [`StructuralEncodingStrategy`] which uses "structural" -//! encoding. A tree of encoders is built up for each field. The struct & list encoders -//! simply pull off the validity and offsets and collect them. Then, in the primitive leaf -//! encoder the validity, offsets, and values are accumulated in an accumulation buffer. Once -//! enough data has been collected the primitive encoder will either use a miniblock encoding -//! or a full zip encoding to create a page of data from the accumulation buffer. +//! Structural strategies build a tree of encoders for each field from the +//! version-free builders in [`structural`]. Struct and list encoders collect +//! validity and offsets; primitive leaf encoders accumulate values and emit +//! miniblock or full-zip pages. use std::{collections::HashMap, sync::Arc}; use arrow_array::{Array, ArrayRef, RecordBatch}; -use arrow_schema::DataType; use bytes::{Bytes, BytesMut}; use futures::future::BoxFuture; use lance_core::datatypes::{Field, Schema}; -use lance_core::error::LanceOptionExt; use lance_core::utils::bit::{is_pwr_two, pad_bytes_to}; use lance_core::{Error, Result}; use crate::buffer::LanceBuffer; -use crate::compression::{CompressionStrategy, DefaultCompressionStrategy}; -use crate::compression_config::CompressionParams; +use crate::data::DataBlock; use crate::decoder::PageEncoding; -use crate::encodings::logical::blob::{BlobStructuralEncoder, BlobV2StructuralEncoder}; -use crate::encodings::logical::fixed_size_list::FixedSizeListStructuralEncoder; -use crate::encodings::logical::list::ListStructuralEncoder; -use crate::encodings::logical::map::MapStructuralEncoder; -use crate::encodings::logical::primitive::PrimitiveStructuralEncoder; -use crate::encodings::logical::r#struct::StructStructuralEncoder; use crate::repdef::RepDefBuilder; -use crate::version::LanceFileVersion; use crate::{ decoder::{ColumnInfo, PageInfo}, format::pb, }; +pub mod structural; + /// The minimum alignment for a page buffer. Writers must respect this. pub const MIN_PAGE_BUFFER_ALIGNMENT: u64 = 8; +/// An array encoded with the `pb::ArrayEncoding` grammar. +#[derive(Debug)] +pub struct EncodedArray { + pub data: DataBlock, + pub encoding: pb::ArrayEncoding, +} + +impl EncodedArray { + pub fn new(data: DataBlock, encoding: pb::ArrayEncoding) -> Self { + Self { data, encoding } + } + + pub fn into_buffers(self) -> (Vec, pb::ArrayEncoding) { + (self.data.into_buffers(), self.encoding) + } +} + +/// Encodes one data block and describes it with `pb::ArrayEncoding`. +pub trait ArrayEncoder: std::fmt::Debug + Send + Sync { + fn encode( + &self, + data: DataBlock, + data_type: &arrow_schema::DataType, + buffer_index: &mut u32, + ) -> Result; +} + +/// Selects an `ArrayEncoder` for one page. +pub trait ArrayEncodingStrategy: Send + Sync + std::fmt::Debug { + fn create_array_encoder( + &self, + arrays: &[ArrayRef], + field: &Field, + ) -> Result>; +} + /// An encoded page of data /// /// Maps to a top-level array @@ -235,9 +261,6 @@ pub struct EncodingOptions { /// The encoder needs to know this so it figures the position of out-of-line /// buffers correctly pub buffer_alignment: u64, - - /// The Lance file version being written - pub version: LanceFileVersion, } impl Default for EncodingOptions { @@ -247,20 +270,10 @@ impl Default for EncodingOptions { max_page_bytes: 32 * 1024 * 1024, keep_original_array: true, buffer_alignment: 64, - version: LanceFileVersion::default(), } } } -impl EncodingOptions { - /// If true (for Lance file version 2.2+), miniblock chunk sizes are u32, - /// to allow storing larger chunks and their sizes for better compression. - /// For Lance file version 2.1, miniblock chunk sizes are u16. - pub fn support_large_chunk(&self) -> bool { - self.version >= LanceFileVersion::V2_2 - } -} - /// A trait to pick which kind of field encoding to use for a field /// /// Unlike the ArrayEncodingStrategy, the field encoding strategy is @@ -273,324 +286,23 @@ pub trait FieldEncodingStrategy: Send + Sync + std::fmt::Debug { /// The field encoder can be chosen on the data type as well as /// any metadata that is attached to the field. /// - /// The `encoding_strategy_root` is the encoder that should be - /// used to encode any inner data in struct / list / etc. fields. - /// - /// Initially it is the same as `self` and generally should be - /// forwarded to any inner encoding strategy. fn create_field_encoder( &self, - encoding_strategy_root: &dyn FieldEncodingStrategy, field: &Field, column_index: &mut ColumnIndexSequence, - options: &EncodingOptions, + context: &FieldEncodingContext<'_>, ) -> Result>; } -pub fn default_encoding_strategy(version: LanceFileVersion) -> Box { - match version.resolve() { - LanceFileVersion::Legacy => panic!(), - LanceFileVersion::V2_0 => Box::new( - crate::previous::encoder::CoreFieldEncodingStrategy::new(version), - ), - _ => Box::new(StructuralEncodingStrategy::with_version(version)), - } -} - -/// Create an encoding strategy with user-configured compression parameters -pub fn default_encoding_strategy_with_params( - version: LanceFileVersion, - params: CompressionParams, -) -> Result> { - match version.resolve() { - LanceFileVersion::Legacy | LanceFileVersion::V2_0 => Err(Error::invalid_input( - "Compression parameters are only supported in Lance file version 2.1 and later", - )), - _ => { - let compression_strategy = - Arc::new(DefaultCompressionStrategy::with_params(params).with_version(version)); - Ok(Box::new(StructuralEncodingStrategy { - compression_strategy, - version, - })) - } - } -} - -/// An encoding strategy used for 2.1+ files -#[derive(Debug)] -pub struct StructuralEncodingStrategy { - pub compression_strategy: Arc, - pub version: LanceFileVersion, -} - -// For some reason, clippy thinks we can add Default to the above derive but -// rustc doesn't agree (no default for Arc) -#[allow(clippy::derivable_impls)] -impl Default for StructuralEncodingStrategy { - fn default() -> Self { - Self { - compression_strategy: Arc::new(DefaultCompressionStrategy::new()), - version: LanceFileVersion::default(), - } - } -} - -impl StructuralEncodingStrategy { - pub fn with_version(version: LanceFileVersion) -> Self { - Self { - compression_strategy: Arc::new(DefaultCompressionStrategy::new().with_version(version)), - version, - } - } - - fn is_primitive_type(data_type: &DataType) -> bool { - match data_type { - DataType::FixedSizeList(inner, _) => Self::is_primitive_type(inner.data_type()), - _ => matches!( - data_type, - DataType::Boolean - | DataType::Date32 - | DataType::Date64 - | DataType::Decimal128(_, _) - | DataType::Decimal256(_, _) - | DataType::Duration(_) - | DataType::Float16 - | DataType::Float32 - | DataType::Float64 - | DataType::Int16 - | DataType::Int32 - | DataType::Int64 - | DataType::Int8 - | DataType::Interval(_) - | DataType::Null - | DataType::Time32(_) - | DataType::Time64(_) - | DataType::Timestamp(_, _) - | DataType::UInt16 - | DataType::UInt32 - | DataType::UInt64 - | DataType::UInt8 - | DataType::FixedSizeBinary(_) - | DataType::Binary - | DataType::LargeBinary - | DataType::Utf8 - | DataType::LargeUtf8, - ), - } - } - - fn do_create_field_encoder( - &self, - _encoding_strategy_root: &dyn FieldEncodingStrategy, - field: &Field, - column_index: &mut ColumnIndexSequence, - options: &EncodingOptions, - root_field_metadata: &HashMap, - ) -> Result> { - let data_type = field.data_type(); - - // Check if field is marked as blob - if field.is_blob() { - match data_type { - DataType::Binary | DataType::LargeBinary => { - return Ok(Box::new(BlobStructuralEncoder::new( - field, - column_index.next_column_index(field.id as u32), - options, - self.compression_strategy.clone(), - )?)); - } - DataType::Struct(_) if self.version >= LanceFileVersion::V2_2 => { - return Ok(Box::new(BlobV2StructuralEncoder::new( - field, - column_index.next_column_index(field.id as u32), - options, - self.compression_strategy.clone(), - )?)); - } - DataType::Struct(_) => { - return Err(Error::invalid_input_source( - "Blob v2 struct input requires file version >= 2.2".into(), - )); - } - _ => { - return Err(Error::invalid_input_source( - format!( - "Blob encoding only supports Binary/LargeBinary or v2 Struct, got {}", - data_type - ) - .into(), - )); - } - } - } - - if Self::is_primitive_type(&data_type) { - Ok(Box::new(PrimitiveStructuralEncoder::try_new( - options, - self.compression_strategy.clone(), - column_index.next_column_index(field.id as u32), - field.clone(), - Arc::new(root_field_metadata.clone()), - )?)) - } else { - match data_type { - DataType::List(_) | DataType::LargeList(_) => { - let child = field.children.first().expect_ok()?; - let child_encoder = self.do_create_field_encoder( - _encoding_strategy_root, - child, - column_index, - options, - root_field_metadata, - )?; - Ok(Box::new(ListStructuralEncoder::new( - options.keep_original_array, - child_encoder, - ))) - } - DataType::FixedSizeList(inner, _) - if matches!(inner.data_type(), DataType::Struct(_)) => - { - if self.version < LanceFileVersion::V2_2 { - return Err(Error::not_supported_source(format!( - "FixedSizeList is only supported in Lance file format 2.2+, current version: {}", - self.version - ) - .into())); - } - // Complex FixedSizeList needs structural encoding - let child = field.children.first().expect_ok()?; - let child_encoder = self.do_create_field_encoder( - _encoding_strategy_root, - child, - column_index, - options, - root_field_metadata, - )?; - Ok(Box::new(FixedSizeListStructuralEncoder::new( - options.keep_original_array, - child_encoder, - ))) - } - DataType::Map(_, keys_sorted) => { - // TODO: We only support keys_sorted=false for now, - // because converting a rust arrow map field to the python arrow field will - // lose the keys_sorted property. - if keys_sorted { - return Err(Error::not_supported_source(format!("Map data type is not supported with keys_sorted=true now, current value is {}", keys_sorted).into())); - } - if self.version < LanceFileVersion::V2_2 { - return Err(Error::not_supported_source(format!( - "Map data type is only supported in Lance file format 2.2+, current version: {}", - self.version - ) - .into())); - } - let entries_child = field.children.first().ok_or_else(|| { - Error::schema("Map should have an entries child".to_string()) - })?; - let DataType::Struct(struct_fields) = entries_child.data_type() else { - return Err(Error::schema( - "Map entries field must be a Struct".to_string(), - )); - }; - if struct_fields.len() < 2 { - return Err(Error::schema( - "Map entries struct must contain both key and value fields".to_string(), - )); - } - let key_field = &struct_fields[0]; - if key_field.is_nullable() { - return Err(Error::schema(format!( - "Map key field '{}' must be non-nullable according to Arrow Map specification", - key_field.name() - ))); - } - let child_encoder = self.do_create_field_encoder( - _encoding_strategy_root, - entries_child, - column_index, - options, - root_field_metadata, - )?; - Ok(Box::new(MapStructuralEncoder::new( - options.keep_original_array, - child_encoder, - ))) - } - DataType::Struct(fields) => { - if field.is_packed_struct() || fields.is_empty() { - // Both packed structs and empty structs are encoded as primitive - Ok(Box::new(PrimitiveStructuralEncoder::try_new( - options, - self.compression_strategy.clone(), - column_index.next_column_index(field.id as u32), - field.clone(), - Arc::new(root_field_metadata.clone()), - )?)) - } else { - let children_encoders = field - .children - .iter() - .map(|field| { - self.do_create_field_encoder( - _encoding_strategy_root, - field, - column_index, - options, - root_field_metadata, - ) - }) - .collect::>>()?; - Ok(Box::new(StructStructuralEncoder::new( - options.keep_original_array, - children_encoders, - ))) - } - } - DataType::Dictionary(_, value_type) => { - // A dictionary of primitive is, itself, primitive - if Self::is_primitive_type(&value_type) { - Ok(Box::new(PrimitiveStructuralEncoder::try_new( - options, - self.compression_strategy.clone(), - column_index.next_column_index(field.id as u32), - field.clone(), - Arc::new(root_field_metadata.clone()), - )?)) - } else { - // A dictionary of logical is, itself, logical and we don't support that today - // It could be possible (e.g. store indices in one column and values in remaining columns) - // but would be a significant amount of work - // - // An easier fallback implementation would be to decode-on-write and encode-on-read - Err(Error::not_supported_source(format!("cannot encode a dictionary column whose value type is a logical type ({})", value_type).into())) - } - } - _ => todo!("Implement encoding for field {}", field), - } - } - } -} - -impl FieldEncodingStrategy for StructuralEncodingStrategy { - fn create_field_encoder( - &self, - encoding_strategy_root: &dyn FieldEncodingStrategy, - field: &Field, - column_index: &mut ColumnIndexSequence, - options: &EncodingOptions, - ) -> Result> { - self.do_create_field_encoder( - encoding_strategy_root, - field, - column_index, - options, - &field.metadata, - ) - } +/// Context shared while one top-level field and all of its children are mapped +/// to concrete field encoders. +pub struct FieldEncodingContext<'a> { + /// The complete strategy composition used for recursive child fields. + pub strategy: &'a dyn FieldEncodingStrategy, + /// Runtime-only writer options. + pub options: &'a EncodingOptions, + /// Metadata inherited from the top-level field. + pub root_field_metadata: &'a HashMap, } /// A batch encoder that encodes RecordBatch objects by delegating @@ -612,12 +324,13 @@ impl BatchEncoder { .fields .iter() .map(|field| { - let encoder = strategy.create_field_encoder( + let context = FieldEncodingContext { strategy, - field, - &mut col_idx_sequence, options, - )?; + root_field_metadata: &field.metadata, + }; + let encoder = + strategy.create_field_encoder(field, &mut col_idx_sequence, &context)?; col_idx += encoder.as_ref().num_columns(); Ok(encoder) }) @@ -768,50 +481,9 @@ pub async fn encode_batch( #[cfg(test)] mod tests { use super::*; - use crate::compression_config::{CompressionFieldParams, CompressionParams}; + use crate::testing::{TestEncoding, create_test_field_encoder, test_encoding_strategy}; use arrow_schema::{DataType as ArrowDataType, Field as ArrowField, Fields as ArrowFields}; - #[test] - fn test_configured_encoding_strategy() { - // Create test parameters - let mut params = CompressionParams::new(); - params.columns.insert( - "*_id".to_string(), - CompressionFieldParams { - rle_threshold: Some(0.5), - compression: Some("lz4".to_string()), - compression_level: None, - bss: None, - minichunk_size: None, - }, - ); - - // Test with V2.1 - should succeed - let strategy = - default_encoding_strategy_with_params(LanceFileVersion::V2_1, params.clone()) - .expect("Should succeed for V2.1"); - - // Verify it's a StructuralEncodingStrategy - assert!(format!("{:?}", strategy).contains("StructuralEncodingStrategy")); - assert!(format!("{:?}", strategy).contains("DefaultCompressionStrategy")); - - // Test with V2.0 - should fail - let err = default_encoding_strategy_with_params(LanceFileVersion::V2_0, params.clone()) - .expect_err("Should fail for V2.0"); - assert!( - err.to_string() - .contains("only supported in Lance file version 2.1") - ); - - // Test with Legacy - should fail - let err = default_encoding_strategy_with_params(LanceFileVersion::Legacy, params) - .expect_err("Should fail for Legacy"); - assert!( - err.to_string() - .contains("only supported in Lance file version 2.1") - ); - } - #[test] fn test_fixed_size_list_struct_requires_v2_2() { let list_item = ArrowField::new( @@ -830,11 +502,12 @@ mod tests { ); let field = Field::try_from(&arrow_field).unwrap(); - let strategy = StructuralEncodingStrategy::with_version(LanceFileVersion::V2_1); + let strategy = test_encoding_strategy(TestEncoding::StructuralU16); let mut column_index = ColumnIndexSequence::default(); let options = EncodingOptions::default(); - let result = strategy.create_field_encoder(&strategy, &field, &mut column_index, &options); + let result = + create_test_field_encoder(strategy.as_ref(), &field, &mut column_index, &options); assert!( result.is_err(), "FixedSizeList should be rejected for file version 2.1" @@ -843,7 +516,7 @@ mod tests { assert!( err.to_string() - .contains("FixedSizeList is only supported in Lance file format 2.2+") + .contains("FixedSizeList is not enabled by the selected file format") ); } } diff --git a/rust/lance-encoding/src/encoder/structural.rs b/rust/lance-encoding/src/encoder/structural.rs new file mode 100644 index 00000000000..3d4e49f42c8 --- /dev/null +++ b/rust/lance-encoding/src/encoder/structural.rs @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +//! Version-free structural field encoder builders. + +use std::sync::Arc; + +use arrow_schema::DataType; +use lance_core::{Error, Result, datatypes::Field, error::LanceOptionExt}; + +pub use crate::encodings::logical::primitive::PrimitivePageEncoding; + +use crate::encodings::logical::{ + blob::{BlobStructuralEncoder, BlobV2StructuralEncoder}, + fixed_size_list::FixedSizeListStructuralEncoder, + list::ListStructuralEncoder, + map::MapStructuralEncoder, + primitive::PrimitiveStructuralEncoder, + r#struct::StructStructuralEncoder, +}; + +use super::{ColumnIndexSequence, FieldEncoder, FieldEncodingContext}; + +/// Encode primitive leaves, primitive fixed-size lists, dictionaries, and +/// packed or empty structs using one concrete primitive page grammar. +#[derive(Debug, Clone)] +pub struct PrimitiveFieldEncoding { + page_encodings: Arc<[PrimitivePageEncoding]>, +} + +impl PrimitiveFieldEncoding { + /// Create a primitive field mechanism from ordered executable page behaviors. + pub fn new(page_encodings: impl IntoIterator) -> Self { + Self { + page_encodings: page_encodings.into_iter().collect(), + } + } + + fn is_primitive_type(data_type: &DataType) -> bool { + match data_type { + DataType::FixedSizeList(inner, _) => Self::is_primitive_type(inner.data_type()), + _ => matches!( + data_type, + DataType::Boolean + | DataType::Date32 + | DataType::Date64 + | DataType::Decimal128(_, _) + | DataType::Decimal256(_, _) + | DataType::Duration(_) + | DataType::Float16 + | DataType::Float32 + | DataType::Float64 + | DataType::Int16 + | DataType::Int32 + | DataType::Int64 + | DataType::Int8 + | DataType::Interval(_) + | DataType::Null + | DataType::Time32(_) + | DataType::Time64(_) + | DataType::Timestamp(_, _) + | DataType::UInt16 + | DataType::UInt32 + | DataType::UInt64 + | DataType::UInt8 + | DataType::FixedSizeBinary(_) + | DataType::Binary + | DataType::LargeBinary + | DataType::Utf8 + | DataType::LargeUtf8, + ), + } + } + + fn create_at( + &self, + field: Field, + column_index: u32, + context: &FieldEncodingContext<'_>, + ) -> Result> { + Ok(Box::new(PrimitiveStructuralEncoder::try_new( + context.options, + self.page_encodings.clone(), + column_index, + field, + Arc::new(context.root_field_metadata.clone()), + )?)) + } + + /// Create a primitive field encoder when this mechanism recognizes `field`. + pub fn try_create( + &self, + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, + ) -> Result>> { + if field.is_blob() { + return Ok(None); + } + + let data_type = field.data_type(); + let is_primitive = Self::is_primitive_type(&data_type); + let is_packed_or_empty_struct = matches!( + &data_type, + DataType::Struct(fields) if field.is_packed_struct() || fields.is_empty() + ); + let is_primitive_dictionary = matches!( + &data_type, + DataType::Dictionary(_, value_type) if Self::is_primitive_type(value_type) + ); + + if !is_primitive && !is_packed_or_empty_struct && !is_primitive_dictionary { + if let DataType::Dictionary(_, value_type) = data_type { + return Err(Error::not_supported_source( + format!( + "cannot encode a dictionary column whose value type is a logical type ({})", + value_type + ) + .into(), + )); + } + return Ok(None); + } + + Ok(Some(self.create_at( + field.clone(), + column_index.next_column_index(field.id as u32), + context, + )?)) + } +} + +/// Create the original binary blob descriptor when `field` matches. +pub fn try_create_binary_blob( + primitive: &PrimitiveFieldEncoding, + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, +) -> Result>> { + if !field.is_blob() || !matches!(field.data_type(), DataType::Binary | DataType::LargeBinary) { + return Ok(None); + } + let descriptor_column_index = column_index.next_column_index(field.id as u32); + Ok(Some(Box::new(BlobStructuralEncoder::new( + field, + |descriptor_field| primitive.create_at(descriptor_field, descriptor_column_index, context), + )?))) +} + +/// Create the structural blob descriptor when `field` matches. +pub fn try_create_structural_blob( + primitive: &PrimitiveFieldEncoding, + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, +) -> Result>> { + if !field.is_blob() || !matches!(field.data_type(), DataType::Struct(_)) { + return Ok(None); + } + let descriptor_column_index = column_index.next_column_index(field.id as u32); + Ok(Some(Box::new(BlobV2StructuralEncoder::new( + field, + |descriptor_field| primitive.create_at(descriptor_field, descriptor_column_index, context), + )?))) +} + +/// Create a variable-size list encoder when `field` matches. +pub fn try_create_list( + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, +) -> Result>> { + if !matches!( + field.data_type(), + DataType::List(_) | DataType::LargeList(_) + ) { + return Ok(None); + } + let child = field.children.first().expect_ok()?; + let child_encoder = context + .strategy + .create_field_encoder(child, column_index, context)?; + Ok(Some(Box::new(ListStructuralEncoder::new( + context.options.keep_original_array, + child_encoder, + )))) +} + +/// Create a fixed-size-list encoder whose child is a struct when applicable. +pub fn try_create_structural_fixed_size_list( + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, +) -> Result>> { + if !matches!( + field.data_type(), + DataType::FixedSizeList(inner, _) if matches!(inner.data_type(), DataType::Struct(_)) + ) { + return Ok(None); + } + let child = field.children.first().expect_ok()?; + let child_encoder = context + .strategy + .create_field_encoder(child, column_index, context)?; + Ok(Some(Box::new(FixedSizeListStructuralEncoder::new( + context.options.keep_original_array, + child_encoder, + )))) +} + +/// Create an Arrow map encoder when `field` matches. +pub fn try_create_map( + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, +) -> Result>> { + let DataType::Map(_, keys_sorted) = field.data_type() else { + return Ok(None); + }; + if keys_sorted { + return Err(Error::not_supported_source( + format!( + "Map data type is not supported with keys_sorted=true now, current value is {}", + keys_sorted + ) + .into(), + )); + } + let entries_child = field + .children + .first() + .ok_or_else(|| Error::schema("Map should have an entries child".to_string()))?; + let DataType::Struct(struct_fields) = entries_child.data_type() else { + return Err(Error::schema( + "Map entries field must be a Struct".to_string(), + )); + }; + if struct_fields.len() < 2 { + return Err(Error::schema( + "Map entries struct must contain both key and value fields".to_string(), + )); + } + let key_field = &struct_fields[0]; + if key_field.is_nullable() { + return Err(Error::schema(format!( + "Map key field '{}' must be non-nullable according to Arrow Map specification", + key_field.name() + ))); + } + let child_encoder = + context + .strategy + .create_field_encoder(entries_child, column_index, context)?; + Ok(Some(Box::new(MapStructuralEncoder::new( + context.options.keep_original_array, + child_encoder, + )))) +} + +/// Create a non-packed, non-empty struct encoder when `field` matches. +pub fn try_create_struct( + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, +) -> Result>> { + let DataType::Struct(fields) = field.data_type() else { + return Ok(None); + }; + if field.is_blob() || field.is_packed_struct() || fields.is_empty() { + return Ok(None); + } + let children_encoders = field + .children + .iter() + .map(|child| { + context + .strategy + .create_field_encoder(child, column_index, context) + }) + .collect::>>()?; + Ok(Some(Box::new(StructStructuralEncoder::new( + context.options.keep_original_array, + children_encoders, + )))) +} diff --git a/rust/lance-encoding/src/encodings/fuzz_tests.rs b/rust/lance-encoding/src/encodings/fuzz_tests.rs index b92bac09cca..3521d633ac3 100644 --- a/rust/lance-encoding/src/encodings/fuzz_tests.rs +++ b/rust/lance-encoding/src/encodings/fuzz_tests.rs @@ -16,7 +16,6 @@ use arrow_schema::{DataType, Field}; use proptest::prelude::*; use crate::testing::{TestCases, check_round_trip_encoding_of_data}; -use crate::version::LanceFileVersion; use lance_core::Result; use lance_datagen::{ArrayGenerator, ByteCount, Dimension, RowCount, Seed, array, gen_batch}; @@ -280,7 +279,7 @@ proptest! { } let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) + .with_structural_encodings() .with_batch_size(100) .with_range(0..num_rows.min(500) as u64) .with_indices(vec![0, num_rows as u64 / 2, (num_rows - 1) as u64]); @@ -301,7 +300,7 @@ async fn test_edge_cases_single_value() { let single_int32 = Arc::new(Int32Array::from(vec![42])) as Arc; let single_string = Arc::new(StringArray::from(vec!["test"])) as Arc; - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![single_int32], &test_cases, HashMap::new()).await; @@ -317,7 +316,7 @@ async fn test_edge_cases_all_nulls() { vec![None, None, None] as Vec> )) as Arc; - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![all_nulls_int32], &test_cases, HashMap::new()).await; @@ -347,7 +346,7 @@ proptest! { let list_array = Arc::new(list_builder.finish()) as Arc; let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) + .with_structural_encodings() .with_range(0..list_sizes.len().min(50) as u64); check_round_trip_encoding_of_data( @@ -381,7 +380,7 @@ proptest! { .expect("Failed to generate test data"); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data( vec![test_data], @@ -424,7 +423,7 @@ async fn test_list_dict_empty_batch() { let list_array = Arc::new(list_builder.finish()); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) + .with_structural_encodings() // Read only the empty/null lists (rows 50-99) // This batch will have 0 underlying values .with_range(50..100); @@ -539,7 +538,7 @@ async fn test_all_valid_combinations() { let test_data = generate_test_data_for_config(&config, 100, 42).expect("Failed to generate test data"); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![test_data], &test_cases, HashMap::new()).await; } diff --git a/rust/lance-encoding/src/encodings/logical/blob.rs b/rust/lance-encoding/src/encodings/logical/blob.rs index d1798b1ef69..72ea44c2e64 100644 --- a/rust/lance-encoding/src/encodings/logical/blob.rs +++ b/rust/lance-encoding/src/encodings/logical/blob.rs @@ -21,7 +21,6 @@ use crate::{ constants::PACKED_STRUCT_META_KEY, decoder::PageEncoding, encoder::{EncodeTask, EncodedColumn, EncodedPage, FieldEncoder, OutOfLineBuffers}, - encodings::logical::primitive::PrimitiveStructuralEncoder, format::ProtobufUtils21, repdef::{DefinitionInterpretation, RepDefBuilder}, }; @@ -42,9 +41,7 @@ pub struct BlobStructuralEncoder { impl BlobStructuralEncoder { pub fn new( field: &Field, - column_index: u32, - options: &crate::encoder::EncodingOptions, - compression_strategy: Arc, + make_descriptor_encoder: impl FnOnce(Field) -> Result>, ) -> Result { // Create descriptor field: struct // Preserve the original field's metadata for packed struct @@ -63,13 +60,7 @@ impl BlobStructuralEncoder { )?; // Use PrimitiveStructuralEncoder to handle the descriptor - let descriptor_encoder = Box::new(PrimitiveStructuralEncoder::try_new( - options, - compression_strategy, - column_index, - descriptor_field, - Arc::new(HashMap::new()), - )?); + let descriptor_encoder = make_descriptor_encoder(descriptor_field)?; Ok(Self { descriptor_encoder, @@ -236,9 +227,7 @@ pub struct BlobV2StructuralEncoder { impl BlobV2StructuralEncoder { pub fn new( field: &Field, - column_index: u32, - options: &crate::encoder::EncodingOptions, - compression_strategy: Arc, + make_descriptor_encoder: impl FnOnce(Field) -> Result>, ) -> Result { let mut descriptor_metadata = HashMap::with_capacity(1); descriptor_metadata.insert(PACKED_STRUCT_META_KEY.to_string(), "true".to_string()); @@ -250,13 +239,7 @@ impl BlobV2StructuralEncoder { .with_metadata(descriptor_metadata), )?; - let descriptor_encoder = Box::new(PrimitiveStructuralEncoder::try_new( - options, - compression_strategy, - column_index, - descriptor_field, - Arc::new(HashMap::new()), - )?); + let descriptor_encoder = make_descriptor_encoder(descriptor_field)?; Ok(Self { descriptor_encoder }) } @@ -430,13 +413,12 @@ impl FieldEncoder for BlobV2StructuralEncoder { mod tests { use super::*; use crate::{ - compression::DefaultCompressionStrategy, encoder::{ColumnIndexSequence, EncodingOptions}, testing::{ - TestCases, check_round_trip_encoding_of_data, - check_round_trip_encoding_of_data_with_expected, + TestCases, TestEncoding, check_round_trip_encoding_of_data, + check_round_trip_encoding_of_data_with_expected, create_test_field_encoder, + test_encoding_strategy, }, - version::LanceFileVersion, }; use arrow_array::{ ArrayRef, LargeBinaryArray, StringArray, StructArray, UInt8Array, UInt32Array, UInt64Array, @@ -445,14 +427,18 @@ mod tests { #[test] fn test_blob_encoder_creation() { - let field = - Field::try_from(ArrowField::new("blob_field", DataType::LargeBinary, true)).unwrap(); + let field = Field::try_from( + ArrowField::new("blob_field", DataType::LargeBinary, true).with_metadata( + HashMap::from([(lance_arrow::BLOB_META_KEY.to_string(), "true".to_string())]), + ), + ) + .unwrap(); let mut column_index = ColumnIndexSequence::default(); - let column_idx = column_index.next_column_index(0); let options = EncodingOptions::default(); - let compression = Arc::new(DefaultCompressionStrategy::new()); + let strategy = test_encoding_strategy(TestEncoding::StructuralU16); - let encoder = BlobStructuralEncoder::new(&field, column_idx, &options, compression); + let encoder = + create_test_field_encoder(strategy.as_ref(), &field, &mut column_index, &options); assert!(encoder.is_ok()); } @@ -466,12 +452,12 @@ mod tests { ) .unwrap(); let mut column_index = ColumnIndexSequence::default(); - let column_idx = column_index.next_column_index(0); let options = EncodingOptions::default(); - let compression = Arc::new(DefaultCompressionStrategy::new()); + let strategy = test_encoding_strategy(TestEncoding::StructuralU16); let mut encoder = - BlobStructuralEncoder::new(&field, column_idx, &options, compression).unwrap(); + create_test_field_encoder(strategy.as_ref(), &field, &mut column_index, &options) + .unwrap(); // Create test data with larger blobs let large_data = vec![0u8; 1024 * 100]; // 100KB blob @@ -524,7 +510,7 @@ mod tests { // Use the standard test harness check_round_trip_encoding_of_data( vec![array], - &TestCases::default().with_max_file_version(LanceFileVersion::V2_1), + &TestCases::default().with_array_and_u16_encodings(), blob_metadata, ) .await; @@ -628,7 +614,7 @@ mod tests { check_round_trip_encoding_of_data_with_expected( vec![Arc::new(struct_array)], Some(Arc::new(expected_descriptor)), - &TestCases::default().with_min_file_version(LanceFileVersion::V2_2), + &TestCases::default().with_u32_structural_encodings(), blob_metadata, ) .await; @@ -693,7 +679,7 @@ mod tests { check_round_trip_encoding_of_data_with_expected( vec![Arc::new(struct_array)], Some(Arc::new(expected_descriptor)), - &TestCases::default().with_min_file_version(LanceFileVersion::V2_2), + &TestCases::default().with_u32_structural_encodings(), blob_metadata, ) .await; @@ -755,7 +741,7 @@ mod tests { check_round_trip_encoding_of_data_with_expected( vec![Arc::new(struct_array)], Some(Arc::new(expected_descriptor)), - &TestCases::default().with_min_file_version(LanceFileVersion::V2_2), + &TestCases::default().with_u32_structural_encodings(), blob_metadata, ) .await; @@ -817,7 +803,7 @@ mod tests { check_round_trip_encoding_of_data_with_expected( vec![Arc::new(struct_array)], Some(Arc::new(expected_descriptor)), - &TestCases::default().with_min_file_version(LanceFileVersion::V2_2), + &TestCases::default().with_u32_structural_encodings(), blob_metadata, ) .await; diff --git a/rust/lance-encoding/src/encodings/logical/fixed_size_list.rs b/rust/lance-encoding/src/encodings/logical/fixed_size_list.rs index 1308288f226..94d3702fd66 100644 --- a/rust/lance-encoding/src/encodings/logical/fixed_size_list.rs +++ b/rust/lance-encoding/src/encodings/logical/fixed_size_list.rs @@ -534,7 +534,6 @@ mod tests { STRUCTURAL_ENCODING_MINIBLOCK, }, testing::{TestCases, check_specific_random}, - version::LanceFileVersion, }; fn make_fsl_struct_type(struct_fields: Fields, dimension: i32) -> DataType { @@ -688,18 +687,17 @@ mod tests { } #[rstest] - #[case::simple(simple_struct_fields(), 2, LanceFileVersion::V2_2)] - #[case::nested_struct(nested_struct_fields(), 2, LanceFileVersion::V2_2)] - #[case::struct_with_list(struct_with_list_fields(), 2, LanceFileVersion::V2_2)] - #[case::struct_with_large_list(struct_with_large_list_fields(), 2, LanceFileVersion::V2_2)] - #[case::nested_struct_with_list(nested_struct_with_list_fields(), 2, LanceFileVersion::V2_2)] - #[case::struct_with_nested_fsl(struct_with_nested_fsl_fields(), 2, LanceFileVersion::V2_2)] - #[case::struct_with_map(struct_with_map_fields(), 2, LanceFileVersion::V2_2)] + #[case::simple(simple_struct_fields(), 2)] + #[case::nested_struct(nested_struct_fields(), 2)] + #[case::struct_with_list(struct_with_list_fields(), 2)] + #[case::struct_with_large_list(struct_with_large_list_fields(), 2)] + #[case::nested_struct_with_list(nested_struct_with_list_fields(), 2)] + #[case::struct_with_nested_fsl(struct_with_nested_fsl_fields(), 2)] + #[case::struct_with_map(struct_with_map_fields(), 2)] #[test_log::test(tokio::test)] async fn test_fsl_struct_random( #[case] struct_fields: Fields, #[case] dimension: i32, - #[case] min_version: LanceFileVersion, #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)] structural_encoding: &str, ) { @@ -710,7 +708,7 @@ mod tests { structural_encoding.into(), ); let field = Field::new("", data_type, true).with_metadata(field_metadata); - let test_cases = TestCases::basic().with_min_file_version(min_version); + let test_cases = TestCases::basic().with_u32_structural_encodings(); check_specific_random(field, test_cases).await; } diff --git a/rust/lance-encoding/src/encodings/logical/list.rs b/rust/lance-encoding/src/encodings/logical/list.rs index c9bdcc0bc87..b79b651e624 100644 --- a/rust/lance-encoding/src/encodings/logical/list.rs +++ b/rust/lance-encoding/src/encodings/logical/list.rs @@ -251,9 +251,9 @@ mod tests { use arrow_schema::{DataType, Field, Fields}; use rstest::rstest; - use crate::{ - testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data}, - version::LanceFileVersion, + use crate::testing::{ + TestCases, TestEncoding, check_basic_random, check_round_trip_encoding_of_data, + create_test_field_encoder, test_encoding_strategy, }; fn make_list_type(inner_type: DataType) -> DataType { @@ -277,20 +277,16 @@ mod tests { let arrow_field = Field::new("", array.data_type().clone(), true).with_metadata(field_metadata); let lance_field = lance_core::datatypes::Field::try_from(&arrow_field).unwrap(); - let encoding_strategy = crate::encoder::default_encoding_strategy(LanceFileVersion::V2_2); + let encoding_strategy = test_encoding_strategy(TestEncoding::StructuralU32); let mut column_index_seq = crate::encoder::ColumnIndexSequence::default(); - let encoding_options = crate::encoder::EncodingOptions { - version: LanceFileVersion::V2_2, - ..Default::default() - }; - let mut encoder = encoding_strategy - .create_field_encoder( - encoding_strategy.as_ref(), - &lance_field, - &mut column_index_seq, - &encoding_options, - ) - .unwrap(); + let encoding_options = crate::encoder::EncodingOptions::default(); + let mut encoder = create_test_field_encoder( + encoding_strategy.as_ref(), + &lance_field, + &mut column_index_seq, + &encoding_options, + ) + .unwrap(); let mut external_buffers = crate::encoder::OutOfLineBuffers::new(0, crate::encoder::MIN_PAGE_BUFFER_ALIGNMENT); let num_rows = array.len() as u64; @@ -515,7 +511,7 @@ mod tests { structural_encoding.into(), ); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(chunks, &test_cases, field_metadata).await; } @@ -625,7 +621,7 @@ mod tests { .with_range(5..7) .with_indices(vec![1, 6]) .with_indices(vec![6]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(outer_list)], &test_cases, field_metadata) .await; } @@ -656,7 +652,7 @@ mod tests { .with_range(1..3) .with_indices(vec![1, 3]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata) .await; } @@ -687,7 +683,7 @@ mod tests { .with_range(1..3) .with_indices(vec![1, 3]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata) .await; } @@ -719,7 +715,7 @@ mod tests { .with_range(1..2) .with_indices(vec![0]) .with_indices(vec![1]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata) .await; } @@ -771,7 +767,7 @@ mod tests { .with_range(1..2) .with_indices(vec![1]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data( vec![Arc::new(list_arr)], &test_cases, @@ -810,7 +806,7 @@ mod tests { .with_range(1..2) .with_indices(vec![1]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_arr)], &test_cases, field_metadata) .await; } @@ -846,7 +842,7 @@ mod tests { ); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) + .with_structural_encodings() .with_page_sizes(vec![100]) .with_range(800..900); check_round_trip_encoding_of_data( @@ -1006,7 +1002,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..2) .with_indices(vec![1]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data( vec![struct_array.clone()], &test_cases, @@ -1028,7 +1024,7 @@ mod tests { outer_list_builder.append_null(); let list_array = Arc::new(outer_list_builder.finish()); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await; } @@ -1093,7 +1089,7 @@ mod tests { // This should trigger the assertion failure at primitive.rs:1362 // debug_assert!(rows_avail > 0) - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); // The bug manifests when encoding this specific pattern // Expected: successful round-trip encoding @@ -1147,7 +1143,7 @@ mod tests { .with_range(0..1000) .with_range(0..num_rows as u64) .with_indices(vec![0, (step / 2) as u64, num_rows as u64 - 1]) - .with_max_file_version(LanceFileVersion::V2_2); + .with_dense_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata) .await; } @@ -1187,7 +1183,7 @@ mod tests { .with_range(0..1000) .with_range(0..num_rows as u64) .with_indices(vec![0, (step / 2) as u64, num_rows as u64 - 1]) - .with_max_file_version(LanceFileVersion::V2_2); + .with_dense_encodings(); let list_array = Arc::new(list_array) as ArrayRef; let pages = encode_v22_pages(list_array.clone()).await; assert_split_miniblock_layout(&pages, false); @@ -1223,7 +1219,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..num_rows as u64) .with_indices(vec![0, empty_prefix_rows as u64, num_rows as u64 - 1]) - .with_max_file_version(LanceFileVersion::V2_2); + .with_dense_encodings(); let list_array = Arc::new(list_array) as ArrayRef; let pages = encode_v22_pages(list_array.clone()).await; assert_split_miniblock_layout(&pages, true); @@ -1260,7 +1256,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..num_rows as u64) .with_indices(vec![0, null_prefix_rows as u64, num_rows as u64 - 1]) - .with_max_file_version(LanceFileVersion::V2_2); + .with_dense_encodings(); let list_array = Arc::new(list_array) as ArrayRef; let pages = encode_v22_pages(list_array.clone()).await; assert_split_miniblock_layout(&pages, true); @@ -1291,7 +1287,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..num_rows as u64) .with_indices(vec![0, empty_prefix_rows as u64]) - .with_max_file_version(LanceFileVersion::V2_2); + .with_dense_encodings(); let list_array = Arc::new(list_array) as ArrayRef; let pages = encode_v22_pages(list_array.clone()).await; assert_split_miniblock_layout(&pages, true); @@ -1364,7 +1360,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..1) .with_indices(vec![0]) - .with_max_file_version(LanceFileVersion::V2_2); + .with_dense_encodings(); check_round_trip_encoding_of_data(vec![outer_list], &test_cases, HashMap::new()).await; } @@ -1412,8 +1408,7 @@ mod tests { .with_range(dense_rows.saturating_sub(8)..(dense_rows + 8)) .with_range(0..total_rows) .with_indices(vec![0, dense_rows - 1, dense_rows, total_rows - 1]) - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2); + .with_encoding(TestEncoding::StructuralU32); let list_array = Arc::new(list_array) as ArrayRef; let pages = encode_v22_pages(list_array.clone()).await; assert_split_miniblock_layout(&pages, true); @@ -1438,8 +1433,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..total_rows) - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2); + .with_encoding(TestEncoding::StructuralU32); let list_array = Arc::new(list_array) as ArrayRef; let pages = try_encode_v22_pages_with_metadata(list_array.clone(), field_metadata.clone()) .await diff --git a/rust/lance-encoding/src/encodings/logical/map.rs b/rust/lance-encoding/src/encodings/logical/map.rs index be153030105..d07c267bd2a 100644 --- a/rust/lance-encoding/src/encodings/logical/map.rs +++ b/rust/lance-encoding/src/encodings/logical/map.rs @@ -246,15 +246,14 @@ mod tests { use arrow_schema::{DataType, Field, Fields}; use crate::decoder::{DecodedArray, StructuralDecodeArrayTask}; - use crate::encoder::{ColumnIndexSequence, EncodingOptions, default_encoding_strategy}; + use crate::encoder::{ColumnIndexSequence, EncodingOptions}; use crate::encodings::logical::primitive::sparse::{ SparseCountSet, SparsePositionSet, SparseStructuralLayerPlan, SparseStructuralPlan, SparseValidityMeaning, SparseValiditySet, }; use crate::repdef::{CompositeRepDefUnraveler, RepDefUnraveler}; - use crate::{ - testing::{TestCases, check_round_trip_encoding_of_data}, - version::LanceFileVersion, + use crate::testing::{ + TestCases, TestEncoding, check_round_trip_encoding_of_data, test_encoding_strategy, }; use arrow_schema::Field as ArrowField; use lance_core::datatypes::Field as LanceField; @@ -361,7 +360,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..2) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(map_array)], &test_cases, HashMap::new()) .await; @@ -394,7 +393,7 @@ mod tests { .with_range(0..4) .with_indices(vec![1]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(map_array)], &test_cases, HashMap::new()) .await; @@ -425,7 +424,7 @@ mod tests { .with_range(0..2) .with_indices(vec![0]) .with_indices(vec![1]) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(map_array)], &test_cases, HashMap::new()) .await; @@ -474,7 +473,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..3) .with_indices(vec![0, 2]) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data( vec![Arc::new(struct_array)], @@ -528,7 +527,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..3) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data( vec![Arc::new(struct_array)], @@ -574,7 +573,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..3) .with_indices(vec![0, 2]) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, HashMap::new()) .await; @@ -634,7 +633,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..1) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(outer_map)], &test_cases, HashMap::new()) .await; @@ -664,7 +663,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..2) .with_indices(vec![0, 1]) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(map_array)], &test_cases, HashMap::new()) .await; @@ -691,7 +690,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..2) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(map_array)], &test_cases, HashMap::new()) .await; @@ -712,7 +711,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..2) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(map_array)], &test_cases, HashMap::new()) .await; @@ -747,7 +746,7 @@ mod tests { let test_cases = TestCases::default() .with_range(0..3) .with_indices(vec![0, 1, 2]) - .with_min_file_version(LanceFileVersion::V2_2); + .with_u32_structural_encodings(); // This test ensures that regardless of the internal keep_original_array setting, // the end-to-end behavior produces equivalent results @@ -766,11 +765,11 @@ mod tests { let map_field = LanceField::try_from(&map_arrow_field).unwrap(); // Test encoder: Try to create encoder with V2_1 version - should fail - let encoder_strategy = default_encoding_strategy(LanceFileVersion::V2_1); + let encoder_strategy = test_encoding_strategy(TestEncoding::StructuralU16); let mut column_index = ColumnIndexSequence::default(); let options = EncodingOptions::default(); - let encoder_result = encoder_strategy.create_field_encoder( + let encoder_result = crate::testing::create_test_field_encoder( encoder_strategy.as_ref(), &map_field, &mut column_index, @@ -787,9 +786,8 @@ mod tests { let encoder_err_msg = format!("{}", encoder_err); assert!( - encoder_err_msg.contains("2.2"), - "Encoder error message should mention version 2.2, got: {}", - encoder_err_msg + encoder_err_msg.contains("not enabled by the selected file format"), + "unexpected encoder error: {encoder_err_msg}" ); assert!( encoder_err_msg.contains("Map data type"), diff --git a/rust/lance-encoding/src/encodings/logical/primitive.rs b/rust/lance-encoding/src/encodings/logical/primitive.rs index c13ef2f3034..beb6a306c16 100644 --- a/rust/lance-encoding/src/encodings/logical/primitive.rs +++ b/rust/lance-encoding/src/encodings/logical/primitive.rs @@ -59,7 +59,8 @@ use crate::{ use crate::{ repdef::{ CompositeRepDefUnraveler, ControlWordIterator, ControlWordParser, DefinitionInterpretation, - MiniBlockRepDefBudget, RepDefSlicer, SerializedRepDefs, build_control_word_iterator, + MiniBlockRepDefBudget, NormalizedStructuralPlan, RepDefSlicer, SerializedRepDefs, + build_control_word_iterator, }, utils::accumulation::AccumulationQueue, }; @@ -71,7 +72,6 @@ use crate::constants::{ DICT_VALUES_COMPRESSION_LEVEL_ENV_VAR, DICT_VALUES_COMPRESSION_LEVEL_META_KEY, DICT_VALUES_COMPRESSION_META_KEY, }; -use crate::version::LanceFileVersion; use crate::{ EncodingsIo, buffer::LanceBuffer, @@ -3752,19 +3752,125 @@ const MINIBLOCK_ALIGNMENT: usize = 8; /// TODO: We should concatenate metadata buffers from all pages into a single buffer /// at (roughly) the end of the file so there is, at most, one read per column of /// metadata per file. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum MiniblockChunkSize { + U16, + U32, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ComplexNullEncoding { + RawLevels, + CompressedLevels, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum FixedWidthDictionaryEncoding { + Exclude64Bit, + Include64Bit, +} + +trait PrimitivePageEncodingBehavior: Send + Sync + Debug { + fn validate_field(&self, _field: &Field, _metadata: &HashMap) -> Result<()> { + Ok(()) + } + + fn try_plan_pages( + &self, + _ctx: &PrimitivePlanContext<'_>, + _arrays: &[ArrayRef], + _normalized: &NormalizedStructuralPlan, + _row_number: u64, + _num_rows: u64, + _num_values: u64, + ) -> Result>> { + Ok(None) + } + + fn try_encode_page( + &self, + _ctx: &PrimitiveEncodeContext, + page: PrimitivePageData, + ) -> Result { + Ok(PrimitiveEncodeAttempt::Unhandled(page)) + } +} + +/// One executable primitive-page behavior selected by an exact file +/// composition. +#[derive(Debug, Clone)] +pub struct PrimitivePageEncoding { + behavior: Arc, +} + +impl PrimitivePageEncoding { + /// Reject an explicit request for sparse structural encoding. + pub fn reject_sparse() -> Self { + Self { + behavior: Arc::new(RejectSparsePrimitiveEncoding), + } + } + + /// Encode constant non-null values as a constant page when applicable. + pub fn constant() -> Self { + Self { + behavior: Arc::new(ConstantPrimitiveEncoding), + } + } + + /// Plan and encode sparse structural pages when applicable. + pub fn sparse(compression: Arc) -> Self { + Self { + behavior: Arc::new(SparsePrimitiveEncoding { compression }), + } + } + + /// Encode dense pages with the original u16 miniblock grammar. + pub fn dense_u16(compression: Arc) -> Self { + Self { + behavior: Arc::new(DenseU16PrimitiveEncoding { compression }), + } + } + + /// Encode dense pages with the u32 miniblock grammar. + pub fn dense_u32(compression: Arc) -> Self { + Self { + behavior: Arc::new(DenseU32PrimitiveEncoding { compression }), + } + } +} + +#[derive(Debug)] +struct RejectSparsePrimitiveEncoding; + +#[derive(Debug)] +struct ConstantPrimitiveEncoding; + +#[derive(Debug)] +struct SparsePrimitiveEncoding { + compression: Arc, +} + +#[derive(Debug)] +struct DenseU16PrimitiveEncoding { + compression: Arc, +} + +#[derive(Debug)] +struct DenseU32PrimitiveEncoding { + compression: Arc, +} + pub struct PrimitiveStructuralEncoder { // Accumulates arrays until we have enough data to justify a disk page accumulation_queue: AccumulationQueue, keep_original_array: bool, - support_large_chunk: bool, accumulated_repdefs: Vec, - // The compression strategy we will use to compress the data - compression_strategy: Arc, + page_encodings: Arc<[PrimitivePageEncoding]>, column_index: u32, field: Field, encoding_metadata: Arc>, - version: LanceFileVersion, } struct CompressedLevelsChunk { @@ -3813,6 +3919,17 @@ struct PrimitivePageData { num_rows: u64, } +struct PrimitivePlanContext<'a> { + column_idx: u32, + field: &'a Field, + encoding_metadata: &'a HashMap, +} + +enum PrimitiveEncodeAttempt { + Encoded(EncodedPage), + Unhandled(PrimitivePageData), +} + // Immutable encoder state shared by per-page encode tasks. // // Cloning this only clones Arc-backed configuration and field metadata. Page data @@ -3821,42 +3938,24 @@ struct PrimitivePageData { struct PrimitiveEncodeContext { // Column being encoded. column_idx: u32, - // Logical field metadata for compression/layout selection. field: Field, - // Compression strategy shared across pages. - compression_strategy: Arc, - // Field-level encoding metadata such as structural encoding overrides. encoding_metadata: Arc>, - // Whether miniblock chunks may use the v2.2 large-chunk metadata. - support_large_chunk: bool, - // Lance file version selected by the writer. - version: LanceFileVersion, - // True when the only rep/def information is simple nullable validity. is_simple_validity: bool, - // True when the field has any non-empty rep/def information. has_repdef_info: bool, } impl PrimitiveStructuralEncoder { pub fn try_new( options: &EncodingOptions, - compression_strategy: Arc, + page_encodings: Arc<[PrimitivePageEncoding]>, column_index: u32, field: Field, encoding_metadata: Arc>, ) -> Result { - let requests_sparse = encoding_metadata - .get(STRUCTURAL_ENCODING_META_KEY) - .is_some_and(|requested| requested.eq_ignore_ascii_case(STRUCTURAL_ENCODING_SPARSE)); - if requests_sparse && options.version.resolve() < LanceFileVersion::V2_3 { - return Err(Error::invalid_input_source( - format!( - "Field '{}' requests sparse structural encoding, which requires Lance file format 2.3+; current version is {}", - field.name, - options.version.resolve() - ) - .into(), - )); + for page_encoding in page_encodings.iter() { + page_encoding + .behavior + .validate_field(&field, &encoding_metadata)?; } Ok(Self { accumulation_queue: AccumulationQueue::new( @@ -3864,17 +3963,35 @@ impl PrimitiveStructuralEncoder { column_index, options.keep_original_array, ), - support_large_chunk: options.support_large_chunk(), keep_original_array: options.keep_original_array, accumulated_repdefs: Vec::new(), column_index, - compression_strategy, + page_encodings, field, encoding_metadata, - version: options.version, }) } + fn encode_page( + page_encodings: &[PrimitivePageEncoding], + ctx: &PrimitiveEncodeContext, + mut page: PrimitivePageData, + ) -> Result { + for page_encoding in page_encodings { + match page_encoding.behavior.try_encode_page(ctx, page)? { + PrimitiveEncodeAttempt::Encoded(page) => return Ok(page), + PrimitiveEncodeAttempt::Unhandled(unhandled) => page = unhandled, + } + } + Err(Error::invalid_input_source( + format!( + "No primitive page encoding atom supports field '{}'", + ctx.field.name + ) + .into(), + )) + } + // TODO: This is a heuristic we may need to tune at some point // // As data gets narrow then the "zipping" process gets too expensive @@ -3969,7 +4086,7 @@ impl PrimitiveStructuralEncoder { miniblocks: MiniBlockCompressed, rep: Option>, def: Option>, - support_large_chunk: bool, + miniblock_chunk_size: MiniblockChunkSize, ) -> Result { let bytes_rep = rep .as_ref() @@ -3990,7 +4107,10 @@ impl PrimitiveStructuralEncoder { // 2 bytes for the length of each buffer and up to 7 bytes of padding per buffer let max_extra = 9 * num_buffers; let mut data_buffer = Vec::with_capacity(bytes_rep + bytes_def + bytes_data + max_extra); - let chunk_size_bytes = if support_large_chunk { 4 } else { 2 }; + let chunk_size_bytes = match miniblock_chunk_size { + MiniblockChunkSize::U16 => 2, + MiniblockChunkSize::U32 => 4, + }; let mut meta_buffer = Vec::with_capacity(miniblocks.chunks.len() * chunk_size_bytes); let mut rep_iter = rep.map(|r| r.into_iter()); @@ -4032,7 +4152,7 @@ impl PrimitiveStructuralEncoder { data_buffer.extend_from_slice(&bytes_def.to_le_bytes()); } - if support_large_chunk { + if miniblock_chunk_size == MiniblockChunkSize::U32 { for &buffer_size in &chunk.buffer_sizes { data_buffer.extend_from_slice(&buffer_size.to_le_bytes()); } @@ -4077,10 +4197,9 @@ impl PrimitiveStructuralEncoder { } let chunk_bytes = data_buffer.len() - start_pos; - let max_chunk_size = if support_large_chunk { - 1_u64 << 31 // 28 bits of 8-byte words in u32 metadata - } else { - 32 * 1024 // 32KiB limit with u16 metadata + let max_chunk_size = match miniblock_chunk_size { + MiniblockChunkSize::U16 => 32 * 1024, + MiniblockChunkSize::U32 => 1_u64 << 31, }; if chunk_bytes == 0 || chunk_bytes as u64 > max_chunk_size { return Err(Error::internal(format!( @@ -4107,7 +4226,7 @@ impl PrimitiveStructuralEncoder { let divided_bytes_minus_one = (divided_bytes - 1) as u64; let metadata = (divided_bytes_minus_one << 4) | chunk.log_num_values as u64; - if support_large_chunk { + if miniblock_chunk_size == MiniblockChunkSize::U32 { meta_buffer.extend_from_slice(&(metadata as u32).to_le_bytes()); } else { meta_buffer.extend_from_slice(&(metadata as u16).to_le_bytes()); @@ -4303,10 +4422,10 @@ impl PrimitiveStructuralEncoder { repdef: crate::repdef::SerializedRepDefs, row_number: u64, num_rows: u64, - version: LanceFileVersion, + complex_null_encoding: ComplexNullEncoding, compression_strategy: &dyn CompressionStrategy, ) -> Result { - if version.resolve() < LanceFileVersion::V2_2 { + if complex_null_encoding == ComplexNullEncoding::RawLevels { let rep_bytes = if let Some(rep) = repdef.repetition_levels.as_ref() { LanceBuffer::reinterpret_slice(rep.clone()) } else { @@ -4600,7 +4719,7 @@ impl PrimitiveStructuralEncoder { row_number: u64, dictionary_data: Option, num_rows: u64, - support_large_chunk: bool, + miniblock_chunk_size: MiniblockChunkSize, ) -> Result { if let DataBlock::AllNull(_null_block) = data { // We should not be using mini-block for all-null. There are other structural @@ -4660,7 +4779,8 @@ impl PrimitiveStructuralEncoder { .map(|cd| std::mem::take(&mut cd.data)); let serialized = - Self::serialize_miniblocks(compressed_data, rep_data, def_data, support_large_chunk)?; + Self::serialize_miniblocks(compressed_data, rep_data, def_data, miniblock_chunk_size)?; + let has_large_chunk = miniblock_chunk_size == MiniblockChunkSize::U32; // Metadata, Data, Dictionary, (maybe) Repetition Index let mut data = Vec::with_capacity(4); @@ -4689,7 +4809,7 @@ impl PrimitiveStructuralEncoder { Some((dictionary_encoding, num_dictionary_items)), &repdef.def_meaning, num_items, - support_large_chunk, + has_large_chunk, ); Ok(EncodedPage { num_rows, @@ -4708,7 +4828,7 @@ impl PrimitiveStructuralEncoder { None, &repdef.def_meaning, num_items, - support_large_chunk, + has_large_chunk, ); if let Some(rep_index) = rep_index { @@ -5040,7 +5160,7 @@ impl PrimitiveStructuralEncoder { fn should_dictionary_encode( data_block: &DataBlock, field: &Field, - version: LanceFileVersion, + fixed_width_dictionary_encoding: FixedWidthDictionaryEncoding, ) -> Option { const DEFAULT_SAMPLE_SIZE: usize = 4096; const DEFAULT_SAMPLE_UNIQUE_RATIO: f64 = 0.98; @@ -5049,7 +5169,9 @@ impl PrimitiveStructuralEncoder { // estimating the size for other types. match data_block { DataBlock::FixedWidth(fixed) => { - if fixed.bits_per_value == 64 && version < LanceFileVersion::V2_2 { + if fixed.bits_per_value == 64 + && fixed_width_dictionary_encoding == FixedWidthDictionaryEncoding::Exclude64Bit + { return None; } if fixed.bits_per_value != 64 && fixed.bits_per_value != 128 { @@ -5368,14 +5490,18 @@ impl PrimitiveStructuralEncoder { Ok(pages) } - fn encode_page(ctx: PrimitiveEncodeContext, page: PrimitivePageData) -> Result { + fn encode_dense_page( + ctx: PrimitiveEncodeContext, + page: PrimitivePageData, + compression_strategy: Arc, + miniblock_chunk_size: MiniblockChunkSize, + complex_null_encoding: ComplexNullEncoding, + fixed_width_dictionary_encoding: FixedWidthDictionaryEncoding, + ) -> Result { let PrimitiveEncodeContext { column_idx, field, - compression_strategy, encoding_metadata, - support_large_chunk, - version, is_simple_validity, has_repdef_info, } = ctx; @@ -5392,33 +5518,8 @@ impl PrimitiveStructuralEncoder { repdef, single_row_miniblock_repdef_levels, } => (repdef, single_row_miniblock_repdef_levels), - PrimitivePageStructure::Sparse { - plan, - prepared_values, - } => { - log::debug!( - "Encoding column {} with {} visible items ({} rows) using sparse layout", - column_idx, - num_values, - num_rows - ); - return sparse::writer::encode_page( - column_idx, - &field, - compression_strategy.as_ref(), - prepared_values.map_or_else( - || { - sparse::writer::SparseValueInput::Unprepared(DataBlock::from_arrays( - &arrays, num_values, - )) - }, - sparse::writer::SparseValueInput::Prepared, - ), - plan, - row_number, - num_rows, - support_large_chunk, - ); + PrimitivePageStructure::Sparse { .. } => { + unreachable!("dense atom received sparse page") } }; @@ -5436,7 +5537,7 @@ impl PrimitiveStructuralEncoder { repdef, row_number, num_rows, - version, + complex_null_encoding, compression_strategy.as_ref(), ); } @@ -5468,7 +5569,7 @@ impl PrimitiveStructuralEncoder { repdef, row_number, num_rows, - version, + complex_null_encoding, compression_strategy.as_ref(), ) }; @@ -5487,20 +5588,6 @@ impl PrimitiveStructuralEncoder { let data_block = DataBlock::from_arrays(&arrays, num_values); - if version.resolve() >= LanceFileVersion::V2_2 - && let Some(scalar) = Self::find_constant_scalar(&arrays, leaf_validity.as_ref())? - { - log::debug!( - "Encoding column {} with {} items ({} rows) using constant layout", - column_idx, - num_values, - num_rows - ); - return constant::encode_constant_page( - column_idx, scalar, repdef, row_number, num_rows, - ); - } - if let Some(num_levels) = single_row_miniblock_repdef_levels { let requested_encoding = encoding_metadata .get(STRUCTURAL_ENCODING_META_KEY) @@ -5649,24 +5736,29 @@ impl PrimitiveStructuralEncoder { row_number, Some(dictionary_data_block), num_rows, - support_large_chunk, + miniblock_chunk_size, ); } // Try dictionary encoding first if applicable. If encoding aborts, fall back to the // preferred structural encoding. - let dict_result = Self::should_dictionary_encode(&data_block, &field, version).and_then(|budget| { - log::debug!( - "Encoding column {} with {} items using dictionary encoding (mini-block layout)", - column_idx, - num_values - ); - dict::dictionary_encode( - &data_block, - budget.max_dict_entries, - budget.max_encoded_size, - ) - }); + let dict_result = Self::should_dictionary_encode( + &data_block, + &field, + fixed_width_dictionary_encoding, + ) + .and_then(|budget| { + log::debug!( + "Encoding column {} with {} items using dictionary encoding (mini-block layout)", + column_idx, + num_values + ); + dict::dictionary_encode( + &data_block, + budget.max_dict_entries, + budget.max_encoded_size, + ) + }); if let Some((indices_data_block, dictionary_data_block)) = dict_result { Self::encode_miniblock( @@ -5678,7 +5770,7 @@ impl PrimitiveStructuralEncoder { row_number, Some(dictionary_data_block), num_rows, - support_large_chunk, + miniblock_chunk_size, ) } else if Self::prefers_miniblock(&data_block, encoding_metadata.as_ref()) { log::debug!( @@ -5695,7 +5787,7 @@ impl PrimitiveStructuralEncoder { row_number, None, num_rows, - support_large_chunk, + miniblock_chunk_size, ) } else if Self::prefers_fullzip(encoding_metadata.as_ref()) { log::debug!( @@ -5729,96 +5821,48 @@ impl PrimitiveStructuralEncoder { let is_simple_validity = repdefs.iter().all(|rd| rd.is_simple_validity()); let has_repdef_info = repdefs.iter().any(|rd| !rd.is_empty()); let normalized = RepDefBuilder::normalize(repdefs); - let requested_encoding = self.encoding_metadata.get(STRUCTURAL_ENCODING_META_KEY); - let requests_sparse = requested_encoding - .is_some_and(|requested| requested.eq_ignore_ascii_case(STRUCTURAL_ENCODING_SPARSE)); - let sparse_plan = requests_sparse - .then(|| sparse::writer::plan(&normalized, num_values)) - .transpose()?; - let pages = if let Some(plan) = sparse_plan - && !sparse::writer::uses_constant_layout(&plan, &self.field) - { - vec![PrimitivePageData { - arrays, - structure: PrimitivePageStructure::Sparse { - plan, - prepared_values: None, - }, + let plan_ctx = PrimitivePlanContext { + column_idx: self.column_index, + field: &self.field, + encoding_metadata: &self.encoding_metadata, + }; + let mut pages = None; + for page_encoding in self.page_encodings.iter() { + if let Some(planned) = page_encoding.behavior.try_plan_pages( + &plan_ctx, + &arrays, + &normalized, row_number, num_rows, - }] - } else { - let (repdef, miniblock_repdef_budget) = normalized - .serialize_with_miniblock_repdef_budget( - miniblock::max_repdef_levels_per_chunk, - num_rows, - num_values, - )?; - let automatic_sparse = layout::select_automatic_sparse( - self.version.resolve(), - requested_encoding.map(String::as_str), - &miniblock_repdef_budget, - || { - let data = DataBlock::from_arrays(&arrays, num_values); - if !sparse::writer::supports_value_block(&data) { - return Ok(None); - } - let prepared_values = match sparse::writer::prepare_values( - &self.field, - self.compression_strategy.as_ref(), - data, - self.support_large_chunk, - ) { - Ok(prepared_values) => prepared_values, - Err(error) => { - debug!( - "Keeping column {} on its dense structural path because sparse value preparation is unavailable: {}", - self.column_index, error - ); - return Ok(None); - } - }; - let plan = sparse::writer::plan(&normalized, num_values)?; - if sparse::writer::uses_constant_layout(&plan, &self.field) { - return Ok(None); - } - Ok(Some((plan, prepared_values))) - }, - )?; - match automatic_sparse { - Some((plan, prepared_values)) => vec![PrimitivePageData { - arrays, - structure: PrimitivePageStructure::Sparse { - plan, - prepared_values: Some(prepared_values), - }, - row_number, - num_rows, - }], - None => Self::split_pages_for_miniblock_repdef_budget( - arrays, - repdef, - miniblock_repdef_budget, - row_number, - num_rows, - )?, + num_values, + )? { + pages = Some(planned); + break; } - }; + } + let pages = pages.ok_or_else(|| { + Error::invalid_input_source( + format!( + "No primitive page planner supports field '{}'", + self.field.name + ) + .into(), + ) + })?; let mut tasks = Vec::with_capacity(pages.len()); let ctx = PrimitiveEncodeContext { column_idx: self.column_index, field: self.field.clone(), - compression_strategy: self.compression_strategy.clone(), encoding_metadata: self.encoding_metadata.clone(), - support_large_chunk: self.support_large_chunk, - version: self.version, is_simple_validity, has_repdef_info, }; for page in pages { let ctx = ctx.clone(); - let task = spawn_cpu(move || Self::encode_page(ctx, page)).boxed(); + let page_encodings = self.page_encodings.clone(); + let task = + spawn_cpu(move || Self::encode_page(page_encodings.as_ref(), &ctx, page)).boxed(); tasks.push(task); } Ok(tasks) @@ -5870,6 +5914,291 @@ impl PrimitiveStructuralEncoder { } } +impl PrimitivePageEncodingBehavior for RejectSparsePrimitiveEncoding { + fn validate_field(&self, field: &Field, metadata: &HashMap) -> Result<()> { + if metadata + .get(STRUCTURAL_ENCODING_META_KEY) + .is_some_and(|requested| requested.eq_ignore_ascii_case(STRUCTURAL_ENCODING_SPARSE)) + { + return Err(Error::invalid_input_source( + format!( + "Field '{}' requests sparse structural encoding, which is not enabled by the selected file format", + field.name + ) + .into(), + )); + } + Ok(()) + } +} + +fn plan_dense_primitive_pages( + arrays: &[ArrayRef], + normalized: &NormalizedStructuralPlan, + row_number: u64, + num_rows: u64, + num_values: u64, +) -> Result> { + let (repdef, miniblock_repdef_budget) = normalized.serialize_with_miniblock_repdef_budget( + miniblock::max_repdef_levels_per_chunk, + num_rows, + num_values, + )?; + PrimitiveStructuralEncoder::split_pages_for_miniblock_repdef_budget( + arrays.to_vec(), + repdef, + miniblock_repdef_budget, + row_number, + num_rows, + ) +} + +impl PrimitivePageEncodingBehavior for DenseU16PrimitiveEncoding { + fn try_plan_pages( + &self, + _ctx: &PrimitivePlanContext<'_>, + arrays: &[ArrayRef], + normalized: &NormalizedStructuralPlan, + row_number: u64, + num_rows: u64, + num_values: u64, + ) -> Result>> { + Ok(Some(plan_dense_primitive_pages( + arrays, normalized, row_number, num_rows, num_values, + )?)) + } + + fn try_encode_page( + &self, + ctx: &PrimitiveEncodeContext, + page: PrimitivePageData, + ) -> Result { + if !matches!(&page.structure, PrimitivePageStructure::Dense { .. }) { + return Ok(PrimitiveEncodeAttempt::Unhandled(page)); + } + Ok(PrimitiveEncodeAttempt::Encoded( + PrimitiveStructuralEncoder::encode_dense_page( + ctx.clone(), + page, + self.compression.clone(), + MiniblockChunkSize::U16, + ComplexNullEncoding::RawLevels, + FixedWidthDictionaryEncoding::Exclude64Bit, + )?, + )) + } +} + +impl PrimitivePageEncodingBehavior for DenseU32PrimitiveEncoding { + fn try_plan_pages( + &self, + _ctx: &PrimitivePlanContext<'_>, + arrays: &[ArrayRef], + normalized: &NormalizedStructuralPlan, + row_number: u64, + num_rows: u64, + num_values: u64, + ) -> Result>> { + Ok(Some(plan_dense_primitive_pages( + arrays, normalized, row_number, num_rows, num_values, + )?)) + } + + fn try_encode_page( + &self, + ctx: &PrimitiveEncodeContext, + page: PrimitivePageData, + ) -> Result { + if !matches!(&page.structure, PrimitivePageStructure::Dense { .. }) { + return Ok(PrimitiveEncodeAttempt::Unhandled(page)); + } + Ok(PrimitiveEncodeAttempt::Encoded( + PrimitiveStructuralEncoder::encode_dense_page( + ctx.clone(), + page, + self.compression.clone(), + MiniblockChunkSize::U32, + ComplexNullEncoding::CompressedLevels, + FixedWidthDictionaryEncoding::Include64Bit, + )?, + )) + } +} + +impl PrimitivePageEncodingBehavior for SparsePrimitiveEncoding { + fn try_plan_pages( + &self, + ctx: &PrimitivePlanContext<'_>, + arrays: &[ArrayRef], + normalized: &NormalizedStructuralPlan, + row_number: u64, + num_rows: u64, + num_values: u64, + ) -> Result>> { + let requested_encoding = ctx.encoding_metadata.get(STRUCTURAL_ENCODING_META_KEY); + let requests_sparse = requested_encoding + .is_some_and(|requested| requested.eq_ignore_ascii_case(STRUCTURAL_ENCODING_SPARSE)); + if requests_sparse { + let plan = sparse::writer::plan(normalized, num_values)?; + if sparse::writer::uses_constant_layout(&plan, ctx.field) { + return Ok(None); + } + return Ok(Some(vec![PrimitivePageData { + arrays: arrays.to_vec(), + structure: PrimitivePageStructure::Sparse { + plan, + prepared_values: None, + }, + row_number, + num_rows, + }])); + } + + let (_, miniblock_repdef_budget) = normalized.serialize_with_miniblock_repdef_budget( + miniblock::max_repdef_levels_per_chunk, + num_rows, + num_values, + )?; + let automatic_sparse = layout::select_automatic_sparse( + requested_encoding.map(String::as_str), + &miniblock_repdef_budget, + || { + let data = DataBlock::from_arrays(arrays, num_values); + if !sparse::writer::supports_value_block(&data) { + return Ok(None); + } + let prepared_values = match sparse::writer::prepare_values( + ctx.field, + self.compression.as_ref(), + data, + MiniblockChunkSize::U32, + ) { + Ok(prepared_values) => prepared_values, + Err(error) => { + debug!( + "Keeping column {} on its dense structural path because sparse value preparation is unavailable: {}", + ctx.column_idx, error + ); + return Ok(None); + } + }; + let plan = sparse::writer::plan(normalized, num_values)?; + if sparse::writer::uses_constant_layout(&plan, ctx.field) { + return Ok(None); + } + Ok(Some((plan, prepared_values))) + }, + )?; + Ok(automatic_sparse.map(|(plan, prepared_values)| { + vec![PrimitivePageData { + arrays: arrays.to_vec(), + structure: PrimitivePageStructure::Sparse { + plan, + prepared_values: Some(prepared_values), + }, + row_number, + num_rows, + }] + })) + } + + fn try_encode_page( + &self, + ctx: &PrimitiveEncodeContext, + page: PrimitivePageData, + ) -> Result { + if !matches!(&page.structure, PrimitivePageStructure::Sparse { .. }) { + return Ok(PrimitiveEncodeAttempt::Unhandled(page)); + } + let PrimitivePageData { + arrays, + structure: + PrimitivePageStructure::Sparse { + plan, + prepared_values, + }, + row_number, + num_rows, + } = page + else { + unreachable!() + }; + let num_values = arrays.iter().map(|array| array.len() as u64).sum(); + log::debug!( + "Encoding column {} with {} visible items ({} rows) using sparse layout", + ctx.column_idx, + num_values, + num_rows + ); + Ok(PrimitiveEncodeAttempt::Encoded( + sparse::writer::encode_page( + ctx.column_idx, + &ctx.field, + self.compression.as_ref(), + prepared_values.map_or_else( + || { + sparse::writer::SparseValueInput::Unprepared(DataBlock::from_arrays( + &arrays, num_values, + )) + }, + sparse::writer::SparseValueInput::Prepared, + ), + plan, + row_number, + num_rows, + MiniblockChunkSize::U32, + )?, + )) + } +} + +impl PrimitivePageEncodingBehavior for ConstantPrimitiveEncoding { + fn try_encode_page( + &self, + ctx: &PrimitiveEncodeContext, + page: PrimitivePageData, + ) -> Result { + let PrimitivePageStructure::Dense { repdef, .. } = &page.structure else { + return Ok(PrimitiveEncodeAttempt::Unhandled(page)); + }; + let num_values: u64 = page.arrays.iter().map(|array| array.len() as u64).sum(); + if num_values == 0 { + return Ok(PrimitiveEncodeAttempt::Unhandled(page)); + } + let leaf_validity = PrimitiveStructuralEncoder::leaf_validity(repdef, num_values as usize)?; + if leaf_validity + .as_ref() + .is_some_and(|validity| validity.count_set_bits() == 0) + || matches!(ctx.field.data_type(), DataType::Struct(fields) if fields.is_empty()) + { + return Ok(PrimitiveEncodeAttempt::Unhandled(page)); + } + let Some(scalar) = + PrimitiveStructuralEncoder::find_constant_scalar(&page.arrays, leaf_validity.as_ref())? + else { + return Ok(PrimitiveEncodeAttempt::Unhandled(page)); + }; + let PrimitivePageData { + structure: PrimitivePageStructure::Dense { repdef, .. }, + row_number, + num_rows, + .. + } = page + else { + unreachable!() + }; + log::debug!( + "Encoding column {} with {} items ({} rows) using constant layout", + ctx.column_idx, + num_values, + num_rows + ); + Ok(PrimitiveEncodeAttempt::Encoded( + constant::encode_constant_page(ctx.column_idx, scalar, repdef, row_number, num_rows)?, + )) + } +} + impl FieldEncoder for PrimitiveStructuralEncoder { // Buffers data, if there is enough to write a page then we create an encode task fn maybe_encode( @@ -5920,10 +6249,10 @@ impl FieldEncoder for PrimitiveStructuralEncoder { mod tests { use super::{ ChunkInstructions, DataBlock, DecodeMiniBlockTask, FixedPerValueDecompressor, - FixedWidthDataBlock, FullZipCacheableState, FullZipDecodeDetails, FullZipReadSource, - FullZipRepIndexDetails, FullZipScheduler, MiniBlockChunk, MiniBlockChunkIndex, - MiniBlockCompressed, PerValueDecompressor, PreambleAction, StructuralPageScheduler, - VariableFullZipDecoder, + FixedWidthDataBlock, FixedWidthDictionaryEncoding, FullZipCacheableState, + FullZipDecodeDetails, FullZipReadSource, FullZipRepIndexDetails, FullZipScheduler, + MiniBlockChunk, MiniBlockChunkIndex, MiniBlockCompressed, MiniblockChunkSize, + PerValueDecompressor, PreambleAction, StructuralPageScheduler, VariableFullZipDecoder, }; use crate::buffer::LanceBuffer; use crate::compression::DefaultDecompressionStrategy; @@ -5941,8 +6270,8 @@ mod tests { use crate::format::pb21; use crate::format::pb21::compressive_encoding::Compression; use crate::repdef::build_control_word_iterator; + use crate::testing::TestEncoding; use crate::testing::{TestCases, check_round_trip_encoding_of_data}; - use crate::version::LanceFileVersion; use arrow_array::{ArrayRef, Int8Array, StringArray}; use arrow_schema::{DataType, Field as ArrowField}; use std::collections::HashMap; @@ -7260,7 +7589,7 @@ mod tests { ); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) + .with_structural_encodings() .with_batch_size(100) .with_range(0..num_rows.min(500) as u64) .with_indices(vec![0, num_rows as u64 / 2, (num_rows - 1) as u64]); @@ -7271,7 +7600,7 @@ mod tests { async fn test_minichunk_size_helper( string_data: Vec>, minichunk_size: u64, - file_version: LanceFileVersion, + encodings: &[TestEncoding], ) { use crate::constants::MINICHUNK_SIZE_META_KEY; use crate::testing::{TestCases, check_round_trip_encoding_of_data}; @@ -7291,7 +7620,7 @@ mod tests { ); let test_cases = TestCases::default() - .with_min_file_version(file_version) + .with_encodings(encodings.iter().copied()) .with_batch_size(1000); check_round_trip_encoding_of_data(vec![string_array], &test_cases, metadata).await; @@ -7305,7 +7634,16 @@ mod tests { string_data.push(Some(format!("test_string_{}", i).repeat(50))); } // configure minichunk size to 64 bytes (smaller than the default 4kb) for Lance 2.1 - test_minichunk_size_helper(string_data, 64, LanceFileVersion::V2_1).await; + test_minichunk_size_helper( + string_data, + 64, + &[ + TestEncoding::StructuralU16, + TestEncoding::StructuralU32, + TestEncoding::StructuralSparse, + ], + ) + .await; } #[tokio::test] @@ -7316,7 +7654,12 @@ mod tests { for i in 0..10000 { string_data.push(Some(format!("test_string_{}", i).repeat(50))); } - test_minichunk_size_helper(string_data, 128 * 1024, LanceFileVersion::V2_2).await; + test_minichunk_size_helper( + string_data, + 128 * 1024, + &[TestEncoding::StructuralU32, TestEncoding::StructuralSparse], + ) + .await; } #[tokio::test] @@ -7326,7 +7669,12 @@ mod tests { for i in 0..10000 { string_data.push(Some(format!("t_{}", i))); } - test_minichunk_size_helper(string_data, 128 * 1024, LanceFileVersion::V2_2).await; + test_minichunk_size_helper( + string_data, + 128 * 1024, + &[TestEncoding::StructuralU32, TestEncoding::StructuralSparse], + ) + .await; } #[tokio::test] @@ -7353,7 +7701,7 @@ mod tests { // Configure test to use V2_2 and verify encoding let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) + .with_u32_structural_encodings() .with_verify_encoding(Arc::new(|cols: &[crate::encoder::EncodedColumn], _| { assert_eq!(cols.len(), 1); let col = &cols[0]; @@ -7423,7 +7771,7 @@ mod tests { ) .with_metadata(metadata); - encode_first_page(field, dict_array, LanceFileVersion::V2_2).await + encode_first_page(field, dict_array, TestEncoding::StructuralU32).await } async fn encode_auto_fixed_dict_page( @@ -7453,7 +7801,7 @@ mod tests { let field = arrow_schema::Field::new("fixed_col", DataType::Decimal128(38, 0), false) .with_metadata(field_metadata); - encode_first_page(field, decimal, LanceFileVersion::V2_2).await + encode_first_page(field, decimal, TestEncoding::StructuralU32).await } #[tokio::test] @@ -7580,7 +7928,6 @@ mod tests { async fn test_dictionary_encode_int64() { use crate::constants::{DICT_SIZE_RATIO_META_KEY, STRUCTURAL_ENCODING_META_KEY}; use crate::testing::{TestCases, check_round_trip_encoding_of_data}; - use crate::version::LanceFileVersion; use arrow_array::{ArrayRef, Int64Array}; use std::collections::HashMap; use std::sync::Arc; @@ -7603,7 +7950,7 @@ mod tests { metadata.insert(DICT_SIZE_RATIO_META_KEY.to_string(), "0.99".to_string()); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) + .with_u32_structural_encodings() .with_batch_size(1000) .with_range(0..1000) .with_indices(vec![0, 1, 10, 999]) @@ -7616,7 +7963,6 @@ mod tests { async fn test_dictionary_encode_float64() { use crate::constants::{DICT_SIZE_RATIO_META_KEY, STRUCTURAL_ENCODING_META_KEY}; use crate::testing::{TestCases, check_round_trip_encoding_of_data}; - use crate::version::LanceFileVersion; use arrow_array::{ArrayRef, Float64Array}; use std::collections::HashMap; use std::sync::Arc; @@ -7639,7 +7985,7 @@ mod tests { metadata.insert(DICT_SIZE_RATIO_META_KEY.to_string(), "0.99".to_string()); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) + .with_u32_structural_encodings() .with_batch_size(1000) .with_range(0..1000) .with_indices(vec![0, 1, 10, 999]) @@ -7774,7 +8120,7 @@ mod tests { let result = PrimitiveStructuralEncoder::should_dictionary_encode( &block, &field, - LanceFileVersion::V2_1, + FixedWidthDictionaryEncoding::Exclude64Bit, ); assert!( @@ -7821,7 +8167,7 @@ mod tests { let result = PrimitiveStructuralEncoder::should_dictionary_encode( &block, &field, - LanceFileVersion::V2_2, + FixedWidthDictionaryEncoding::Include64Bit, ); assert!( @@ -7848,7 +8194,7 @@ mod tests { let result = PrimitiveStructuralEncoder::should_dictionary_encode( &block, &field, - LanceFileVersion::V2_2, + FixedWidthDictionaryEncoding::Include64Bit, ); assert!( @@ -7866,7 +8212,7 @@ mod tests { let field = arrow_schema::Field::new("test", DataType::Utf8, false).with_metadata(metadata); let array = create_sorted_string_array(200_000, 8_000); - let page = encode_first_page(field, array, LanceFileVersion::V2_2).await; + let page = encode_first_page(field, array, TestEncoding::StructuralU32).await; let _ = dictionary_encoding_from_page(&page); } @@ -7886,7 +8232,7 @@ mod tests { let result = PrimitiveStructuralEncoder::should_dictionary_encode( &block, &field, - LanceFileVersion::V2_1, + FixedWidthDictionaryEncoding::Exclude64Bit, ); assert!( @@ -7912,7 +8258,7 @@ mod tests { let result = PrimitiveStructuralEncoder::should_dictionary_encode( &block, &field, - LanceFileVersion::V2_1, + FixedWidthDictionaryEncoding::Exclude64Bit, ); assert!( @@ -7938,9 +8284,13 @@ mod tests { num_values: 32_769, }; - let serialized = - PrimitiveStructuralEncoder::serialize_miniblocks(miniblocks, None, None, false) - .unwrap(); + let serialized = PrimitiveStructuralEncoder::serialize_miniblocks( + miniblocks, + None, + None, + MiniblockChunkSize::U16, + ) + .unwrap(); let chunk_metadata = serialized.metadata.borrow_to_typed_slice::(); assert_eq!(chunk_metadata.len(), 2); @@ -7954,33 +8304,33 @@ mod tests { async fn encode_first_page( field: arrow_schema::Field, array: ArrayRef, - version: LanceFileVersion, + version: TestEncoding, ) -> crate::encoder::EncodedPage { - use crate::encoder::{ - ColumnIndexSequence, EncodingOptions, MIN_PAGE_BUFFER_ALIGNMENT, OutOfLineBuffers, - default_encoding_strategy, - }; use crate::repdef::RepDefBuilder; + use crate::{ + encoder::{ + ColumnIndexSequence, EncodingOptions, MIN_PAGE_BUFFER_ALIGNMENT, OutOfLineBuffers, + }, + testing::{create_test_field_encoder, test_encoding_strategy}, + }; let lance_field = lance_core::datatypes::Field::try_from(&field).unwrap(); - let encoding_strategy = default_encoding_strategy(version); + let encoding_strategy = test_encoding_strategy(version); let mut column_index_seq = ColumnIndexSequence::default(); let encoding_options = EncodingOptions { cache_bytes_per_column: 1, max_page_bytes: 32 * 1024 * 1024, keep_original_array: true, buffer_alignment: MIN_PAGE_BUFFER_ALIGNMENT, - version, }; - let mut encoder = encoding_strategy - .create_field_encoder( - encoding_strategy.as_ref(), - &lance_field, - &mut column_index_seq, - &encoding_options, - ) - .unwrap(); + let mut encoder = create_test_field_encoder( + encoding_strategy.as_ref(), + &lance_field, + &mut column_index_seq, + &encoding_options, + ) + .unwrap(); let mut external_buffers = OutOfLineBuffers::new(0, MIN_PAGE_BUFFER_ALIGNMENT); let repdef = RepDefBuilder::default(); @@ -8011,7 +8361,7 @@ mod tests { .unwrap(), ); let field = arrow_schema::Field::new("c", DataType::FixedSizeBinary(33), true); - let page = encode_first_page(field, arr.clone(), LanceFileVersion::V2_2).await; + let page = encode_first_page(field, arr.clone(), TestEncoding::StructuralU32).await; let PageEncoding::Structural(layout) = &page.description else { panic!("Expected structural encoding"); @@ -8023,8 +8373,7 @@ mod tests { assert_eq!(page.data.len(), 1); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2) + .with_encoding(TestEncoding::StructuralU32) .with_page_sizes(vec![4096]); check_round_trip_encoding_of_data(vec![arr], &test_cases, HashMap::new()).await; } @@ -8037,7 +8386,7 @@ mod tests { std::iter::repeat_n("hello", 512), )); let field = arrow_schema::Field::new("c", DataType::Utf8, true); - let page = encode_first_page(field, arr.clone(), LanceFileVersion::V2_2).await; + let page = encode_first_page(field, arr.clone(), TestEncoding::StructuralU32).await; let PageEncoding::Structural(layout) = &page.description else { panic!("Expected structural encoding"); @@ -8049,8 +8398,7 @@ mod tests { assert_eq!(page.data.len(), 1); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2) + .with_encoding(TestEncoding::StructuralU32) .with_page_sizes(vec![4096]); check_round_trip_encoding_of_data(vec![arr], &test_cases, HashMap::new()).await; } @@ -8067,7 +8415,7 @@ mod tests { Some(7), ])); let field = arrow_schema::Field::new("c", DataType::Int32, true); - let page = encode_first_page(field, arr.clone(), LanceFileVersion::V2_2).await; + let page = encode_first_page(field, arr.clone(), TestEncoding::StructuralU32).await; let PageEncoding::Structural(layout) = &page.description else { panic!("Expected structural encoding"); @@ -8079,8 +8427,7 @@ mod tests { assert_eq!(page.data.len(), 2); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2) + .with_encoding(TestEncoding::StructuralU32) .with_page_sizes(vec![4096]); check_round_trip_encoding_of_data(vec![arr], &test_cases, HashMap::new()).await; } @@ -8113,7 +8460,7 @@ mod tests { ))), true, ); - let page = encode_first_page(field, arr.clone(), LanceFileVersion::V2_2).await; + let page = encode_first_page(field, arr.clone(), TestEncoding::StructuralU32).await; let PageEncoding::Structural(layout) = &page.description else { panic!("Expected structural encoding"); @@ -8125,8 +8472,7 @@ mod tests { assert_eq!(page.data.len(), 2); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2) + .with_encoding(TestEncoding::StructuralU32) .with_page_sizes(vec![4096]); check_round_trip_encoding_of_data(vec![arr], &test_cases, HashMap::new()).await; } @@ -8152,7 +8498,7 @@ mod tests { ), true, ); - let page = encode_first_page(field, arr.clone(), LanceFileVersion::V2_2).await; + let page = encode_first_page(field, arr.clone(), TestEncoding::StructuralU32).await; if let PageEncoding::Structural(layout) = &page.description { assert!( @@ -8162,8 +8508,7 @@ mod tests { } let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2) + .with_encoding(TestEncoding::StructuralU32) .with_page_sizes(vec![4096]); check_round_trip_encoding_of_data(vec![arr], &test_cases, HashMap::new()).await; } @@ -8174,7 +8519,7 @@ mod tests { let arr: ArrayRef = Arc::new(arrow_array::Int32Array::from(vec![7; 1024])); let field = arrow_schema::Field::new("c", DataType::Int32, true); - let page = encode_first_page(field, arr.clone(), LanceFileVersion::V2_1).await; + let page = encode_first_page(field, arr.clone(), TestEncoding::StructuralU16).await; let PageEncoding::Structural(layout) = &page.description else { return; @@ -8185,8 +8530,7 @@ mod tests { ); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) - .with_max_file_version(LanceFileVersion::V2_1) + .with_encoding(TestEncoding::StructuralU16) .with_page_sizes(vec![4096]); check_round_trip_encoding_of_data(vec![arr], &test_cases, HashMap::new()).await; } @@ -8197,7 +8541,7 @@ mod tests { let arr: ArrayRef = Arc::new(arrow_array::Int32Array::from(vec![None, None, None])); let field = arrow_schema::Field::new("c", DataType::Int32, true); - let page = encode_first_page(field, arr.clone(), LanceFileVersion::V2_2).await; + let page = encode_first_page(field, arr.clone(), TestEncoding::StructuralU32).await; let PageEncoding::Structural(layout) = &page.description else { panic!("Expected structural encoding"); @@ -8209,26 +8553,26 @@ mod tests { assert_eq!(page.data.len(), 0); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_2) + .with_encoding(TestEncoding::StructuralU32) .with_page_sizes(vec![4096]); check_round_trip_encoding_of_data(vec![arr], &test_cases, HashMap::new()).await; } #[test] fn test_encode_decode_complex_all_null_vals_roundtrip() { - use crate::compression::{ - DecompressionStrategy, DefaultCompressionStrategy, DefaultDecompressionStrategy, - }; + use crate::compression::{DecompressionStrategy, DefaultDecompressionStrategy}; let values: Arc<[u16]> = Arc::from((0..2048).map(|i| (i % 5) as u16).collect::>()); - let compression_strategy = DefaultCompressionStrategy::default(); + let compression_strategy = crate::testing::test_compression_strategy( + TestEncoding::StructuralU16, + crate::compression_config::CompressionParams::default(), + ); let decompression_strategy = DefaultDecompressionStrategy::default(); let (compressed_buf, encoding) = PrimitiveStructuralEncoder::encode_complex_all_null_vals( &values, - &compression_strategy, + compression_strategy.as_ref(), ) .unwrap(); @@ -8264,7 +8608,8 @@ mod tests { true, ); - let page_v21 = encode_first_page(field.clone(), arr.clone(), LanceFileVersion::V2_1).await; + let page_v21 = + encode_first_page(field.clone(), arr.clone(), TestEncoding::StructuralU16).await; let PageEncoding::Structural(layout_v21) = &page_v21.description else { panic!("Expected structural encoding"); }; @@ -8276,7 +8621,7 @@ mod tests { assert_eq!(layout_v21.num_rep_values, 0); assert_eq!(layout_v21.num_def_values, 0); - let page_v22 = encode_first_page(field, arr, LanceFileVersion::V2_2).await; + let page_v22 = encode_first_page(field, arr, TestEncoding::StructuralU32).await; let PageEncoding::Structural(layout_v22) = &page_v22.description else { panic!("Expected structural encoding"); }; @@ -8295,7 +8640,7 @@ mod tests { (0..1000).map(|i| if i % 2 == 0 { None } else { Some(vec![]) }), ); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_2); + let test_cases = TestCases::default().with_u32_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, HashMap::new()) .await; } @@ -8317,7 +8662,7 @@ mod tests { } let list_array = Arc::new(list_builder.finish()); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await; } } diff --git a/rust/lance-encoding/src/encodings/logical/primitive/layout.rs b/rust/lance-encoding/src/encodings/logical/primitive/layout.rs index f9749219ea6..95df12ede6b 100644 --- a/rust/lance-encoding/src/encodings/logical/primitive/layout.rs +++ b/rust/lance-encoding/src/encodings/logical/primitive/layout.rs @@ -3,17 +3,15 @@ use lance_core::Result; -use crate::{repdef::MiniBlockRepDefBudget, version::LanceFileVersion}; +use crate::repdef::MiniBlockRepDefBudget; /// Runs automatic sparse planning only after the dense mini-block budget makes it useful. pub(super) fn select_automatic_sparse( - version: LanceFileVersion, requested_encoding: Option<&str>, dense_budget: &MiniBlockRepDefBudget, candidate: impl FnOnce() -> Result>, ) -> Result> { - if version < LanceFileVersion::V2_3 - || requested_encoding.is_some() + if requested_encoding.is_some() || !matches!( dense_budget, MiniBlockRepDefBudget::RequiresPageSplit(_) @@ -34,32 +32,25 @@ mod tests { #[test] fn within_budget_does_not_construct_sparse_candidate() { - let selected = select_automatic_sparse::<()>( - LanceFileVersion::V2_3, - None, - &MiniBlockRepDefBudget::WithinBudget, - || panic!("within-budget pages must not construct sparse candidates"), - ) - .unwrap(); + let selected = + select_automatic_sparse::<()>(None, &MiniBlockRepDefBudget::WithinBudget, || { + panic!("within-budget pages must not construct sparse candidates") + }) + .unwrap(); assert!(selected.is_none()); } #[test] fn over_budget_selects_only_eligible_candidates() { let split = MiniBlockRepDefBudget::RequiresPageSplit(Vec::new()); - let selected = - select_automatic_sparse(LanceFileVersion::V2_3, None, &split, || Ok(Some(42))).unwrap(); + let selected = select_automatic_sparse(None, &split, || Ok(Some(42))).unwrap(); assert_eq!(selected, Some(42)); - let ineligible = - select_automatic_sparse::<()>(LanceFileVersion::V2_3, None, &split, || Ok(None)) - .unwrap(); + let ineligible = select_automatic_sparse::<()>(None, &split, || Ok(None)).unwrap(); assert!(ineligible.is_none()); let unsplittable = MiniBlockRepDefBudget::SingleRowOverBudget(70_000); - let selected = - select_automatic_sparse(LanceFileVersion::V2_3, None, &unsplittable, || Ok(Some(7))) - .unwrap(); + let selected = select_automatic_sparse(None, &unsplittable, || Ok(Some(7))).unwrap(); assert_eq!(selected, Some(7)); } @@ -71,20 +62,11 @@ mod tests { STRUCTURAL_ENCODING_FULLZIP, STRUCTURAL_ENCODING_SPARSE, ] { - let selected = select_automatic_sparse::<()>( - LanceFileVersion::V2_3, - Some(requested), - &split, - || panic!("explicit modes must not invoke automatic sparse planning"), - ) + let selected = select_automatic_sparse::<()>(Some(requested), &split, || { + panic!("explicit modes must not invoke automatic sparse planning") + }) .unwrap(); assert!(selected.is_none()); } - - let selected = select_automatic_sparse::<()>(LanceFileVersion::V2_2, None, &split, || { - panic!("Lance 2.2 must not invoke automatic sparse planning") - }) - .unwrap(); - assert!(selected.is_none()); } } diff --git a/rust/lance-encoding/src/encodings/logical/primitive/sparse/writer.rs b/rust/lance-encoding/src/encodings/logical/primitive/sparse/writer.rs index 3d82b4a5ef6..4dc13fa8aac 100644 --- a/rust/lance-encoding/src/encodings/logical/primitive/sparse/writer.rs +++ b/rust/lance-encoding/src/encodings/logical/primitive/sparse/writer.rs @@ -19,6 +19,8 @@ use crate::{ statistics::ComputeStat, }; +use super::super::MiniblockChunkSize; + use super::{ SparseCountSet, SparsePositionSet, SparseStructuralLayerPlan, SparseStructuralPlan, SparseValidityMeaning, SparseValiditySet, @@ -438,7 +440,7 @@ fn with_explicit_value_counts( fn serialize_value_chunks( compressed: SparseMiniBlockCompressed, - support_large_chunk: bool, + miniblock_chunk_size: MiniblockChunkSize, ) -> Result { let bytes_data = compressed.data.iter().map(LanceBuffer::len).sum::(); let num_buffers = compressed.data.len(); @@ -457,7 +459,7 @@ fn serialize_value_chunks( let chunk_start = data_buffer.len(); debug_assert_eq!(chunk_start % MINIBLOCK_ALIGNMENT, 0); data_buffer.extend_from_slice(&0_u16.to_le_bytes()); - if support_large_chunk { + if miniblock_chunk_size == MiniblockChunkSize::U32 { for buffer_size in &chunk.buffer_sizes { data_buffer.extend_from_slice(&buffer_size.to_le_bytes()); } @@ -540,7 +542,7 @@ pub fn prepare_values( field: &Field, compression_strategy: &dyn CompressionStrategy, data: DataBlock, - support_large_chunk: bool, + miniblock_chunk_size: MiniblockChunkSize, ) -> Result { match &data { DataBlock::AllNull(_) => { @@ -564,8 +566,10 @@ pub fn prepare_values( let num_values = data.num_values(); let compressor = compression_strategy.create_miniblock_compressor(field, &data)?; let (compressed, value_compression) = compressor.compress(data)?; - let values = - serialize_value_chunks(with_explicit_value_counts(compressed)?, support_large_chunk)?; + let values = serialize_value_chunks( + with_explicit_value_counts(compressed)?, + miniblock_chunk_size, + )?; Ok(PreparedSparseValues { num_values, value_compression, @@ -825,7 +829,7 @@ pub(in crate::encodings::logical::primitive) fn encode_page( plan: SparseStructuralPlan, row_number: u64, num_rows: u64, - support_large_chunk: bool, + miniblock_chunk_size: MiniblockChunkSize, ) -> Result { let PreparedSparseValues { num_values, @@ -833,7 +837,7 @@ pub(in crate::encodings::logical::primitive) fn encode_page( values, } = match values { SparseValueInput::Unprepared(data) => { - prepare_values(field, compression_strategy, data, support_large_chunk)? + prepare_values(field, compression_strategy, data, miniblock_chunk_size)? } SparseValueInput::Prepared(prepared) => prepared, }; @@ -851,7 +855,7 @@ pub(in crate::encodings::logical::primitive) fn encode_page( num_buffers: values.num_buffers, num_items: plan.num_items, num_visible_items: plan.num_visible_items, - has_large_chunk: support_large_chunk, + has_large_chunk: miniblock_chunk_size == MiniblockChunkSize::U32, structural_layers: structural.layers, }, )), @@ -891,10 +895,11 @@ mod tests { data::FixedSizeListBlock, encoder::{ ColumnIndexSequence, EncodingOptions, FieldEncoder, MIN_PAGE_BUFFER_ALIGNMENT, - OutOfLineBuffers, default_encoding_strategy, + OutOfLineBuffers, + }, + testing::{ + TestCases, TestEncoding, check_round_trip_encoding_of_data, test_encoding_strategy, }, - testing::{TestCases, check_round_trip_encoding_of_data}, - version::LanceFileVersion, }; use super::*; @@ -1196,19 +1201,18 @@ mod tests { fn create_encoder( array: &ArrayRef, - version: LanceFileVersion, + version: TestEncoding, metadata: HashMap, ) -> Result> { let arrow_field = ArrowField::new("values", array.data_type().clone(), true).with_metadata(metadata); let field = Field::try_from(&arrow_field)?; - let strategy = default_encoding_strategy(version); + let strategy = test_encoding_strategy(version); let options = EncodingOptions { cache_bytes_per_column: 1, - version, ..Default::default() }; - strategy.create_field_encoder( + crate::testing::create_test_field_encoder( strategy.as_ref(), &field, &mut ColumnIndexSequence::default(), @@ -1218,7 +1222,7 @@ mod tests { async fn encode_pages( array: ArrayRef, - version: LanceFileVersion, + version: TestEncoding, metadata: HashMap, ) -> Result> { encode_chunks(vec![array], version, metadata).await @@ -1226,7 +1230,7 @@ mod tests { async fn encode_chunks( arrays: Vec, - version: LanceFileVersion, + version: TestEncoding, metadata: HashMap, ) -> Result> { let first = arrays @@ -1474,9 +1478,13 @@ mod tests { None, Some(40), ])) as ArrayRef; - let pages = encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let pages = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert_eq!(pages.len(), 1); let sparse = sparse_layout(&pages[0]); assert_eq!(sparse.num_items, 6); @@ -1497,8 +1505,7 @@ mod tests { )); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..5) .with_indices(vec![0, 2, 5]); @@ -1508,9 +1515,13 @@ mod tests { #[tokio::test] async fn test_explicit_sparse_nullable_struct_roundtrip() { let array = nullable_struct(); - let pages = encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let pages = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert_eq!(pages.len(), 1); let sparse = sparse_layout(&pages[0]); assert_eq!(sparse.structural_layers.len(), 2); @@ -1535,8 +1546,7 @@ mod tests { )); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..5) .with_indices(vec![0, 2, 4]); @@ -1563,9 +1573,13 @@ mod tests { ], Some(null_buffer([true, false, true, true, false])), )) as ArrayRef; - let pages = encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let pages = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert!(pages.iter().any(|page| matches!( page_layout(page), pb21::page_layout::Layout::ConstantLayout(_) @@ -1576,8 +1590,7 @@ mod tests { ))); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..5) .with_indices(vec![0, 2, 4]); @@ -1605,7 +1618,7 @@ mod tests { let null_positions = encode_pages( mostly_valid.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, sparse_metadata(), ) .await @@ -1622,7 +1635,7 @@ mod tests { let valid_positions = encode_pages( mostly_null.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, sparse_metadata(), ) .await @@ -1638,8 +1651,7 @@ mod tests { ); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..5) .with_indices(vec![0, 2, 5]); @@ -1652,9 +1664,13 @@ mod tests { async fn test_explicit_sparse_nested_page_boundaries_range_and_take() { let nested = deeply_nested(); let chunks = vec![nested.slice(0, 2), nested.slice(2, 3)]; - let pages = encode_chunks(chunks.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let pages = encode_chunks( + chunks.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert!(pages.len() >= 2, "expected multiple sparse pages"); let sparse = pages .iter() @@ -1681,8 +1697,7 @@ mod tests { })); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_batch_size(2) .with_range(1..5) @@ -1703,8 +1718,7 @@ mod tests { Some(vec![false, true, true, true, true]), ); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(0..5) .with_range(1..4) @@ -1712,9 +1726,13 @@ mod tests { .with_indices(vec![2, 3]); for array in [list, large_list] { - let pages = encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let pages = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert!(pages.iter().map(sparse_layout).all(|layout| { layout.structural_layers.iter().any(|layer| { matches!( @@ -1730,9 +1748,13 @@ mod tests { #[tokio::test] async fn test_explicit_sparse_map_and_fixed_size_list_roundtrip() { let map = map_i32(); - let map_pages = encode_pages(map.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let map_pages = encode_pages( + map.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert!(map_pages.iter().map(sparse_layout).any(|layout| { layout.structural_layers.iter().any(|layer| { matches!( @@ -1742,17 +1764,20 @@ mod tests { }) })); let map_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..4) .with_indices(vec![0, 2, 3]); check_round_trip_encoding_of_data(vec![map], &map_cases, sparse_metadata()).await; let fsl = fixed_size_list_struct(); - let fsl_pages = encode_pages(fsl.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let fsl_pages = encode_pages( + fsl.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); for page in &fsl_pages { let layout = sparse_layout(page); let outer_slots = layer_num_slots(layout.structural_layers.first().unwrap()); @@ -1770,8 +1795,7 @@ mod tests { .any(|layer| fixed_size_list_dimension(layer) == Some(2)) })); let fsl_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..6) .with_indices(vec![0, 3, 5]); @@ -1781,9 +1805,13 @@ mod tests { #[tokio::test] async fn test_explicit_sparse_list_fixed_size_list_struct_roundtrip() { let array = list_fixed_size_list_struct(); - let pages = encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let pages = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert!(pages.iter().map(sparse_layout).any(|layout| { let kinds = layout .structural_layers @@ -1803,8 +1831,7 @@ mod tests { } let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..4) .with_indices(vec![0, 2, 3]) @@ -1815,9 +1842,13 @@ mod tests { #[tokio::test] async fn test_explicit_sparse_serializes_semantic_list_forms() { let all_array = list_i32(vec![0, 2, 4, 6], None); - let all = encode_pages(all_array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let all = encode_pages( + all_array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); let all_layer = list_layer(sparse_layout(&all[0])); assert!(matches!( all_layer.non_empty_positions.as_ref().unwrap().positions, @@ -1834,7 +1865,7 @@ mod tests { let range_array = list_i32(vec![0, 2, 4, 4, 4], None); let range = encode_pages( range_array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, sparse_metadata(), ) .await @@ -1860,7 +1891,7 @@ mod tests { let explicit_array = list_i32(vec![0, 1, 1, 4, 4, 6], None); let explicit = encode_pages( explicit_array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, sparse_metadata(), ) .await @@ -1881,8 +1912,7 @@ mod tests { assert_eq!(explicit[0].data.len(), 4); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1]) .with_range(1..3) .with_indices(vec![0, 2]); @@ -1895,7 +1925,7 @@ mod tests { async fn test_constant_layout_boundary_is_explicit() { let structural_only = encode_pages( list_i32(vec![0, 0, 0, 0], None), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, sparse_metadata(), ) .await @@ -1906,28 +1936,33 @@ mod tests { )); let empty_struct = Arc::new(StructArray::new_empty_fields(3, None)) as ArrayRef; - let empty_struct_pages = - encode_pages(empty_struct, LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let empty_struct_pages = encode_pages( + empty_struct, + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert!(matches!( page_layout(&empty_struct_pages[0]), pb21::page_layout::Layout::ConstantLayout(_) )); let all_null = Arc::new(Int32Array::from(vec![None, None, None])) as ArrayRef; - let all_null_pages = encode_pages(all_null, LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let all_null_pages = + encode_pages(all_null, TestEncoding::StructuralSparse, sparse_metadata()) + .await + .unwrap(); assert!(matches!( page_layout(&all_null_pages[0]), pb21::page_layout::Layout::ConstantLayout(_) )); let constant = Arc::new(Int32Array::from(vec![7, 7, 7])) as ArrayRef; - let constant_pages = encode_pages(constant, LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let constant_pages = + encode_pages(constant, TestEncoding::StructuralSparse, sparse_metadata()) + .await + .unwrap(); assert!(matches!( page_layout(&constant_pages[0]), pb21::page_layout::Layout::SparseLayout(_) @@ -1937,12 +1972,12 @@ mod tests { #[tokio::test] async fn test_default_and_explicit_dense_layouts_are_unchanged() { let array = Arc::new(Int32Array::from_iter_values(0..16)) as ArrayRef; - let v2_2_default = encode_pages(array.clone(), LanceFileVersion::V2_2, HashMap::new()) + let v2_2_default = encode_pages(array.clone(), TestEncoding::StructuralU32, HashMap::new()) .await .unwrap(); let v2_2_miniblock = encode_pages( array.clone(), - LanceFileVersion::V2_2, + TestEncoding::StructuralU32, structural_metadata(STRUCTURAL_ENCODING_MINIBLOCK), ) .await @@ -1957,9 +1992,13 @@ mod tests { assert_eq!(default.data, explicit.data); } - let v2_3_default = encode_pages(array.clone(), LanceFileVersion::V2_3, HashMap::new()) - .await - .unwrap(); + let v2_3_default = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + HashMap::new(), + ) + .await + .unwrap(); assert!(matches!( page_layout(&v2_3_default[0]), pb21::page_layout::Layout::MiniBlockLayout(_) @@ -1967,7 +2006,7 @@ mod tests { let v2_3_miniblock = encode_pages( array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, structural_metadata(STRUCTURAL_ENCODING_MINIBLOCK), ) .await @@ -1979,7 +2018,7 @@ mod tests { let v2_3_fullzip = encode_pages( array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, structural_metadata(STRUCTURAL_ENCODING_FULLZIP), ) .await @@ -1989,7 +2028,7 @@ mod tests { pb21::page_layout::Layout::FullZipLayout(_) )); - let v2_3_sparse = encode_pages(array, LanceFileVersion::V2_3, sparse_metadata()) + let v2_3_sparse = encode_pages(array, TestEncoding::StructuralSparse, sparse_metadata()) .await .unwrap(); assert!(matches!( @@ -2005,7 +2044,7 @@ mod tests { let within_budget = encode_pages( sparse_i32_list(4_096, 1_024), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, HashMap::new(), ) .await @@ -2016,9 +2055,13 @@ mod tests { pb21::page_layout::Layout::MiniBlockLayout(_) )); - let automatic = encode_pages(array.clone(), LanceFileVersion::V2_3, HashMap::new()) - .await - .unwrap(); + let automatic = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + HashMap::new(), + ) + .await + .unwrap(); assert_eq!(automatic.len(), 1); assert!(matches!( page_layout(&automatic[0]), @@ -2027,7 +2070,7 @@ mod tests { let miniblock = encode_pages( array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, structural_metadata(STRUCTURAL_ENCODING_MINIBLOCK), ) .await @@ -2040,7 +2083,7 @@ mod tests { let fullzip = encode_pages( array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, structural_metadata(STRUCTURAL_ENCODING_FULLZIP), ) .await @@ -2051,21 +2094,25 @@ mod tests { pb21::page_layout::Layout::FullZipLayout(_) ))); - let sparse = encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let sparse = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert_eq!(sparse.len(), 1); assert!(matches!( page_layout(&sparse[0]), pb21::page_layout::Layout::SparseLayout(_) )); - let v2_2_default = encode_pages(array.clone(), LanceFileVersion::V2_2, HashMap::new()) + let v2_2_default = encode_pages(array.clone(), TestEncoding::StructuralU32, HashMap::new()) .await .unwrap(); let v2_2_miniblock = encode_pages( array.clone(), - LanceFileVersion::V2_2, + TestEncoding::StructuralU32, structural_metadata(STRUCTURAL_ENCODING_MINIBLOCK), ) .await @@ -2077,8 +2124,7 @@ mod tests { } let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1024 * 1024]) .with_batch_size(NUM_ROWS as u32) .with_range(1_999..2_002) @@ -2093,16 +2139,20 @@ mod tests { Arc::new(ArrowField::new("item", DataType::Int32, true)), ); - let automatic = encode_pages(array.clone(), LanceFileVersion::V2_3, HashMap::new()) - .await - .unwrap(); + let automatic = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + HashMap::new(), + ) + .await + .unwrap(); assert_eq!(automatic.len(), 1); assert!(matches!( page_layout(&automatic[0]), pb21::page_layout::Layout::SparseLayout(_) )); - let v2_2 = encode_pages(array.clone(), LanceFileVersion::V2_2, HashMap::new()) + let v2_2 = encode_pages(array.clone(), TestEncoding::StructuralU32, HashMap::new()) .await .unwrap(); assert_eq!(v2_2.len(), 1); @@ -2113,7 +2163,7 @@ mod tests { let miniblock_error = encode_pages( array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, structural_metadata(STRUCTURAL_ENCODING_MINIBLOCK), ) .await @@ -2126,7 +2176,7 @@ mod tests { let fullzip = encode_pages( array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, structural_metadata(STRUCTURAL_ENCODING_FULLZIP), ) .await @@ -2136,18 +2186,20 @@ mod tests { pb21::page_layout::Layout::FullZipLayout(_) )); - let explicit_sparse = - encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap(); + let explicit_sparse = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap(); assert!(matches!( page_layout(&explicit_sparse[0]), pb21::page_layout::Layout::SparseLayout(_) )); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_range(0..1) .with_indices(vec![0]); check_round_trip_encoding_of_data(vec![array], &cases, HashMap::new()).await; @@ -2162,7 +2214,7 @@ mod tests { let dictionary_array = sparse_list_values(NUM_ROWS, 2_000, dictionary, dictionary_field); let dictionary_pages = encode_pages( dictionary_array.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, HashMap::new(), ) .await @@ -2175,10 +2227,13 @@ mod tests { let (packed_values, packed_field) = variable_packed_struct_values(num_values); let packed_array = sparse_list_values(NUM_ROWS, 2_000, packed_values, packed_field); - let packed_pages = - encode_pages(packed_array.clone(), LanceFileVersion::V2_3, HashMap::new()) - .await - .unwrap(); + let packed_pages = encode_pages( + packed_array.clone(), + TestEncoding::StructuralSparse, + HashMap::new(), + ) + .await + .unwrap(); assert!(packed_pages.len() > 1); assert!(packed_pages.iter().all(|page| matches!( page_layout(page), @@ -2189,7 +2244,7 @@ mod tests { let unsplittable_packed = unsplittable_nested_list(packed_values, packed_field); let packed_fallback = encode_pages( unsplittable_packed.clone(), - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, HashMap::new(), ) .await @@ -2204,14 +2259,14 @@ mod tests { let unsplittable_dictionary = unsplittable_nested_list(dictionary, dictionary_field); let v2_2_error = encode_pages( unsplittable_dictionary.clone(), - LanceFileVersion::V2_2, + TestEncoding::StructuralU32, HashMap::new(), ) .await .unwrap_err(); let v2_3_error = encode_pages( unsplittable_dictionary, - LanceFileVersion::V2_3, + TestEncoding::StructuralSparse, HashMap::new(), ) .await @@ -2224,23 +2279,20 @@ mod tests { ); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1024 * 1024]) .with_batch_size(NUM_ROWS as u32) .with_range(1_999..2_002) .with_indices(vec![0, 2_000, NUM_ROWS as u64 - 1]); check_round_trip_encoding_of_data(vec![dictionary_array], &cases, HashMap::new()).await; let packed_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1024 * 1024]) .with_batch_size(NUM_ROWS as u32); check_round_trip_encoding_of_data(vec![packed_array], &packed_cases, HashMap::new()).await; let single_row_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_3) - .with_max_file_version(LanceFileVersion::V2_3) + .with_encoding(TestEncoding::StructuralSparse) .with_page_sizes(vec![1024 * 1024]) .with_range(0..1) .with_indices(vec![0]); @@ -2282,12 +2334,16 @@ mod tests { let item_field = Arc::new(ArrowField::new("item", values.data_type().clone(), true)); let array = unsplittable_nested_list(values, item_field); - let v2_2 = encode_pages(array.clone(), LanceFileVersion::V2_2, HashMap::new()) - .await - .unwrap(); - let v2_3 = encode_pages(array.clone(), LanceFileVersion::V2_3, HashMap::new()) + let v2_2 = encode_pages(array.clone(), TestEncoding::StructuralU32, HashMap::new()) .await .unwrap(); + let v2_3 = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + HashMap::new(), + ) + .await + .unwrap(); for pages in [&v2_2, &v2_3] { assert_eq!(pages.len(), 1, "unexpected {label} page count"); assert!( @@ -2299,18 +2355,20 @@ mod tests { ); } - let explicit_error = - encode_pages(array.clone(), LanceFileVersion::V2_3, sparse_metadata()) - .await - .unwrap_err(); + let explicit_error = encode_pages( + array.clone(), + TestEncoding::StructuralSparse, + sparse_metadata(), + ) + .await + .unwrap_err(); assert!( explicit_error.to_string().contains("too wide"), "explicit sparse should preserve the {label} value error: {explicit_error}" ); let cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) - .with_max_file_version(LanceFileVersion::V2_3) + .with_u32_structural_encodings() .with_range(0..1) .with_indices(vec![0]); check_round_trip_encoding_of_data(vec![array], &cases, HashMap::new()).await; @@ -2320,25 +2378,28 @@ mod tests { #[test] fn test_explicit_sparse_rejects_lance_2_2() { let array = Arc::new(Int32Array::from(vec![Some(1), None])) as ArrayRef; - let Err(error) = create_encoder(&array, LanceFileVersion::V2_2, sparse_metadata()) else { + let Err(error) = create_encoder(&array, TestEncoding::StructuralU32, sparse_metadata()) + else { panic!("expected Lance 2.2 to reject explicit sparse encoding"); }; assert!( error .to_string() - .contains("requires Lance file format 2.3+") + .contains("not enabled by the selected file format") ); let structural_only = list_i32(vec![0, 0, 0], None); - let Err(error) = - create_encoder(&structural_only, LanceFileVersion::V2_2, sparse_metadata()) - else { + let Err(error) = create_encoder( + &structural_only, + TestEncoding::StructuralU32, + sparse_metadata(), + ) else { panic!("expected Lance 2.2 structural-only input to reject explicit sparse encoding"); }; assert!( error .to_string() - .contains("requires Lance file format 2.3+") + .contains("not enabled by the selected file format") ); } } diff --git a/rust/lance-encoding/src/encodings/logical/struct.rs b/rust/lance-encoding/src/encodings/logical/struct.rs index dc734da72b5..a038bcd2f48 100644 --- a/rust/lance-encoding/src/encodings/logical/struct.rs +++ b/rust/lance-encoding/src/encodings/logical/struct.rs @@ -600,7 +600,7 @@ impl FieldEncoder for StructFieldEncoder { let mut header = EncodedColumn::default(); header.final_pages.push(EncodedPage { data: Vec::new(), - description: PageEncoding::Legacy(pb::ArrayEncoding { + description: PageEncoding::Array(pb::ArrayEncoding { array_encoding: Some(pb::array_encoding::ArrayEncoding::Struct( pb::SimpleStruct {}, )), @@ -632,10 +632,7 @@ mod tests { use arrow_buffer::{BooleanBuffer, NullBuffer, OffsetBuffer, ScalarBuffer}; use arrow_schema::{DataType, Field, Fields}; - use crate::{ - testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data}, - version::LanceFileVersion, - }; + use crate::testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data}; #[test_log::test(tokio::test)] async fn test_simple_struct() { @@ -694,7 +691,7 @@ mod tests { Some(rows_validity), ); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(rows)], &test_cases, HashMap::new()).await; } @@ -724,7 +721,7 @@ mod tests { ); check_round_trip_encoding_of_data( vec![Arc::new(struct_array)], - &TestCases::default().with_min_file_version(LanceFileVersion::V2_1), + &TestCases::default().with_structural_encodings(), HashMap::new(), ) .await; @@ -755,7 +752,7 @@ mod tests { ); check_round_trip_encoding_of_data( vec![Arc::new(struct_array)], - &TestCases::default().with_min_file_version(LanceFileVersion::V2_1), + &TestCases::default().with_structural_encodings(), HashMap::new(), ) .await; @@ -834,7 +831,7 @@ mod tests { check_round_trip_encoding_of_data( vec![Arc::new(list_array)], - &TestCases::default().with_min_file_version(LanceFileVersion::V2_2), + &TestCases::default().with_u32_structural_encodings(), HashMap::new(), ) .await; @@ -868,7 +865,7 @@ mod tests { .with_range(1..2) .with_indices(vec![0]) .with_indices(vec![1]) - .with_min_file_version(LanceFileVersion::V2_1), + .with_structural_encodings(), HashMap::new(), ) .await; @@ -916,7 +913,7 @@ mod tests { check_round_trip_encoding_of_data( vec![Arc::new(row_array)], - &TestCases::default().with_min_file_version(LanceFileVersion::V2_1), + &TestCases::default().with_structural_encodings(), HashMap::new(), ) .await; diff --git a/rust/lance-encoding/src/encodings/physical/binary.rs b/rust/lance-encoding/src/encodings/physical/binary.rs index d02cf2da693..86ac2f69934 100644 --- a/rust/lance-encoding/src/encodings/physical/binary.rs +++ b/rust/lance-encoding/src/encodings/physical/binary.rs @@ -542,22 +542,14 @@ mod tests { use rstest::rstest; use std::{collections::HashMap, sync::Arc, vec}; - use crate::{ - testing::{ - FnArrayGeneratorProvider, TestCases, check_basic_random, - check_round_trip_encoding_of_data, - }, - version::LanceFileVersion, + use crate::testing::{ + FnArrayGeneratorProvider, TestCases, check_basic_random, check_round_trip_encoding_of_data, }; #[test_log::test(tokio::test)] async fn test_utf8_binary() { let field = Field::new("", DataType::Utf8, false); - check_specific_random( - field, - TestCases::basic().with_min_file_version(LanceFileVersion::V2_1), - ) - .await; + check_specific_random(field, TestCases::basic().with_structural_encodings()).await; } #[rstest] @@ -592,7 +584,7 @@ mod tests { field_metadata.insert(COMPRESSION_META_KEY.to_string(), "fsst".into()); let field = Field::new("", data_type, true).with_metadata(field_metadata); // TODO (https://github.com/lance-format/lance/issues/4783) - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_specific_random(field, test_cases).await; } @@ -610,11 +602,7 @@ mod tests { ); field_metadata.insert(COMPRESSION_META_KEY.to_string(), "fsst".into()); let field = Field::new("", data_type, true).with_metadata(field_metadata); - check_specific_random( - field, - TestCases::basic().with_min_file_version(LanceFileVersion::V2_1), - ) - .await; + check_specific_random(field, TestCases::basic().with_structural_encodings()).await; } #[test_log::test(tokio::test)] @@ -801,7 +789,7 @@ mod tests { #[values(true, false)] with_nulls: bool, #[values(100, 500, 35000)] dict_size: u32, ) { - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); let strings = (0..dict_size) .map(|i| i.to_string()) .collect::>(); @@ -829,7 +817,7 @@ mod tests { let test_cases = TestCases::default() .with_expected_encoding("variable") - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); // Test both automatic selection and explicit configuration // 1. Test automatic binary encoding selection (small strings that won't trigger FSST) diff --git a/rust/lance-encoding/src/encodings/physical/bitpacking.rs b/rust/lance-encoding/src/encodings/physical/bitpacking.rs index be0b747e7dc..e5406b3f6fa 100644 --- a/rust/lance-encoding/src/encodings/physical/bitpacking.rs +++ b/rust/lance-encoding/src/encodings/physical/bitpacking.rs @@ -545,7 +545,6 @@ mod test { compression::MiniBlockDecompressor, data::{BlockInfo, DataBlock, FixedWidthDataBlock}, testing::{TestCases, check_round_trip_encoding_of_data}, - version::LanceFileVersion, }; #[rstest] @@ -569,7 +568,7 @@ mod test { #[test_log::test(tokio::test)] async fn test_miniblock_bitpack() { - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); let arrays = vec![ Arc::new(Int8Array::from(vec![100; 1024])) as Arc, @@ -608,7 +607,7 @@ mod test { // Test bitpacking encoding verification with varied small values that should trigger bitpacking let test_cases = TestCases::default() .with_expected_encoding("inline_bitpacking") - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); // Generate data with varied small values to avoid RLE // Mix different values but keep them small to trigger bitpacking @@ -632,7 +631,7 @@ mod test { let test_cases = TestCases::default() .with_expected_encoding("inline_bitpacking") - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); // Build 2048 values: first 1024 all zeros (bit_width=0), // next 1024 small varied values to avoid RLE and trigger bitpacking. diff --git a/rust/lance-encoding/src/encodings/physical/block.rs b/rust/lance-encoding/src/encodings/physical/block.rs index a1f5bdb3fdd..188af50c8a0 100644 --- a/rust/lance-encoding/src/encodings/physical/block.rs +++ b/rust/lance-encoding/src/encodings/physical/block.rs @@ -46,9 +46,20 @@ pub struct CompressionConfig { } impl CompressionConfig { - pub(crate) fn new(scheme: CompressionScheme, level: Option) -> Self { + /// Create a compression configuration for an encoding mechanism. + pub fn new(scheme: CompressionScheme, level: Option) -> Self { Self { scheme, level } } + + /// Return the selected compression scheme. + pub fn scheme(&self) -> CompressionScheme { + self.scheme + } + + /// Return the optional compression level. + pub fn level(&self) -> Option { + self.level + } } impl Default for CompressionConfig { @@ -765,7 +776,6 @@ mod tests { }, encodings::physical::block::lz4::Lz4BufferCompressor, testing::{FnArrayGeneratorProvider, TestCases, check_round_trip_encoding_generated}, - version::LanceFileVersion, }; #[test] @@ -809,7 +819,7 @@ mod tests { // Need to use large pages as small pages might be too small to compress .with_page_sizes(vec![1024 * 1024]) .with_expected_encoding("zstd") - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); // Can't use the default random provider because random data isn't compressible // and we will fallback to uncompressed encoding diff --git a/rust/lance-encoding/src/encodings/physical/fsst.rs b/rust/lance-encoding/src/encodings/physical/fsst.rs index 8c1fe4141df..14e7e0c0ccc 100644 --- a/rust/lance-encoding/src/encodings/physical/fsst.rs +++ b/rust/lance-encoding/src/encodings/physical/fsst.rs @@ -377,16 +377,13 @@ mod tests { use lance_datagen::{ByteCount, RowCount}; - use crate::{ - testing::{TestCases, check_round_trip_encoding_of_data}, - version::LanceFileVersion, - }; + use crate::testing::{TestCases, check_round_trip_encoding_of_data}; #[test_log::test(tokio::test)] async fn test_fsst() { let test_cases = TestCases::default() .with_expected_encoding("fsst") - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); // Generate data suitable for FSST (large strings, total size > 32KB) let arr = lance_datagen::gen_batch() diff --git a/rust/lance-encoding/src/encodings/physical/packed.rs b/rust/lance-encoding/src/encodings/physical/packed.rs index ad2221dffed..6b6fa27ba2b 100644 --- a/rust/lance-encoding/src/encodings/physical/packed.rs +++ b/rust/lance-encoding/src/encodings/physical/packed.rs @@ -18,7 +18,7 @@ use lance_core::{Error, Result, datatypes::Field}; use crate::{ buffer::LanceBuffer, compression::{ - DefaultCompressionStrategy, FixedPerValueDecompressor, MiniBlockDecompressor, + CompressionStrategy, FixedPerValueDecompressor, MiniBlockDecompressor, VariablePerValueDecompressor, }, data::{ @@ -282,12 +282,12 @@ impl VariablePackedFieldData { #[derive(Debug)] pub struct PackedStructVariablePerValueEncoder { - strategy: DefaultCompressionStrategy, + strategy: Arc, fields: Vec, } impl PackedStructVariablePerValueEncoder { - pub fn new(strategy: DefaultCompressionStrategy, fields: Vec) -> Self { + pub fn new(strategy: Arc, fields: Vec) -> Self { Self { strategy, fields } } } @@ -324,11 +324,7 @@ impl PerValueCompressor for PackedStructVariablePerValueEncoder { let mut field_metadata = Vec::with_capacity(self.fields.len()); for (field, child_block) in self.fields.iter().zip(struct_block.children) { - let compressor = crate::compression::CompressionStrategy::create_per_value( - &self.strategy, - field, - &child_block, - )?; + let compressor = self.strategy.create_per_value(field, &child_block)?; let (compressed, encoding) = compressor.compress(child_block)?; match compressed { PerValueDataBlock::Fixed(block) => { @@ -758,12 +754,13 @@ impl VariablePerValueDecompressor for PackedStructVariablePerValueDecompressor { mod tests { use super::*; use crate::{ - compression::CompressionStrategy, - compression::{DefaultCompressionStrategy, DefaultDecompressionStrategy}, + compression::DefaultDecompressionStrategy, + compression_config::CompressionParams, constants::PACKED_STRUCT_META_KEY, statistics::ComputeStat, - testing::{TestCases, check_round_trip_encoding_of_data}, - version::LanceFileVersion, + testing::{ + TestCases, TestEncoding, check_round_trip_encoding_of_data, test_compression_strategy, + }, }; use arrow_array::{ Array, ArrayRef, BinaryArray, Int32Array, Int64Array, LargeStringArray, StringArray, @@ -863,9 +860,9 @@ mod tests { let data_block = DataBlock::Struct(struct_block); let compression_strategy = - DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_2); + test_compression_strategy(TestEncoding::StructuralU32, CompressionParams::default()); let compressor = crate::compression::CompressionStrategy::create_per_value( - &compression_strategy, + compression_strategy.as_ref(), &struct_field, &data_block, )?; @@ -931,9 +928,9 @@ mod tests { let data_block = DataBlock::Struct(struct_block); let compression_strategy = - DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_2); + test_compression_strategy(TestEncoding::StructuralU32, CompressionParams::default()); let compressor = crate::compression::CompressionStrategy::create_per_value( - &compression_strategy, + compression_strategy.as_ref(), &struct_field, &data_block, )?; @@ -1013,7 +1010,7 @@ mod tests { ])); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_2) + .with_u32_structural_encodings() .with_expected_encoding("variable_packed_struct"); check_round_trip_encoding_of_data(vec![array], &test_cases, meta).await; @@ -1052,9 +1049,9 @@ mod tests { let data_block = DataBlock::Struct(struct_block); let compression_strategy = - DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_2); + test_compression_strategy(TestEncoding::StructuralU32, CompressionParams::default()); let compressor = crate::compression::CompressionStrategy::create_per_value( - &compression_strategy, + compression_strategy.as_ref(), &struct_field, &data_block, )?; @@ -1135,7 +1132,7 @@ mod tests { }; let compression_strategy = - DefaultCompressionStrategy::new().with_version(LanceFileVersion::V2_1); + test_compression_strategy(TestEncoding::StructuralU16, CompressionParams::default()); let result = compression_strategy.create_per_value(&struct_field, &DataBlock::Struct(struct_block)); diff --git a/rust/lance-encoding/src/encodings/physical/rle.rs b/rust/lance-encoding/src/encodings/physical/rle.rs index 145b9c43779..ebafa4604f9 100644 --- a/rust/lance-encoding/src/encodings/physical/rle.rs +++ b/rust/lance-encoding/src/encodings/physical/rle.rs @@ -2585,7 +2585,6 @@ mod tests { #[test_log::test(tokio::test)] async fn test_rle_encoding_verification() { use crate::testing::{TestCases, check_round_trip_encoding_of_data}; - use crate::version::LanceFileVersion; use arrow_array::{Array, Int32Array}; use lance_datagen::{ArrayGenerator, RowCount}; use std::collections::HashMap; @@ -2593,7 +2592,7 @@ mod tests { let test_cases = TestCases::default() .with_expected_encoding("rle") - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); // Test both explicit metadata and automatic selection // 1. Test with explicit RLE threshold metadata (also disable BSS) diff --git a/rust/lance-encoding/src/encodings/physical/value.rs b/rust/lance-encoding/src/encodings/physical/value.rs index 606f49b699a..412ce236dfe 100644 --- a/rust/lance-encoding/src/encodings/physical/value.rs +++ b/rust/lance-encoding/src/encodings/physical/value.rs @@ -784,7 +784,6 @@ mod tests { FnArrayGeneratorProvider, TestCases, check_basic_random, check_round_trip_encoding_generated, check_round_trip_encoding_of_data, }, - version::LanceFileVersion, }; use super::ValueEncoder; @@ -834,7 +833,7 @@ mod tests { .with_indices(vec![0, 1, 2]) .with_indices(vec![1]) .with_indices(vec![2]) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(vec![items], &test_cases, HashMap::default()).await; } @@ -845,7 +844,7 @@ mod tests { (0..5000).map(|i| if i % 2 == 0 { Some(i) } else { None }), )); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![items], &test_cases, HashMap::default()).await; } @@ -877,7 +876,7 @@ mod tests { #[test_log::test(tokio::test)] async fn test_decimal128_dictionary_encoding() { - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); let decimals: Vec = (0..100).collect(); let repeated_strings: Vec<_> = decimals .iter() @@ -923,7 +922,7 @@ mod tests { let test_cases = TestCases::default() .with_page_sizes(vec![1000, 2000, 3000, 60000]) .with_batch_size(batch_size) - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); check_round_trip_encoding_of_data(data.clone(), &test_cases, HashMap::new()).await; } @@ -1092,7 +1091,7 @@ mod tests { let list_array = FixedSizeListArray::new(items_field, 2, items, Some(NullBuffer::new(list_nulls))); - let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1); + let test_cases = TestCases::default().with_structural_encodings(); check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, HashMap::new()) .await; @@ -1110,7 +1109,7 @@ mod tests { let list_arr = ListArray::new(list_field, OffsetBuffer::new(offsets), Arc::new(fsl), None); let test_cases = TestCases::default() - .with_min_file_version(LanceFileVersion::V2_1) + .with_structural_encodings() .with_batch_size(1); check_round_trip_encoding_of_data(vec![Arc::new(list_arr)], &test_cases, HashMap::new()) @@ -1217,7 +1216,7 @@ mod tests { let test_cases = TestCases::default() .with_expected_encoding("flat") - .with_min_file_version(LanceFileVersion::V2_1); + .with_structural_encodings(); // Test both explicit configuration and automatic fallback scenarios // 1. Test explicit "none" compression to force flat encoding diff --git a/rust/lance-encoding/src/lib.rs b/rust/lance-encoding/src/lib.rs index a58e0a14c59..b2f98393260 100644 --- a/rust/lance-encoding/src/lib.rs +++ b/rust/lance-encoding/src/lib.rs @@ -8,6 +8,7 @@ use futures::{FutureExt, TryFutureExt, future::BoxFuture}; use lance_core::Result; +pub mod array_encoding; pub mod buffer; pub mod compression; pub mod compression_config; @@ -17,13 +18,11 @@ pub mod decoder; pub mod encoder; pub mod encodings; pub mod format; -pub mod previous; pub mod repdef; pub mod statistics; #[cfg(test)] pub mod testing; pub mod utils; -pub mod version; // We can definitely add support for big-endian machines someday. However, it's not a priority and // would involve extensive testing (probably through emulation) to ensure that the encodings are diff --git a/rust/lance-encoding/src/previous.rs b/rust/lance-encoding/src/previous.rs deleted file mode 100644 index eb4e1bcdb0d..00000000000 --- a/rust/lance-encoding/src/previous.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -//! Legacy code for the 2.0 format that is no longer used in 2.1+ - -pub mod decoder; -pub mod encoder; -pub mod encodings; diff --git a/rust/lance-encoding/src/previous/decoder.rs b/rust/lance-encoding/src/previous/decoder.rs deleted file mode 100644 index bf32bea3d7c..00000000000 --- a/rust/lance-encoding/src/previous/decoder.rs +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -use std::{collections::VecDeque, ops::Range}; - -use crate::decoder::{ - FilterExpression, NextDecodeTask, PriorityRange, ScheduledScanLine, SchedulerContext, -}; - -use arrow_schema::DataType; -use futures::future::BoxFuture; -use lance_core::{Error, Result}; - -pub trait SchedulingJob: std::fmt::Debug { - fn schedule_next( - &mut self, - context: &mut SchedulerContext, - priority: &dyn PriorityRange, - ) -> Result; - - fn num_rows(&self) -> u64; -} - -/// A scheduler for a field's worth of data -/// -/// Each field in a reader's output schema maps to one field scheduler. This scheduler may -/// map to more than one column. For example, one field of struct data may -/// cover many columns of child data. In fact, the entire file is treated as one -/// top-level struct field. -/// -/// The scheduler is responsible for calculating the necessary I/O. One schedule_range -/// request could trigger multiple batches of I/O across multiple columns. The scheduler -/// should emit decoders into the sink as quickly as possible. -/// -/// As soon as the scheduler encounters a batch of data that can decoded then the scheduler -/// should emit a decoder in the "unloaded" state. The decode stream will pull the decoder -/// and start decoding. -/// -/// The order in which decoders are emitted is important. Pages should be emitted in -/// row-major order allowing decode of complete rows as quickly as possible. -/// -/// The `FieldScheduler` should be stateless and `Send` and `Sync`. This is -/// because it might need to be shared. For example, a list page has a reference to -/// the field schedulers for its items column. This is shared with the follow-up I/O -/// task created when the offsets are loaded. -/// -/// See [`crate::decoder`] for more information -pub trait FieldScheduler: Send + Sync + std::fmt::Debug { - /// Called at the beginning of scheduling to initialize the scheduler - fn initialize<'a>( - &'a self, - filter: &'a FilterExpression, - context: &'a SchedulerContext, - ) -> BoxFuture<'a, Result<()>>; - /// Schedules I/O for the requested portions of the field. - /// - /// Note: `ranges` must be ordered and non-overlapping - /// TODO: Support unordered or overlapping ranges in file scheduler - fn schedule_ranges<'a>( - &'a self, - ranges: &[Range], - filter: &FilterExpression, - ) -> Result>; - /// The number of rows in this field - fn num_rows(&self) -> u64; -} - -#[derive(Debug)] -pub struct DecoderReady { - // The decoder that is ready to be decoded - pub decoder: Box, - // The path to the decoder, the first value is the column index - // following values, if present, are nested child indices - // - // For example, a path of [1, 1, 0] would mean to grab the second - // column, then the second child, and then the first child. - // - // It could represent x in the following schema: - // - // score: float64 - // points: struct - // color: string - // location: struct - // x: float64 - // - // Currently, only struct decoders have "children" although other - // decoders may at some point as well. List children are only - // handled through indirect I/O at the moment and so they don't - // need to be represented (yet) - pub path: VecDeque, -} - -/// A decoder for a field's worth of data -/// -/// The decoder is initially "unloaded" (doesn't have all its data). The [`Self::wait_for_loaded`] -/// method should be called to wait for the needed I/O data before attempting to decode -/// any further. -/// -/// Unlike the other decoder types it is assumed that `LogicalPageDecoder` is stateful -/// and only `Send`. This is why we don't need a `rows_to_skip` argument in [`Self::drain`] -pub trait LogicalPageDecoder: std::fmt::Debug + Send { - /// Add a newly scheduled child decoder - /// - /// The default implementation does not expect children and returns - /// an error. - fn accept_child(&mut self, _child: DecoderReady) -> Result<()> { - Err(Error::internal(format!( - "The decoder {:?} does not expect children but received a child", - self - ))) - } - /// Waits until at least `num_rows` have been loaded - fn wait_for_loaded(&'_ mut self, loaded_need: u64) -> BoxFuture<'_, Result<()>>; - /// The number of rows loaded so far - fn rows_loaded(&self) -> u64; - /// The number of rows that still need loading - fn rows_unloaded(&self) -> u64 { - self.num_rows() - self.rows_loaded() - } - /// The total number of rows in the field - fn num_rows(&self) -> u64; - /// The number of rows that have been drained so far - fn rows_drained(&self) -> u64; - /// The number of rows that are still available to drain - fn rows_left(&self) -> u64 { - self.num_rows() - self.rows_drained() - } - /// Creates a task to decode `num_rows` of data into an array - fn drain(&mut self, num_rows: u64) -> Result; - /// The data type of the decoded data - fn data_type(&self) -> &DataType; -} diff --git a/rust/lance-encoding/src/previous/encodings.rs b/rust/lance-encoding/src/previous/encodings.rs deleted file mode 100644 index 67a43993508..00000000000 --- a/rust/lance-encoding/src/previous/encodings.rs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -//! Legacy code for the 2.0 format that is no longer used in 2.1+ - -pub mod logical; -pub mod physical; diff --git a/rust/lance-encoding/src/testing.rs b/rust/lance-encoding/src/testing.rs index 9825cb5949d..8555a7951cc 100644 --- a/rust/lance-encoding/src/testing.rs +++ b/rust/lance-encoding/src/testing.rs @@ -21,27 +21,344 @@ use futures::{FutureExt, StreamExt, future::BoxFuture}; use log::{debug, info, trace}; use tokio::sync::mpsc::{self, UnboundedSender}; -use lance_core::{Result, utils::bit::pad_bytes}; +use lance_core::{Error, Result, datatypes::Field as LanceField, utils::bit::pad_bytes}; use lance_datagen::{ArrayGenerator, RowCount, Seed, array, gen_batch}; use crate::{ EncodingsIo, buffer::LanceBuffer, + compression::{ + BlockCompressor, CompressionStrategy, field_metadata_params, finalize_miniblock_compressor, + reject_packed_struct_per_value, try_bitpacking_block, try_bitpacking_miniblock, + try_byte_stream_split_miniblock, try_child_rle_miniblock, + try_fixed_packed_struct_miniblock, try_fixed_u8_rle_block, try_fixed_u8_rle_miniblock, + try_general_block, try_raw_block, try_raw_fixed_size_list_miniblock, + try_raw_fixed_width_miniblock, try_raw_per_value, try_uncompressed_fixed_width_miniblock, + try_variable_packed_struct_per_value, try_variable_rle_block, try_variable_width_miniblock, + try_variable_width_per_value, + }, + compression_config::{CompressionFieldParams, CompressionParams}, + data::DataBlock, decoder::{ ColumnInfo, DecodeBatchScheduler, DecoderMessage, DecoderPlugins, FilterExpression, PageInfo, create_decode_stream, }, encoder::{ ColumnIndexSequence, EncodedColumn, EncodedPage, EncodingOptions, FieldEncoder, - MIN_PAGE_BUFFER_ALIGNMENT, OutOfLineBuffers, default_encoding_strategy, + FieldEncodingContext, FieldEncodingStrategy, MIN_PAGE_BUFFER_ALIGNMENT, OutOfLineBuffers, + structural::{ + PrimitiveFieldEncoding, PrimitivePageEncoding, try_create_binary_blob, try_create_list, + try_create_map, try_create_struct, try_create_structural_blob, + try_create_structural_fixed_size_list, + }, }, + encodings::logical::primitive::{fullzip::PerValueCompressor, miniblock::MiniBlockCompressor}, repdef::RepDefBuilder, - version::LanceFileVersion, }; const MAX_PAGE_BYTES: u64 = 32 * 1024 * 1024; const TEST_ALIGNMENT: usize = MIN_PAGE_BUFFER_ALIGNMENT as usize; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TestEncoding { + Array, + StructuralU16, + StructuralU32, + StructuralSparse, +} + +impl TestEncoding { + fn all() -> impl Iterator { + [ + Self::Array, + Self::StructuralU16, + Self::StructuralU32, + Self::StructuralSparse, + ] + .into_iter() + } + + fn is_structural(self) -> bool { + self != Self::Array + } +} + +impl std::fmt::Display for TestEncoding { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Array => write!(f, "array"), + Self::StructuralU16 => write!(f, "structural-u16"), + Self::StructuralU32 => write!(f, "structural-u32"), + Self::StructuralSparse => write!(f, "structural-sparse"), + } + } +} + +#[derive(Debug, Clone)] +struct TestCompressionStrategy { + encoding: TestEncoding, + params: CompressionParams, +} + +impl TestCompressionStrategy { + fn field_params(&self, field: &LanceField) -> CompressionFieldParams { + let mut params = self + .params + .get_field_params(&field.name, &field.data_type()); + let mut metadata = field_metadata_params(field); + if self.encoding == TestEncoding::StructuralU16 + && metadata + .minichunk_size + .is_some_and(|size| size >= 32 * 1024) + { + metadata.minichunk_size = None; + } + params.merge(&metadata); + params + } +} + +impl CompressionStrategy for TestCompressionStrategy { + fn create_miniblock_compressor( + &self, + field: &LanceField, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + let compressor = + if let Some(compressor) = try_uncompressed_fixed_width_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_byte_stream_split_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = match self.encoding { + TestEncoding::StructuralSparse => try_child_rle_miniblock(data, ¶ms), + TestEncoding::Array | TestEncoding::StructuralU16 | TestEncoding::StructuralU32 => { + try_fixed_u8_rle_miniblock(data, ¶ms) + } + } { + compressor + } else if let Some(compressor) = try_bitpacking_miniblock(data) { + compressor + } else if let Some(compressor) = try_raw_fixed_width_miniblock(data) { + compressor + } else if let Some(compressor) = try_variable_width_miniblock(field, data, ¶ms)? { + compressor + } else if let Some(compressor) = try_fixed_packed_struct_miniblock(data)? { + compressor + } else if let Some(compressor) = try_raw_fixed_size_list_miniblock(data) { + compressor + } else { + return Err(lance_core::Error::not_supported_source( + format!( + "Mini-block compression not yet supported for block type {}", + data.name() + ) + .into(), + )); + }; + finalize_miniblock_compressor(data, compressor, ¶ms) + } + + fn create_per_value( + &self, + field: &LanceField, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + if let Some(compressor) = try_raw_per_value(data) { + return Ok(compressor); + } + let packed = match self.encoding { + TestEncoding::Array | TestEncoding::StructuralU16 => { + reject_packed_struct_per_value(field, data)? + } + TestEncoding::StructuralU32 | TestEncoding::StructuralSparse => { + try_variable_packed_struct_per_value(Arc::new(self.clone()), field, data)? + } + }; + if let Some(compressor) = packed { + return Ok(compressor); + } + if let Some(compressor) = try_variable_width_per_value(field, data, ¶ms)? { + return Ok(compressor); + } + Err(lance_core::Error::not_supported_source( + format!( + "Per-value compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } + + fn create_block_compressor( + &self, + field: &LanceField, + data: &DataBlock, + ) -> Result<(Box, CompressiveEncoding)> { + let params = self.field_params(field); + let rle = match self.encoding { + TestEncoding::Array | TestEncoding::StructuralU16 => None, + TestEncoding::StructuralU32 => try_fixed_u8_rle_block(data, ¶ms)?, + TestEncoding::StructuralSparse => try_variable_rle_block(data, ¶ms)?, + }; + if let Some(compressor) = rle { + return Ok(compressor); + } + if let Some(compressor) = try_bitpacking_block(data) { + return Ok(compressor); + } + if matches!( + self.encoding, + TestEncoding::StructuralU32 | TestEncoding::StructuralSparse + ) && let Some(compressor) = try_general_block(data, ¶ms)? + { + return Ok(compressor); + } + if let Some(compressor) = try_raw_block(data) { + return Ok(compressor); + } + Err(lance_core::Error::not_supported_source( + format!( + "Block compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } +} + +pub fn test_compression_strategy( + encoding: TestEncoding, + params: CompressionParams, +) -> Arc { + Arc::new(TestCompressionStrategy { encoding, params }) +} + +#[derive(Debug)] +struct TestFieldEncodingStrategy { + encoding: TestEncoding, + primitive: PrimitiveFieldEncoding, +} + +impl FieldEncodingStrategy for TestFieldEncodingStrategy { + fn create_field_encoder( + &self, + field: &LanceField, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, + ) -> Result> { + if let Some(encoder) = + try_create_binary_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if self.encoding != TestEncoding::StructuralU16 + && let Some(encoder) = + try_create_structural_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if field.is_blob() { + return Err(Error::invalid_input_source( + format!( + "Blob encoding is not available for field '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )); + } + if self.encoding != TestEncoding::StructuralU16 { + if let Some(encoder) = try_create_map(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = + try_create_structural_fixed_size_list(field, column_index, context)? + { + return Ok(encoder); + } + } + if let Some(encoder) = self.primitive.try_create(field, column_index, context)? { + return Ok(encoder); + } + if self.encoding == TestEncoding::StructuralU16 { + if matches!( + field.data_type(), + DataType::FixedSizeList(item, _) + if matches!(item.data_type(), DataType::Struct(_)) + ) { + return Err(Error::not_supported_source( + "FixedSizeList is not enabled by the selected file format".into(), + )); + } + if matches!(field.data_type(), DataType::Map(_, _)) { + return Err(Error::not_supported_source( + "Map data type is not enabled by the selected file format".into(), + )); + } + } + if let Some(encoder) = try_create_list(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_struct(field, column_index, context)? { + return Ok(encoder); + } + Err(Error::not_supported_source( + format!( + "{} has no field encoding for '{}' with data type {}", + self.encoding, + field.name, + field.data_type() + ) + .into(), + )) + } +} + +pub fn test_encoding_strategy(encoding: TestEncoding) -> Box { + if encoding == TestEncoding::Array { + return Box::new(crate::array_encoding::ArrayFieldEncodingStrategy::new()); + } + + let compression = test_compression_strategy(encoding, CompressionParams::default()); + let page_encodings = match encoding { + TestEncoding::StructuralU16 => vec![ + PrimitivePageEncoding::reject_sparse(), + PrimitivePageEncoding::dense_u16(compression), + ], + TestEncoding::StructuralU32 => vec![ + PrimitivePageEncoding::reject_sparse(), + PrimitivePageEncoding::constant(), + PrimitivePageEncoding::dense_u32(compression), + ], + TestEncoding::StructuralSparse => vec![ + PrimitivePageEncoding::sparse(compression.clone()), + PrimitivePageEncoding::constant(), + PrimitivePageEncoding::dense_u32(compression), + ], + TestEncoding::Array => unreachable!(), + }; + Box::new(TestFieldEncodingStrategy { + encoding, + primitive: PrimitiveFieldEncoding::new(page_encodings), + }) +} + +pub fn create_test_field_encoder( + strategy: &dyn FieldEncodingStrategy, + field: &lance_core::datatypes::Field, + column_index: &mut ColumnIndexSequence, + options: &EncodingOptions, +) -> Result> { + let context = FieldEncodingContext { + strategy, + options, + root_field_metadata: &field.metadata, + }; + strategy.create_field_encoder(field, column_index, &context) +} + #[derive(Debug)] pub(crate) struct SimulatedScheduler { data: Bytes, @@ -342,24 +659,22 @@ pub async fn check_round_trip_encoding_generated( let lance_field = lance_core::datatypes::Field::try_from(&field).unwrap(); for page_size in test_cases.page_sizes.iter().copied() { debug!("Testing random data with a page size of {}", page_size); - let encoder_factory = |version: LanceFileVersion| { - let encoding_strategy = default_encoding_strategy(version); + let encoder_factory = |encoding: TestEncoding| { + let encoding_strategy = test_encoding_strategy(encoding); let mut column_index_seq = ColumnIndexSequence::default(); let encoding_options = EncodingOptions { max_page_bytes: MAX_PAGE_BYTES, cache_bytes_per_column: page_size, keep_original_array: true, buffer_alignment: MIN_PAGE_BUFFER_ALIGNMENT, - version, }; - encoding_strategy - .create_field_encoder( - encoding_strategy.as_ref(), - &lance_field, - &mut column_index_seq, - &encoding_options, - ) - .unwrap() + create_test_field_encoder( + encoding_strategy.as_ref(), + &lance_field, + &mut column_index_seq, + &encoding_options, + ) + .unwrap() }; check_round_trip_random( @@ -372,9 +687,9 @@ pub async fn check_round_trip_encoding_generated( } } -fn supports_nulls(data_type: &DataType, version: LanceFileVersion) -> bool { +fn supports_nulls(data_type: &DataType, encoding: TestEncoding) -> bool { if let DataType::Struct(fields) = data_type { - if version == LanceFileVersion::V2_0 { + if encoding == TestEncoding::Array { // 2.0 doesn't support nullability for structs false } else if fields.is_empty() { @@ -389,7 +704,7 @@ fn supports_nulls(data_type: &DataType, version: LanceFileVersion) -> bool { } } -type EncodingVerificationFn = dyn Fn(&[EncodedColumn], &LanceFileVersion); +type EncodingVerificationFn = dyn Fn(&[EncodedColumn], &TestEncoding); // The default will just test the full read #[derive(Clone)] @@ -400,8 +715,7 @@ pub struct TestCases { skip_validation: bool, max_page_size: Option, page_sizes: Vec, - min_file_version: Option, - max_file_version: Option, + encodings: Vec, verify_encoding: Option>, expected_encoding: Option>, } @@ -415,8 +729,7 @@ impl Default for TestCases { skip_validation: false, max_page_size: None, page_sizes: vec![4096, 1024 * 1024], - min_file_version: None, - max_file_version: None, + encodings: TestEncoding::all().collect(), verify_encoding: None, expected_encoding: None, } @@ -459,16 +772,40 @@ impl TestCases { self } - pub fn with_min_file_version(mut self, version: LanceFileVersion) -> Self { - self.min_file_version = Some(version); + pub fn with_encoding(mut self, encoding: TestEncoding) -> Self { + self.encodings = vec![encoding]; self } - pub fn with_max_file_version(mut self, version: LanceFileVersion) -> Self { - self.max_file_version = Some(version); + pub fn with_encodings(mut self, encodings: impl IntoIterator) -> Self { + self.encodings = encodings.into_iter().collect(); self } + pub fn with_structural_encodings(self) -> Self { + self.with_encodings([ + TestEncoding::StructuralU16, + TestEncoding::StructuralU32, + TestEncoding::StructuralSparse, + ]) + } + + pub fn with_u32_structural_encodings(self) -> Self { + self.with_encodings([TestEncoding::StructuralU32, TestEncoding::StructuralSparse]) + } + + pub fn with_dense_encodings(self) -> Self { + self.with_encodings([ + TestEncoding::Array, + TestEncoding::StructuralU16, + TestEncoding::StructuralU32, + ]) + } + + pub fn with_array_and_u16_encodings(self) -> Self { + self.with_encodings([TestEncoding::Array, TestEncoding::StructuralU16]) + } + pub fn with_page_sizes(mut self, page_sizes: Vec) -> Self { self.page_sizes = page_sizes; self @@ -483,22 +820,8 @@ impl TestCases { self.max_page_size.unwrap_or(MAX_PAGE_BYTES) } - fn get_versions(&self) -> Vec { - LanceFileVersion::iter_non_legacy() - .filter(|v| { - if let Some(min_file_version) = &self.min_file_version - && v < min_file_version - { - return false; - } - if let Some(max_file_version) = &self.max_file_version - && v > max_file_version - { - return false; - } - true - }) - .collect() + fn encodings(&self) -> impl Iterator + '_ { + self.encodings.iter().copied() } pub fn with_verify_encoding(mut self, verify_encoding: Arc) -> Self { @@ -506,9 +829,9 @@ impl TestCases { self } - fn verify_encoding(&self, encoding: &[EncodedColumn], version: &LanceFileVersion) { + fn verify_encoding(&self, columns: &[EncodedColumn], encoding: &TestEncoding) { if let Some(verify_encoding) = self.verify_encoding.as_ref() { - verify_encoding(encoding, version); + verify_encoding(columns, encoding); } } @@ -704,8 +1027,8 @@ fn verify_page_encoding( return Ok(()); } } - PageEncoding::Legacy(_) => { - // We don't need to care about legacy. + PageEncoding::Array(_) => { + // We don't need to care about the v2.0 array encoding. } } @@ -748,28 +1071,26 @@ pub async fn check_round_trip_encoding_of_data_with_expected( let mut field = Field::new("", example_data.data_type().clone(), true); field = field.with_metadata(metadata); let lance_field = lance_core::datatypes::Field::try_from(&field).unwrap(); - for file_version in test_cases.get_versions() { + for encoding in test_cases.encodings() { for page_size in test_cases.page_sizes.iter() { - let encoding_strategy = default_encoding_strategy(file_version); + let encoding_strategy = test_encoding_strategy(encoding); let mut column_index_seq = ColumnIndexSequence::default(); let encoding_options = EncodingOptions { cache_bytes_per_column: *page_size, max_page_bytes: test_cases.get_max_page_size(), keep_original_array: true, buffer_alignment: MIN_PAGE_BUFFER_ALIGNMENT, - version: file_version, }; - let encoder = encoding_strategy - .create_field_encoder( - encoding_strategy.as_ref(), - &lance_field, - &mut column_index_seq, - &encoding_options, - ) - .unwrap(); + let encoder = create_test_field_encoder( + encoding_strategy.as_ref(), + &lance_field, + &mut column_index_seq, + &encoding_options, + ) + .unwrap(); info!( - "Testing round trip encoding of data with file version {} and page size {}", - file_version, page_size + "Testing round trip encoding of data with test encoding {} and page size {}", + encoding, page_size ); check_round_trip_encoding_inner( encoder, @@ -777,7 +1098,7 @@ pub async fn check_round_trip_encoding_of_data_with_expected( data.clone(), expected_override.clone(), test_cases, - file_version, + encoding, ) .await } @@ -850,7 +1171,7 @@ async fn check_round_trip_encoding_inner( data: Vec>, expected_override: Option>, test_cases: &TestCases, - file_version: LanceFileVersion, + encoding: TestEncoding, ) { let mut writer = SimulatedWriter::new(encoder.num_columns()); @@ -891,7 +1212,7 @@ async fn check_round_trip_encoding_inner( log_page(&encoded_page); // For V2.1, verify encoding in the page if expected - if file_version >= LanceFileVersion::V2_1 + if encoding.is_structural() && let Some(ref expected) = test_cases.expected_encoding { verify_page_encoding(&encoded_page, expected, encoded_page.column_idx as usize) @@ -913,7 +1234,7 @@ async fn check_round_trip_encoding_inner( log_page(&encoded_page); // For V2.1, verify encoding in the page if expected - if file_version >= LanceFileVersion::V2_1 + if encoding.is_structural() && let Some(ref expected) = test_cases.expected_encoding { verify_page_encoding(&encoded_page, expected, encoded_page.column_idx as usize) @@ -925,7 +1246,7 @@ async fn check_round_trip_encoding_inner( let mut external_buffers = writer.new_external_buffers(); let encoded_columns = encoder.finish(&mut external_buffers).await.unwrap(); - test_cases.verify_encoding(&encoded_columns, &file_version); + test_cases.verify_encoding(&encoded_columns, &encoding); for buffer in external_buffers.take_buffers() { writer.write_lance_buffer(buffer); } @@ -978,7 +1299,7 @@ async fn check_round_trip_encoding_inner( let expected_data = expected_override.clone().or_else(|| concat_data.clone()); - let is_structural_encoding = file_version >= LanceFileVersion::V2_1; + let is_structural_encoding = encoding.is_structural(); let decode_field = if is_structural_encoding { let mut lance_field = lance_core::datatypes::Field::try_from(field).unwrap(); @@ -1118,20 +1439,20 @@ const NUM_RANDOM_ROWS: u32 = 10000; /// /// To test specific test cases use the async fn check_round_trip_random( - encoder_factory: impl Fn(LanceFileVersion) -> Box, + encoder_factory: impl Fn(TestEncoding) -> Box, field: Field, array_generator_provider: Box, test_cases: &TestCases, ) { for null_rate in [None, Some(0.5), Some(1.0)] { for use_slicing in [false, true] { - for file_version in test_cases.get_versions() { + for encoding in test_cases.encodings() { if null_rate != Some(1.0) && matches!(field.data_type(), DataType::Null) { continue; } let field = if null_rate.is_some() { - if !supports_nulls(field.data_type(), file_version) { + if !supports_nulls(field.data_type(), encoding) { continue; } field.clone().with_nullable(true) @@ -1189,8 +1510,8 @@ async fn check_round_trip_random( } info!( - "Testing version {} with {} rows divided across {} batches for {} rows per batch with null_rate={:?} and use_slicing={}", - file_version, + "Testing encoding {} with {} rows divided across {} batches for {} rows per batch with null_rate={:?} and use_slicing={}", + encoding, NUM_RANDOM_ROWS, num_ingest_batches, rows_per_batch, @@ -1198,12 +1519,12 @@ async fn check_round_trip_random( use_slicing ); check_round_trip_encoding_inner( - encoder_factory(file_version), + encoder_factory(encoding), &field, data, None, test_cases, - file_version, + encoding, ) .await } diff --git a/rust/lance-encoding/src/version.rs b/rust/lance-encoding/src/version.rs deleted file mode 100644 index cd8f09b011b..00000000000 --- a/rust/lance-encoding/src/version.rs +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -use std::str::FromStr; - -use lance_arrow::DataTypeExt; -use lance_core::datatypes::Field; -use lance_core::deepsize::{Context, DeepSizeOf}; -use lance_core::{Error, Result}; - -pub const LEGACY_FORMAT_VERSION: &str = "0.1"; -pub const V2_FORMAT_2_0: &str = "2.0"; -pub const V2_FORMAT_2_1: &str = "2.1"; -pub const V2_FORMAT_2_2: &str = "2.2"; -pub const V2_FORMAT_2_3: &str = "2.3"; - -/// Lance file version -#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Ord, PartialOrd, strum::EnumIter)] -pub enum LanceFileVersion { - // This is a little confusing but we rely on the following facts: - // - // Any version <= Next is stable - // The latest version before Stable is the default version for new datasets - // Any version >= Next is unstable - // - // As a result, 'Stable' is not the divider between stable and unstable (Next does this) - // but only serves to mark the default version for new datasets. - // - /// The legacy (0.1) format - Legacy, - V2_0, - #[default] - V2_1, - /// The latest stable release (also the default version for new datasets) - Stable, - V2_2, - /// The latest unstable release - Next, - V2_3, -} - -impl DeepSizeOf for LanceFileVersion { - fn deep_size_of_children(&self, _context: &mut Context) -> usize { - 0 - } -} - -impl LanceFileVersion { - /// Convert Stable or Next to the actual version - pub fn resolve(&self) -> Self { - match self { - Self::Stable => Self::default(), - Self::Next => Self::V2_3, - _ => *self, - } - } - - pub fn is_unstable(&self) -> bool { - self >= &Self::Next - } - - pub fn try_from_major_minor(major: u32, minor: u32) -> Result { - match (major, minor) { - (0, 0) => Ok(Self::Legacy), - (0, 1) => Ok(Self::Legacy), - (0, 2) => Ok(Self::Legacy), - (0, 3) => Ok(Self::V2_0), - (2, 0) => Ok(Self::V2_0), - (2, 1) => Ok(Self::V2_1), - (2, 2) => Ok(Self::V2_2), - (2, 3) => Ok(Self::V2_3), - _ => Err(Error::invalid_input_source( - format!("Unknown Lance storage version: {}.{}", major, minor).into(), - )), - } - } - - pub fn to_numbers(&self) -> (u32, u32) { - match self { - Self::Legacy => (0, 2), - Self::V2_0 => (2, 0), - Self::V2_1 => (2, 1), - Self::V2_2 => (2, 2), - Self::V2_3 => (2, 3), - Self::Stable => self.resolve().to_numbers(), - Self::Next => self.resolve().to_numbers(), - } - } - - pub fn iter_non_legacy() -> impl Iterator { - use strum::IntoEnumIterator; - - Self::iter().filter(|&v| v != Self::Stable && v != Self::Next && v != Self::Legacy) - } - - pub fn support_add_sub_column(&self) -> bool { - self > &Self::V2_1 - } - - pub fn support_remove_sub_column(&self, field: &Field) -> bool { - if self <= &Self::V2_1 { - field.data_type().is_struct() - } else { - field.data_type().is_nested() - } - } -} - -impl std::fmt::Display for LanceFileVersion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Legacy => LEGACY_FORMAT_VERSION, - Self::V2_0 => V2_FORMAT_2_0, - Self::V2_1 => V2_FORMAT_2_1, - Self::V2_2 => V2_FORMAT_2_2, - Self::V2_3 => V2_FORMAT_2_3, - Self::Stable => "stable", - Self::Next => "next", - } - ) - } -} - -impl FromStr for LanceFileVersion { - type Err = Error; - - fn from_str(value: &str) -> Result { - match value.to_lowercase().as_str() { - LEGACY_FORMAT_VERSION => Ok(Self::Legacy), - V2_FORMAT_2_0 => Ok(Self::V2_0), - V2_FORMAT_2_1 => Ok(Self::V2_1), - V2_FORMAT_2_2 => Ok(Self::V2_2), - V2_FORMAT_2_3 => Ok(Self::V2_3), - "stable" => Ok(Self::Stable), - "legacy" => Ok(Self::Legacy), - "next" => Ok(Self::Next), - // Version 0.3 is an alias of 2.0 - "0.3" => Ok(Self::V2_0), - _ => Err(Error::invalid_input_source( - format!("Unknown Lance storage version: {}", value).into(), - )), - } - } -} diff --git a/rust/lance-file/Cargo.toml b/rust/lance-file/Cargo.toml index 56536e840e9..f9e98f60cca 100644 --- a/rust/lance-file/Cargo.toml +++ b/rust/lance-file/Cargo.toml @@ -19,6 +19,7 @@ lance-io.workspace = true arrow-arith.workspace = true arrow-array.workspace = true arrow-buffer.workspace = true +arrow-cast.workspace = true arrow-data.workspace = true arrow-schema.workspace = true arrow-select.workspace = true @@ -43,6 +44,7 @@ criterion.workspace = true rstest.workspace = true proptest.workspace = true pretty_assertions.workspace = true +rand.workspace = true test-log.workspace = true libc.workspace = true diff --git a/rust/lance-file/benches/reader.rs b/rust/lance-file/benches/reader.rs index 7ee36e0b6f4..5d280f00d26 100644 --- a/rust/lance-file/benches/reader.rs +++ b/rust/lance-file/benches/reader.rs @@ -14,8 +14,9 @@ use lance_encoding::decoder::{DecoderConfig, DecoderPlugins, FilterExpression}; use lance_file::{ reader::{FileReader, FileReaderOptions}, testing::test_cache, - version::LanceFileVersion, - writer::{FileWriter, FileWriterOptions}, + version::ConcreteFileVersion, + versions as file_versions, + writer::FileWriterOptions, }; use lance_io::{ object_store::ObjectStore, @@ -28,9 +29,9 @@ use tokio::runtime::Runtime; fn bench_reader(c: &mut Criterion) { for version in [ - LanceFileVersion::V2_0, - LanceFileVersion::V2_1, - LanceFileVersion::V2_2, + ConcreteFileVersion::V2_0, + ConcreteFileVersion::V2_1, + ConcreteFileVersion::V2_2, ] { let mut group = c.benchmark_group(format!("reader_{}", version)); let data = lance_datagen::gen_batch() @@ -47,13 +48,11 @@ fn bench_reader(c: &mut Criterion) { let file_path = base_path.clone().join("foo.lance"); let object_writer = rt.block_on(object_store.create(&file_path)).unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = file_versions::create_writer( + version, object_writer, data.schema().as_ref().try_into().unwrap(), - FileWriterOptions { - format_version: Some(version), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); rt.block_on(writer.write_batch(&data)).unwrap(); @@ -175,7 +174,7 @@ fn get_cached_readers( tmpdir: &TempDir, filesystem: &str, rt: &Runtime, - version: LanceFileVersion, + version: ConcreteFileVersion, ) -> Arc { use std::sync::{LazyLock, Mutex}; @@ -213,13 +212,11 @@ fn get_cached_readers( // Write file let object_writer = rt.block_on(object_store.create(&file_path)).unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = file_versions::create_writer( + version, object_writer, data.schema().as_ref().try_into().unwrap(), - FileWriterOptions { - format_version: Some(version), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); rt.block_on(writer.write_batch(&data)).unwrap(); @@ -366,9 +363,9 @@ fn bench_random_access(c: &mut Criterion) { let mut group = c.benchmark_group("take"); let versions = [ - LanceFileVersion::V2_0, - LanceFileVersion::V2_1, - LanceFileVersion::V2_2, + ConcreteFileVersion::V2_0, + ConcreteFileVersion::V2_1, + ConcreteFileVersion::V2_2, ]; for filesystem in filesystems { @@ -379,7 +376,8 @@ fn bench_random_access(c: &mut Criterion) { for multithreaded in [false, true] { for rows_at_a_time in [1, 100] { for cached in [true, false] { - if !cached && (filesystem == "mem" || version == LanceFileVersion::V2_0) { + if !cached && (filesystem == "mem" || version == ConcreteFileVersion::V2_0) + { continue; } diff --git a/rust/lance-file/src/datatypes.rs b/rust/lance-file/src/datatypes.rs index c31cb5c97e7..3d84e99267c 100644 --- a/rust/lance-file/src/datatypes.rs +++ b/rust/lance-file/src/datatypes.rs @@ -1,14 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The Lance Authors -use arrow_schema::DataType; -use async_recursion::async_recursion; use lance_arrow::ARROW_EXT_NAME_KEY; -use lance_arrow::DataTypeExt; use lance_core::datatypes::{Dictionary, Encoding, Field, LogicalType, Schema}; use lance_core::{Error, Result}; -use lance_io::traits::Reader; -use lance_io::utils::{read_binary_array, read_fixed_stride_array}; use std::collections::HashMap; use crate::format::pb; @@ -348,66 +343,6 @@ impl From for pb::Encoding { } } -#[async_recursion] -async fn load_field_dictionary<'a>(field: &mut Field, reader: &dyn Reader) -> Result<()> { - if let DataType::Dictionary(_, value_type) = field.data_type() { - assert!(field.dictionary.is_some()); - if let Some(dict_info) = field.dictionary.as_mut() { - use DataType::*; - match value_type.as_ref() { - _ if value_type.is_binary_like() => { - dict_info.values = Some( - read_binary_array( - reader, - value_type.as_ref(), - true, // Empty values are null - dict_info.offset, - dict_info.length, - .., - ) - .await?, - ); - } - Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64 => { - dict_info.values = Some( - read_fixed_stride_array( - reader, - value_type.as_ref(), - dict_info.offset, - dict_info.length, - .., - ) - .await?, - ); - } - _ => { - return Err(Error::schema(format!( - "Does not support {} as dictionary value type", - value_type - ))); - } - } - } else { - panic!("Should not reach here: dictionary field does not load dictionary info") - } - Ok(()) - } else { - for child in field.children.as_mut_slice() { - load_field_dictionary(child, reader).await?; - } - Ok(()) - } -} - -/// Load dictionary value array from manifest files. -// TODO: pub(crate) -pub async fn populate_schema_dictionary(schema: &mut Schema, reader: &dyn Reader) -> Result<()> { - for field in schema.fields.as_mut_slice() { - load_field_dictionary(field, reader).await?; - } - Ok(()) -} - #[cfg(test)] mod tests { use std::collections::HashMap; diff --git a/rust/lance-file/src/lib.rs b/rust/lance-file/src/lib.rs index c89f7c7b5bf..f48563dbc4a 100644 --- a/rust/lance-file/src/lib.rs +++ b/rust/lance-file/src/lib.rs @@ -4,28 +4,29 @@ pub mod datatypes; pub mod format; pub(crate) mod io; -pub mod previous; pub mod reader; pub mod testing; +pub mod version; +pub mod versions; pub mod writer; pub use io::LanceEncodingsIo; use format::MAGIC; -pub use lance_encoding::version; - use lance_core::{Error, Result}; -use lance_encoding::version::LanceFileVersion; use lance_io::object_store::ObjectStore; use object_store::path::Path; +use version::ConcreteFileVersion; pub async fn determine_file_version( store: &ObjectStore, path: &Path, known_size: Option, -) -> Result { +) -> Result { let size = match known_size { - None => store.size(path).await.unwrap() as usize, + None => usize::try_from(store.size(path).await?).map_err(|_| { + Error::invalid_input(format!("file {} is too large for this platform", path)) + })?, Some(size) => size, }; if size < 8 { @@ -51,5 +52,5 @@ pub async fn determine_file_version( let major_version = u16::from_le_bytes([footer[0], footer[1]]); let minor_version = u16::from_le_bytes([footer[2], footer[3]]); - LanceFileVersion::try_from_major_minor(major_version as u32, minor_version as u32) + ConcreteFileVersion::from_footer_numbers(major_version, minor_version) } diff --git a/rust/lance-file/src/previous/mod.rs b/rust/lance-file/src/previous/mod.rs deleted file mode 100644 index 9031d2b4992..00000000000 --- a/rust/lance-file/src/previous/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -//! Legacy Lance file v1 implementation kept for backwards compatibility. - -pub mod format; -pub mod page_table; -pub mod reader; -pub mod writer; diff --git a/rust/lance-file/src/reader.rs b/rust/lance-file/src/reader.rs index a4108020859..5b9558ec30b 100644 --- a/rust/lance-file/src/reader.rs +++ b/rust/lance-file/src/reader.rs @@ -4,6 +4,7 @@ use std::{ borrow::Cow, collections::{BTreeMap, BTreeSet}, + fmt::Debug, io::Cursor, ops::Range, pin::Pin, @@ -12,6 +13,7 @@ use std::{ use arrow_array::RecordBatchReader; use arrow_schema::Schema as ArrowSchema; +use async_trait::async_trait; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt}; use bytes::{Bytes, BytesMut}; use futures::{Stream, StreamExt, stream::BoxStream}; @@ -19,16 +21,14 @@ use lance_core::deepsize::{Context, DeepSizeOf}; use lance_encoding::{ EncodingsIo, decoder::{ - ColumnInfo, DecoderConfig, DecoderPlugins, FilterExpression, PageEncoding, PageInfo, - ReadBatchTask, RequestedRows, SchedulerDecoderConfig, schedule_and_decode, - schedule_and_decode_blocking, + ColumnInfo, DecoderConfig, DecoderPlugins, FilterExpression, PageEncoding, ReadBatchTask, + RequestedRows, SchedulerDecoderConfig, schedule_and_decode, schedule_and_decode_blocking, }, encoder::EncodedBatch, - version::LanceFileVersion, }; use log::debug; use object_store::path::Path; -use prost::{Message, Name}; +use prost::Message; use lance_core::{ Error, Result, @@ -45,11 +45,14 @@ use lance_io::{ use crate::{ datatypes::{Fields, FieldsWithMeta}, - format::{MAGIC, MAJOR_VERSION, MINOR_VERSION, pb, pbfile}, + format::{MAGIC, pb, pbfile}, io::LanceEncodingsIo, - writer::PAGE_BUFFER_ALIGNMENT, + version::ConcreteFileVersion, + versions, }; +pub(crate) mod structural; + /// Default chunk size for reading large pages (8MiB) /// Pages larger than this will be split into multiple chunks during read pub const DEFAULT_READ_CHUNK_SIZE: u64 = 8 * 1024 * 1024; @@ -102,8 +105,11 @@ pub struct CachedFileMetadata { pub num_global_buffer_bytes: u64, /// The number of bytes contained in the CMO and GBO tables pub num_footer_bytes: u64, + /// The major version number stored in the file footer. pub major_version: u16, + /// The minor version number stored in the file footer. pub minor_version: u16, + pub version: ConcreteFileVersion, /// The actual total file size in bytes, as reported by the object store. pub file_size_bytes: u64, /// User global buffers (index >= 1) whose bytes were already captured by the @@ -176,14 +182,14 @@ impl DeepSizeOf for CachedFileMetadata { /// hold decoded metadata for every column. #[derive(Debug, DeepSizeOf)] pub struct FileMetadataIndex { - file_schema: Arc, - num_rows: u64, - file_buffers: Vec, - column_metadata_offsets: Arc<[(u64, u64)]>, - num_columns: u32, - version: LanceFileVersion, - file_size_bytes: u64, - retained_global_buffers: BTreeMap, + pub(crate) file_schema: Arc, + pub(crate) num_rows: u64, + pub(crate) file_buffers: Vec, + pub(crate) column_metadata_offsets: Arc<[(u64, u64)]>, + pub(crate) num_columns: u32, + pub(crate) version: ConcreteFileVersion, + pub(crate) file_size_bytes: u64, + pub(crate) retained_global_buffers: BTreeMap, } impl FileMetadataIndex { @@ -229,18 +235,8 @@ impl CacheKey for ColumnMetadataCacheKey { } impl CachedFileMetadata { - pub fn version(&self) -> LanceFileVersion { - match (self.major_version, self.minor_version) { - (0, 3) => LanceFileVersion::V2_0, - (2, 0) => LanceFileVersion::V2_0, - (2, 1) => LanceFileVersion::V2_1, - (2, 2) => LanceFileVersion::V2_2, - (2, 3) => LanceFileVersion::V2_3, - _ => panic!( - "Unsupported version: {}.{}", - self.major_version, self.minor_version - ), - } + pub fn version(&self) -> ConcreteFileVersion { + self.version } } @@ -313,123 +309,14 @@ pub struct ReaderProjection { } impl ReaderProjection { - fn from_field_ids_helper<'a>( - file_version: LanceFileVersion, - fields: impl Iterator, - field_id_to_column_index: &BTreeMap, - column_indices: &mut Vec, - ) -> Result<()> { - for field in fields { - let is_structural = file_version >= LanceFileVersion::V2_1; - let (contributes, recurse) = field_column_shape(field, is_structural); - // In the 2.0 system we needed ids for intermediate fields. In 2.1+ - // we only need ids for leaf fields. - if contributes - && let Some(column_idx) = field_id_to_column_index.get(&(field.id as u32)).copied() - { - column_indices.push(column_idx); - } - if recurse { - Self::from_field_ids_helper( - file_version, - field.children.iter(), - field_id_to_column_index, - column_indices, - )?; - } - } - Ok(()) - } - - /// Creates a projection using a mapping from field IDs to column indices - /// - /// You can obtain such a mapping when the file is written using the - /// [`crate::writer::FileWriter::field_id_to_column_indices`] method. - pub fn from_field_ids( - file_version: LanceFileVersion, - schema: &Schema, - field_id_to_column_index: &BTreeMap, - ) -> Result { - let mut column_indices = Vec::new(); - Self::from_field_ids_helper( - file_version, - schema.fields.iter(), - field_id_to_column_index, - &mut column_indices, - )?; - let projection = Self { - schema: Arc::new(schema.clone()), - column_indices, - }; - Ok(projection) - } - - /// Creates a projection that reads the entire file - /// - /// If the schema provided is not the schema of the entire file then - /// the projection will be invalid and the read will fail. - /// If the field is a `struct datatype` with `packed` set to true in the field metadata, - /// the whole struct has one column index. - /// To support nested `packed-struct encoding`, this method need to be further adjusted. - pub fn from_whole_schema(schema: &Schema, version: LanceFileVersion) -> Self { - let schema = Arc::new(schema.clone()); - let is_structural = version >= LanceFileVersion::V2_1; - let mut column_indices = vec![]; - let mut curr_column_idx = 0; - let mut packed_struct_fields_num = 0; - for field in schema.fields_pre_order() { - if packed_struct_fields_num > 0 { - packed_struct_fields_num -= 1; - continue; - } - if field.is_packed_struct() { - column_indices.push(curr_column_idx); - curr_column_idx += 1; - packed_struct_fields_num = field.children.len(); - } else if field.children.is_empty() || !is_structural { - column_indices.push(curr_column_idx); - curr_column_idx += 1; - } - } - Self { - schema, - column_indices, - } - } - - /// Creates a projection that reads the specified columns provided by name + /// Returns whether this projection is selective enough to benefit from + /// loading column metadata through the file's metadata index. /// - /// The syntax for column names is the same as [`lance_core::datatypes::Schema::project`] - /// - /// If the schema provided is not the schema of the entire file then - /// the projection will be invalid and the read will fail. - pub fn from_column_names( - file_version: LanceFileVersion, - schema: &Schema, - column_names: &[&str], - ) -> Result { - let field_id_to_column_index = schema - .fields_pre_order() - // In the 2.0 system we needed ids for intermediate fields. In 2.1+ - // we only need ids for leaf fields. - .filter(|field| { - file_version < LanceFileVersion::V2_1 || field.is_leaf() || field.is_packed_struct() - }) - .enumerate() - .map(|(idx, field)| (field.id as u32, idx as u32)) - .collect::>(); - let projected = schema.project(column_names)?; - let mut column_indices = Vec::new(); - Self::from_field_ids_helper( - file_version, - projected.fields.iter(), - &field_id_to_column_index, - &mut column_indices, - )?; - Ok(Self { - schema: Arc::new(projected), - column_indices, - }) + /// The caller must already have selected a file format that supports indexed + /// metadata. This method only evaluates the projection shape and selectivity. + pub fn prefers_indexed_metadata(&self, total_columns: usize) -> bool { + FileMetadataProvider::projection_matches_indexed_metadata(self) + && self.column_indices.len().saturating_mul(4) < total_columns } } @@ -461,28 +348,52 @@ impl Default for FileReaderOptions { } #[derive(Debug, Clone)] -struct PreparedProjection { - column_infos: Vec>, - decoder_projection: ReaderProjection, +pub(crate) struct PreparedProjection { + pub column_infos: Vec>, + pub decoder_projection: ReaderProjection, } #[derive(Debug, Clone)] -enum FileMetadataProvider { +pub(crate) enum FileMetadataProvider { Full(Arc), Indexed(Arc), } +/// Executable projection behavior selected by an exact file-version module. +/// +/// The shared reader invokes this behavior but never interprets a version or +/// accepted-grammar profile. +#[async_trait] +pub(crate) trait ReadProjection: Debug + Send + Sync { + fn validate_indexed( + &self, + projection: &ReaderProjection, + metadata_index: &FileMetadataIndex, + ) -> Result<()>; + + fn read_length(&self, prepared: &PreparedProjection) -> Result; + + async fn prepare( + &self, + metadata_provider: &FileMetadataProvider, + projection: &ReaderProjection, + io: &Arc, + cache: &Arc, + ) -> Result<(PreparedProjection, u64)>; +} + #[derive(Debug, Clone)] -struct FileReadCore { - scheduler: Arc, - base_projection: ReaderProjection, - metadata_provider: FileMetadataProvider, - decoder_plugins: Arc, - cache: Arc, - options: FileReaderOptions, +pub(crate) struct DecodeEngine { + pub scheduler: Arc, + pub base_projection: ReaderProjection, + pub metadata_provider: FileMetadataProvider, + pub read_projection: Arc, + pub decoder_plugins: Arc, + pub cache: Arc, + pub options: FileReaderOptions, } -/// A projection-scoped reader for Lance files. +/// A projection-scoped reader for a current-format Lance file. /// /// This reader fixes a base projection at construction time. All later reads /// must stay within that projection, which lets the reader load only the column @@ -491,51 +402,71 @@ struct FileReadCore { /// file metadata. #[derive(Debug, Clone)] pub struct ProjectedFileReader { - core: FileReadCore, + core: DecodeEngine, } -/// A Lance file reader backed by fully decoded file metadata. +/// A current-format Lance file reader backed by fully decoded metadata. #[derive(Debug, Clone)] pub struct FileReader { - core: FileReadCore, - metadata: Arc, + pub(crate) core: DecodeEngine, + pub(crate) metadata: Arc, +} + +pub(crate) fn tasks_to_record_batch_stream( + schema: Arc, + tasks: Pin + Send>>, + batch_readahead: u32, +) -> Pin> { + let arrow_schema = Arc::new(ArrowSchema::from(schema.as_ref())); + let batches = tasks + .map(|task| task.task) + .buffered(batch_readahead as usize) + .boxed(); + Box::pin(RecordBatchStreamAdapter::new(arrow_schema, batches)) +} + +pub(crate) enum RawFileMetadataOpen { + Legacy { + major_version: u16, + minor_version: u16, + }, + Current { + version: ConcreteFileVersion, + metadata: RawFileMetadata, + }, +} + +pub(crate) struct RawFileMetadata { + pub file_schema: Arc, + pub column_metadatas: Vec, + pub num_rows: u64, + pub file_buffers: Vec, + pub num_data_bytes: u64, + pub num_column_metadata_bytes: u64, + pub num_global_buffer_bytes: u64, + pub num_footer_bytes: u64, + pub footer: Footer, + pub file_size_bytes: u64, + pub retained_global_buffers: BTreeMap, } + #[derive(Debug)] -struct Footer { +pub(crate) struct Footer { #[allow(dead_code)] - column_meta_start: u64, + pub column_meta_start: u64, // We don't use this today because we always load metadata for every column // and don't yet support "metadata projection" #[allow(dead_code)] - column_meta_offsets_start: u64, - global_buff_offsets_start: u64, - num_global_buffers: u32, - num_columns: u32, - major_version: u16, - minor_version: u16, + pub column_meta_offsets_start: u64, + pub global_buff_offsets_start: u64, + pub num_global_buffers: u32, + pub num_columns: u32, + pub major_version: u16, + pub minor_version: u16, } const FOOTER_LEN: usize = 40; -// How a field maps onto physical columns, shared by the projection-building and -// projection-validation walks so they stay in lockstep. In the 2.0 layout every -// ordinary field (including structs and lists) has its own column; in 2.1 only -// leaves do. Blob/packed-struct fields are opaque in all versions: they are a -// single column with no descent, including unloaded blob descriptor schemas. -// Returns `(contributes, recurse)`: whether the field has its own column and -// whether to walk into its children. The DFS order is the field's own column (if -// any) followed by its children, so a field's root (first) column is always the -// first entry of its sub-slice. -fn field_column_shape(field: &Field, is_structural: bool) -> (bool, bool) { - if field.is_blob() || field.is_packed_struct() { - return (true, false); - } - - let contributes = !is_structural || field.children.is_empty(); - let recurse = !field.children.is_empty(); - (contributes, recurse) -} - // Count the V2.1 physical columns required to reconstruct a projected field. // This is the same DFS shape consumed by `ColumnInfoIter`: ordinary structural // nodes are transparent and leaves contribute columns. Indexed metadata loading @@ -549,100 +480,45 @@ fn indexed_projection_column_count(field: &Field) -> Option { return None; } - let (contributes, recurse) = field_column_shape(field, true); - let initial = usize::from(contributes); - if !recurse { - return Some(initial); + if field.children.is_empty() { + return Some(1); } - field.children.iter().try_fold(initial, |count, child| { + field.children.iter().try_fold(0usize, |count, child| { count.checked_add(indexed_projection_column_count(child)?) }) } -// Whether a field's children each cover the same rows as the field itself. Struct -// children do (one value per parent row), so they must share its length. List, -// map, and fixed-size-list items have an independent cardinality (item count, not -// row count) and are validated only against themselves. -fn children_share_parent_length(field: &Field) -> bool { - field.logical_type.is_struct() -} - -// Validate one field's slice of a projection's flat `column_indices`, returning -// the field's top-level row count (the page-row sum of its root column). Walks the -// same DFS order as `from_field_ids_helper`, advancing `cursor` past every column -// the field contributes. -// -// `comparable` tracks whether the field's row count shares the read's top-level -// cardinality. A struct's children must all match that count -- the decoders -// combine them assuming equal lengths and would otherwise panic or read past a -// shorter child -- so the equality check runs only while `comparable` holds. Once -// the walk descends through a list/map/fixed-size-list its items have an -// independent cardinality (item count, not row count), so `comparable` turns off -// for that whole subtree and a nested struct's children are no longer compared. -fn validate_field_length Result>( - field: &Field, - is_structural: bool, - comparable: bool, - column_indices: &[u32], - cursor: &mut usize, - column_len: &F, -) -> Result { - let (contributes, recurse) = field_column_shape(field, is_structural); - let mut field_rows: Option = None; - if contributes { - let column = *column_indices.get(*cursor).ok_or_else(|| { - Error::invalid_input(format!( - "projection supplied fewer column indices than its fields require \ - (ran out at field '{}')", - field.name - )) - })?; - *cursor += 1; - field_rows = Some(column_len(column as usize)?); - } - if recurse { - // Only enforce equal-length children for a struct whose own count is still - // at the top-level cardinality; below a list/map/fixed-size-list the items - // have an independent cardinality, so neither this field nor its - // descendants are comparable. - let enforce_children = comparable && children_share_parent_length(field); - for child in &field.children { - let child_rows = validate_field_length( - child, - is_structural, - enforce_children, - column_indices, - cursor, - column_len, - )?; - // A struct that contributes no column of its own (the 2.1 layout) - // takes its row count from its first child. - let expected = *field_rows.get_or_insert(child_rows); - if enforce_children && child_rows != expected { - return Err(Error::invalid_input(format!( - "cannot read field '{}': its children have differing lengths \ - (child '{}' has {} rows, but the field has {}); a struct's \ - children must all have the same length", - field.name, child.name, child_rows, expected - ))); - } - } - } - field_rows.ok_or_else(|| { - Error::invalid_input(format!( - "projected field '{}' maps to no columns", - field.name - )) - }) -} - // The reader combines a projection's columns into rectangular batches, so they // must all have the same length. Returns that common length, or a descriptive // error (naming each column's length) when they differ. Ordinary files always // pass; only files written with `FileWriter::write_column` whose columns ended up // unequal can fail, and those must be read separately. -fn verify_uniform_lengths(field_lengths: &[(&str, u64)]) -> Result { +pub(crate) fn normalized_column_num_rows(info: &ColumnInfo) -> Result { + info.page_infos.iter().try_fold(0_u64, |rows, page| { + let page_rows = match &page.encoding { + PageEncoding::Structural(layout) => match &layout.layout { + Some(pbenc21::page_layout::Layout::SparseLayout(sparse)) => sparse + .structural_layers + .first() + .and_then(|layer| layer.layer.as_ref()) + .map_or(page.num_rows, |layer| match layer { + pbenc21::sparse_structural_layer::Layer::Validity(layer) => layer.num_slots, + pbenc21::sparse_structural_layer::Layer::List(layer) => layer.num_slots, + pbenc21::sparse_structural_layer::Layer::FixedSizeList(layer) => { + layer.num_slots + } + }), + _ => page.num_rows, + }, + _ => page.num_rows, + }; + rows.checked_add(page_rows) + .ok_or_else(|| Error::invalid_input_source("Column row count overflows u64".into())) + }) +} + +pub(crate) fn verify_uniform_lengths(field_lengths: &[(&str, u64)]) -> Result { let first = field_lengths.first().map_or(0, |&(_, len)| len); if field_lengths.iter().all(|&(_, len)| len == first) { return Ok(first); @@ -659,6 +535,30 @@ fn verify_uniform_lengths(field_lengths: &[(&str, u64)]) -> Result { } impl FileReader { + pub(crate) fn base_projection(&self) -> &ReaderProjection { + &self.core.base_projection + } + + pub(crate) fn full_projection(&self, projection: ReaderProjection) -> PreparedProjection { + PreparedProjection { + column_infos: self.metadata.column_infos.clone(), + decoder_projection: projection, + } + } + + pub(crate) async fn read_prepared_tasks( + &self, + params: ReadBatchParams, + batch_size: u32, + prepared: PreparedProjection, + read_len: u64, + filter: FilterExpression, + ) -> Result + Send>>> { + self.core + .read_prepared_tasks(params, batch_size, prepared, read_len, filter) + .await + } + pub fn with_scheduler(&self, scheduler: Arc) -> Self { Self { core: self.core.with_scheduler(scheduler), @@ -818,14 +718,6 @@ impl FileReader { let major_version = cursor.read_u16::()?; let minor_version = cursor.read_u16::()?; - if major_version == MAJOR_VERSION as u16 && minor_version == MINOR_VERSION as u16 { - return Err(Error::version_conflict( - "Attempt to use the lance v2 reader to read a legacy file".to_string(), - major_version, - minor_version, - )); - } - let magic_bytes = footer_bytes.slice(len - 4..); if magic_bytes.as_ref() != MAGIC { return Err(Error::invalid_input(format!( @@ -844,6 +736,22 @@ impl FileReader { }) } + fn current_file_version(footer: &Footer) -> Result { + let version = + ConcreteFileVersion::from_footer_numbers(footer.major_version, footer.minor_version)?; + match version { + ConcreteFileVersion::V1 => Err(Error::version_conflict( + "Attempt to use the lance v2 reader to read a legacy file".to_string(), + footer.major_version, + footer.minor_version, + )), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => Ok(version), + } + } + // TODO: Once we have coalesced I/O we should only read the column metadatas that we need fn read_all_column_metadata( column_metadata_bytes: Bytes, @@ -935,25 +843,12 @@ impl FileReader { } } - fn do_decode_gbo_table( - gbo_bytes: &Bytes, - footer: &Footer, - version: LanceFileVersion, - ) -> Result> { + fn do_decode_gbo_table(gbo_bytes: &Bytes, footer: &Footer) -> Result> { let mut global_bufs_cursor = Cursor::new(gbo_bytes); let mut global_buffers = Vec::with_capacity(footer.num_global_buffers as usize); - for buffer_index in 0..footer.num_global_buffers { + for _ in 0..footer.num_global_buffers { let buf_pos = global_bufs_cursor.read_u64::()?; - if version >= LanceFileVersion::V2_1 && buf_pos % PAGE_BUFFER_ALIGNMENT as u64 != 0 { - return Err(Error::invalid_input_source( - format!( - "Global buffer {} position {} is not aligned to {} bytes", - buffer_index, buf_pos, PAGE_BUFFER_ALIGNMENT - ) - .into(), - )); - } let buf_size = global_bufs_cursor.read_u64::()?; global_buffers.push(BufferDescriptor { position: buf_pos, @@ -969,7 +864,6 @@ impl FileReader { file_len: u64, scheduler: &FileScheduler, footer: &Footer, - version: LanceFileVersion, ) -> Result> { // This could, in theory, trigger another IOP but the GBO table should never be large // enough for that to happen @@ -980,7 +874,7 @@ impl FileReader { file_len, ) .await?; - Self::do_decode_gbo_table(&gbo_bytes, footer, version) + Self::do_decode_gbo_table(&gbo_bytes, footer) } fn decode_schema(schema_bytes: Bytes) -> Result<(u64, lance_core::datatypes::Schema)> { @@ -995,32 +889,24 @@ impl FileReader { Ok((num_rows, schema)) } - // TODO: Support late projection. Currently, if we want to perform a - // projected read of a file, we load all of the column metadata, and then - // only read the column data that is requested. This is fine for most cases. - // - // However, if there are many columns then loading all of the column metadata - // may be expensive. We should support a mode where we only load the column - // metadata for the columns that are requested (the file format supports this). - // - // The main challenge is that we either need to ignore the column metadata cache - // or have a more sophisticated cache that can cache per-column metadata. - // - // Also, if the number of columns is fairly small, it's faster to read them as a - // single IOP, but we can fix this through coalescing. - pub async fn read_all_metadata(scheduler: &FileScheduler) -> Result { - // 1. read the footer + pub(crate) async fn read_raw_metadata_for_dispatch( + scheduler: &FileScheduler, + ) -> Result { let (tail_bytes, file_len) = Self::read_tail(scheduler).await?; let tail_offset = file_len - tail_bytes.len() as u64; let footer = Self::decode_footer(&tail_bytes)?; + let version = + ConcreteFileVersion::from_footer_numbers(footer.major_version, footer.minor_version)?; + if version == ConcreteFileVersion::V1 { + return Ok(RawFileMetadataOpen::Legacy { + major_version: footer.major_version, + minor_version: footer.minor_version, + }); + } - let file_version = LanceFileVersion::try_from_major_minor( - footer.major_version as u32, - footer.minor_version as u32, - )?; - - let gbo_table = - Self::decode_gbo_table(&tail_bytes, file_len, scheduler, &footer, file_version).await?; + // Exact readers validate their own alignment contract after this + // version-free table parse. + let gbo_table = Self::decode_gbo_table(&tail_bytes, file_len, scheduler, &footer).await?; if gbo_table.is_empty() { return Err(Error::internal( "File did not contain any global buffers, schema expected".to_string(), @@ -1028,19 +914,12 @@ impl FileReader { } let schema_start = gbo_table[0].position; let schema_size = gbo_table[0].size; - let num_footer_bytes = file_len - schema_start; - - // By default we read all column metadatas. We do NOT read the column metadata buffers - // at this point. We only want to read the column metadata for columns we are actually loading. let all_metadata_bytes = Self::optimistic_tail_read(&tail_bytes, schema_start, scheduler, file_len).await?; - let schema_bytes = all_metadata_bytes.slice(0..schema_size as usize); let (num_rows, schema) = Self::decode_schema(schema_bytes)?; - // Next, read the metadata for the columns - // This is both the column metadata and the CMO table let column_metadata_start = (footer.column_meta_start - schema_start) as usize; let column_metadata_end = (footer.global_buff_offsets_start - schema_start) as usize; let column_metadata_bytes = @@ -1050,35 +929,28 @@ impl FileReader { let num_global_buffer_bytes = gbo_table.iter().map(|buf| buf.size).sum::(); let num_data_bytes = footer.column_meta_start - num_global_buffer_bytes; let num_column_metadata_bytes = footer.global_buff_offsets_start - footer.column_meta_start; - - let column_infos = Self::meta_to_col_infos(column_metadatas.as_slice(), file_version)?; - - // The tail read above already pulled in any global buffer that lives within - // the captured window. Copy those user buffers (index >= 1; the schema at 0 - // is decoded above and never fetched via read_global_buffer) out of the tail - // so read_global_buffer can serve them without I/O. We copy rather than slice - // so the much larger tail allocation can be released once decoding is done. let retained_global_buffers = Self::retained_global_buffers_from_tail(&gbo_table, &tail_bytes, tail_offset); - Ok(CachedFileMetadata { - file_schema: Arc::new(schema), - column_metadatas, - column_infos, - num_rows, - num_data_bytes, - num_column_metadata_bytes, - num_global_buffer_bytes, - num_footer_bytes, - file_buffers: gbo_table, - major_version: footer.major_version, - minor_version: footer.minor_version, - file_size_bytes: file_len, - retained_global_buffers, + Ok(RawFileMetadataOpen::Current { + version, + metadata: RawFileMetadata { + file_schema: Arc::new(schema), + column_metadatas, + num_rows, + file_buffers: gbo_table, + num_data_bytes, + num_column_metadata_bytes, + num_global_buffer_bytes, + num_footer_bytes, + footer, + file_size_bytes: file_len, + retained_global_buffers, + }, }) } - async fn read_metadata_index_with_known_schema( + async fn read_raw_metadata_index_with_known_schema( scheduler: &FileScheduler, known_schema: Option<(Arc, u64)>, ) -> Result { @@ -1086,13 +958,9 @@ impl FileReader { let tail_offset = file_len - tail_bytes.len() as u64; let footer = Self::decode_footer(&tail_bytes)?; - let file_version = LanceFileVersion::try_from_major_minor( - footer.major_version as u32, - footer.minor_version as u32, - )?; + let file_version = Self::current_file_version(&footer)?; - let gbo_table = - Self::decode_gbo_table(&tail_bytes, file_len, scheduler, &footer, file_version).await?; + let gbo_table = Self::decode_gbo_table(&tail_bytes, file_len, scheduler, &footer).await?; if gbo_table.is_empty() { return Err(Error::internal( "File did not contain any global buffers, schema expected".to_string(), @@ -1143,176 +1011,26 @@ impl FileReader { /// This reads the file schema from the schema global buffer. Use /// [`Self::read_metadata_index_with_schema`] when the caller already has /// the schema and row count from a higher-level metadata source. - pub async fn read_metadata_index(scheduler: &FileScheduler) -> Result { - Self::read_metadata_index_with_known_schema(scheduler, None).await + pub(crate) async fn read_raw_metadata_index( + scheduler: &FileScheduler, + ) -> Result { + Self::read_raw_metadata_index_with_known_schema(scheduler, None).await } /// Reads the metadata index without fetching the schema global buffer. /// /// Use this when the caller already has the file schema and physical row /// count from an enclosing metadata layer, such as a dataset manifest. - pub async fn read_metadata_index_with_schema( + pub(crate) async fn read_raw_metadata_index_with_schema( scheduler: &FileScheduler, file_schema: Arc, num_rows: u64, ) -> Result { - Self::read_metadata_index_with_known_schema(scheduler, Some((file_schema, num_rows))).await - } - - fn fetch_encoding(encoding: &pbfile::Encoding) -> Result { - match &encoding.location { - Some(pbfile::encoding::Location::Indirect(_)) => Err(Error::invalid_input_source( - "Indirect file encodings are not supported".into(), - )), - Some(pbfile::encoding::Location::Direct(encoding)) => { - let encoding_buf = Bytes::from(encoding.encoding.clone()); - let encoding_any = prost_types::Any::decode(encoding_buf).map_err(|error| { - Error::invalid_input_source( - format!("Invalid direct {} encoding envelope: {error}", M::NAME).into(), - ) - })?; - encoding_any.to_msg::().map_err(|error| { - Error::invalid_input_source( - format!("Invalid direct {} encoding: {error}", M::NAME).into(), - ) - }) - } - Some(pbfile::encoding::Location::None(_)) => Err(Error::invalid_input_source( - format!("Missing {} encoding description", M::NAME).into(), - )), - None => Err(Error::invalid_input_source( - format!("Missing {} encoding location", M::NAME).into(), - )), - } - } - - fn meta_to_col_infos( - column_metadatas: &[pbfile::ColumnMetadata], - file_version: LanceFileVersion, - ) -> Result>> { - column_metadatas - .iter() - .enumerate() - .map(|(col_idx, col_meta)| { - let col_idx = u32::try_from(col_idx).map_err(|_| { - Error::invalid_input_source("File has more than u32::MAX columns".into()) - })?; - Self::meta_to_col_info(col_idx, col_meta, file_version) - }) - .collect() - } - - fn meta_to_col_info( - col_idx: u32, - col_meta: &pbfile::ColumnMetadata, - file_version: LanceFileVersion, - ) -> Result> { - let page_infos = col_meta - .pages - .iter() - .enumerate() - .map(|(page_idx, page)| { - let num_rows = page.length; - let encoding = match file_version { - LanceFileVersion::V2_0 => { - PageEncoding::Legacy(Self::fetch_encoding::( - page.encoding.as_ref().ok_or_else(|| { - Error::invalid_input_source( - format!( - "Column {} page {} is missing its encoding", - col_idx, page_idx - ) - .into(), - ) - })?, - )?) - } - _ => { - PageEncoding::Structural(Self::fetch_encoding::( - page.encoding.as_ref().ok_or_else(|| { - Error::invalid_input_source( - format!( - "Column {} page {} is missing its encoding", - col_idx, page_idx - ) - .into(), - ) - })?, - )?) - } - }; - if page.buffer_offsets.len() != page.buffer_sizes.len() { - return Err(Error::invalid_input_source( - format!( - "Column {} page {} has {} buffer offsets but {} buffer sizes", - col_idx, - page_idx, - page.buffer_offsets.len(), - page.buffer_sizes.len() - ) - .into(), - )); - } - let buffer_offsets_and_sizes = Arc::from( - page.buffer_offsets - .iter() - .zip(page.buffer_sizes.iter()) - .map(|(offset, size)| -> Result<_> { - if file_version >= LanceFileVersion::V2_1 - && offset % PAGE_BUFFER_ALIGNMENT as u64 != 0 - { - return Err(Error::invalid_input_source( - format!( - "Column {} page {} buffer offset {} is not aligned to {} bytes", - col_idx, page_idx, offset, PAGE_BUFFER_ALIGNMENT - ) - .into(), - )); - } - Ok((*offset, *size)) - }) - .collect::>>()?, - ); - Ok(PageInfo { - buffer_offsets_and_sizes, - encoding, - num_rows, - priority: page.priority, - }) - }) - .collect::>>()?; - if col_meta.buffer_offsets.len() != col_meta.buffer_sizes.len() { - return Err(Error::invalid_input_source( - format!( - "Column {} has {} buffer offsets but {} buffer sizes", - col_idx, - col_meta.buffer_offsets.len(), - col_meta.buffer_sizes.len() - ) - .into(), - )); - } - let buffer_offsets_and_sizes = Arc::from( - col_meta - .buffer_offsets - .iter() - .zip(col_meta.buffer_sizes.iter()) - .map(|(offset, size)| (*offset, *size)) - .collect::>(), - ); - Ok(Arc::new(ColumnInfo { - index: col_idx, - page_infos: Arc::from(page_infos), - buffer_offsets_and_sizes, - encoding: Self::fetch_encoding(col_meta.encoding.as_ref().ok_or_else(|| { - Error::invalid_input_source( - format!("Column {} is missing its encoding", col_idx).into(), - ) - })?)?, - })) + Self::read_raw_metadata_index_with_known_schema(scheduler, Some((file_schema, num_rows))) + .await } - fn validate_projection( + pub(crate) fn validate_projection( projection: &ReaderProjection, metadata: &CachedFileMetadata, ) -> Result<()> { @@ -1341,68 +1059,6 @@ impl FileReader { Ok(()) } - /// Opens a new file reader without any pre-existing knowledge - /// - /// This will read the file schema from the file itself and thus requires a bit more I/O - /// - /// A `base_projection` can also be provided. If provided, then the projection will apply - /// to all reads from the file that do not specify their own projection. - pub async fn try_open( - scheduler: FileScheduler, - base_projection: Option, - decoder_plugins: Arc, - cache: &LanceCache, - options: FileReaderOptions, - ) -> Result { - let file_metadata = Arc::new(Self::read_all_metadata(&scheduler).await?); - let path = scheduler.reader().path().clone(); - - // Create LanceEncodingsIo with read chunk size from options - let encodings_io = - LanceEncodingsIo::new(scheduler).with_read_chunk_size(options.read_chunk_size); - - Self::try_open_with_file_metadata( - Arc::new(encodings_io), - path, - base_projection, - decoder_plugins, - file_metadata, - cache, - options, - ) - .await - } - - /// Same as `try_open` but with the file metadata already loaded. - /// - /// This method also can accept any kind of `EncodingsIo` implementation allowing - /// for custom strategies to be used for I/O scheduling (e.g. for takes on fast - /// disks it may be better to avoid asynchronous overhead). - /// Opens a data reader backed by fully decoded file metadata. - pub async fn try_open_with_file_metadata( - scheduler: Arc, - path: Path, - base_projection: Option, - decoder_plugins: Arc, - file_metadata: Arc, - cache: &LanceCache, - options: FileReaderOptions, - ) -> Result { - let cache = Arc::new(cache.with_key_prefix(path.as_ref())); - let core = FileReadCore::try_new( - scheduler, - base_projection, - decoder_plugins, - FileMetadataProvider::Full(file_metadata.clone()), - cache, - options, - )?; - Ok(Self { - core, - metadata: file_metadata, - }) - } - // The actual decoder needs all the column infos that make up a type. In other words, if // the first type in the schema is Struct then the decoder will need 3 column infos. // @@ -1558,97 +1214,11 @@ impl FileReader { .await } - /// Creates a stream of "read tasks" to read the data from the file - /// - /// The arguments are similar to [`Self::read_stream_projected`] but instead of returning a stream - /// of record batches it returns a stream of "read tasks". - /// - /// The tasks should be consumed with some kind of `buffered` argument if CPU parallelism is desired. - /// - /// Note that "read task" is probably a bit imprecise. The tasks are actually "decode tasks". The - /// reading happens asynchronously in the background. In other words, a single read task may map to - /// multiple I/O operations or a single I/O operation may map to multiple read tasks. - /// - /// # Why is this async? - /// - /// Constructing the read stream requires running the decode scheduler's - /// `initialize` step, which performs the metadata I/O (chunk metadata, - /// dictionaries, repetition index, ...) needed to plan the read. We - /// drive that I/O on the awaiting task rather than smuggling it into - /// the stream's first poll. This way callers control where the - /// scheduling I/O runs (typically inside a per-fragment - /// `tokio::spawn`), planning errors surface from the await instead of - /// from the first stream item, and small reads can also complete the - /// synchronous scheduling step before returning (see - /// [`DecoderConfig::inline_scheduling`]). - pub async fn read_tasks( + fn take_rows_blocking( &self, - params: ReadBatchParams, + indices: Vec, batch_size: u32, - projection: Option, - filter: FilterExpression, - ) -> Result + Send>>> { - self.core - .read_tasks(params, batch_size, projection, filter) - .await - } - - /// Reads data from the file as a stream of record batches - /// - /// * `params` - Specifies the range (or indices) of data to read - /// * `batch_size` - The maximum size of a single batch. A batch may be smaller - /// if it is the last batch or if it is not possible to create a batch of the - /// requested size. - /// - /// For example, if the batch size is 1024 and one of the columns is a string - /// column then there may be some ranges of 1024 rows that contain more than - /// 2^31 bytes of string data (which is the maximum size of a string column - /// in Arrow). In this case smaller batches may be emitted. - /// * `batch_readahead` - The number of batches to read ahead. This controls the - /// amount of CPU parallelism of the read. In other words it controls how many - /// batches will be decoded in parallel. It has no effect on the I/O parallelism - /// of the read (how many I/O requests are in flight at once). - /// - /// This parameter also is also related to backpressure. If the consumer of the - /// stream is slow then the reader will build up RAM. - /// * `projection` - A projection to apply to the read. This controls which columns - /// are read from the file. The projection is NOT applied on top of the base - /// projection. The projection is applied directly to the file schema. - /// - /// # Why is this async? - /// - /// This delegates to [`Self::read_tasks`], which awaits the decode - /// scheduler's `initialize` step (and, for small reads, the synchronous - /// scheduling that follows) before returning. See `read_tasks` for - /// details on why this work is performed up front rather than on the - /// stream's first poll. - pub async fn read_stream_projected( - &self, - params: ReadBatchParams, - batch_size: u32, - batch_readahead: u32, - projection: ReaderProjection, - filter: FilterExpression, - ) -> Result>> { - let arrow_schema = Arc::new(ArrowSchema::from(projection.schema.as_ref())); - let tasks_stream = self - .read_tasks(params, batch_size, Some(projection), filter) - .await?; - let batch_stream = tasks_stream - .map(|task| task.task) - .buffered(batch_readahead as usize) - .boxed(); - Ok(Box::pin(RecordBatchStreamAdapter::new( - arrow_schema, - batch_stream, - ))) - } - - fn take_rows_blocking( - &self, - indices: Vec, - batch_size: u32, - projection: ReaderProjection, + projection: ReaderProjection, filter: FilterExpression, ) -> Result> { let column_infos = self.collect_columns_from_projection(&projection)?; @@ -1762,36 +1332,15 @@ impl FileReader { ) } - /// Read data from the file as an iterator of record batches - /// - /// This is a blocking variant of [`Self::read_stream_projected`] that runs entirely in the - /// calling thread. It will block on I/O if the decode is faster than the I/O. It is useful - /// for benchmarking and potentially from "take"ing small batches from fast disks. - /// - /// Large scans of in-memory data will still benefit from threading (and should therefore not - /// use this method) because we can parallelize the decode. - /// - /// Note: calling this from within a tokio runtime will panic. It is acceptable to call this - /// from a spawn_blocking context. - pub fn read_stream_projected_blocking( + pub(crate) fn read_prepared_blocking( &self, params: ReadBatchParams, batch_size: u32, - projection: Option, + prepared: PreparedProjection, + read_len: u64, filter: FilterExpression, ) -> Result> { - let projection = projection.unwrap_or_else(|| self.core.base_projection.clone()); - Self::validate_projection(&projection, &self.metadata)?; - // Apply the same projection-length validation as the async path. This - // reader is always backed by full metadata, so we can build the prepared - // projection synchronously (no column-metadata I/O) and reuse the shared - // check. `read_len` is the projection's common column length, which - // `RangeFull`/`RangeFrom` resolve against rather than `num_rows`. - let prepared = PreparedProjection { - column_infos: self.metadata.column_infos.clone(), - decoder_projection: projection.clone(), - }; - let read_len = self.core.prepared_read_length(&prepared)?; + let projection = prepared.decoder_projection; let verify_bound = |params: &ReadBatchParams, bound: u64, inclusive: bool| { if bound > read_len || (bound == read_len && inclusive) { Err(Error::invalid_input(format!( @@ -1803,17 +1352,13 @@ impl FileReader { }; match ¶ms { ReadBatchParams::Indices(indices) => { - for idx in indices { - match idx { - None => { - return Err(Error::invalid_input("Null value in indices array")); - } - Some(idx) => { - verify_bound(¶ms, idx as u64, true)?; - } + for index in indices { + match index { + None => return Err(Error::invalid_input("Null value in indices array")), + Some(index) => verify_bound(¶ms, index as u64, true)?, } } - let indices = indices.iter().map(|idx| idx.unwrap() as u64).collect(); + let indices = indices.iter().map(|index| index.unwrap() as u64).collect(); self.take_rows_blocking(indices, batch_size, projection, filter) } ReadBatchParams::Range(range) => { @@ -1852,76 +1397,48 @@ impl FileReader { } } - /// Reads data from the file as a stream of record batches - /// - /// This is similar to [`Self::read_stream_projected`] but uses the base projection - /// provided when the file was opened (or reads all columns if the file was - /// opened without a base projection) - /// - /// # Why is this async? - /// - /// This delegates to [`Self::read_stream_projected`], which awaits the - /// decode scheduler's `initialize` step before returning the stream. - /// See [`Self::read_tasks`] for the rationale. - pub async fn read_stream( - &self, - params: ReadBatchParams, - batch_size: u32, - batch_readahead: u32, - filter: FilterExpression, - ) -> Result>> { - self.read_stream_projected( - params, - batch_size, - batch_readahead, - self.core.base_projection.clone(), - filter, - ) - .await - } - pub fn schema(&self) -> &Arc { self.core.schema() } } impl FileMetadataProvider { - fn version(&self) -> LanceFileVersion { + pub(crate) fn version(&self) -> ConcreteFileVersion { match self { - Self::Full(metadata) => metadata.version(), + Self::Full(metadata) => metadata.version, Self::Indexed(metadata_index) => metadata_index.version, } } - fn num_rows(&self) -> u64 { + pub(crate) fn num_rows(&self) -> u64 { match self { Self::Full(metadata) => metadata.num_rows, Self::Indexed(metadata_index) => metadata_index.num_rows, } } - fn schema(&self) -> &Arc { + pub(crate) fn schema(&self) -> &Arc { match self { Self::Full(metadata) => &metadata.file_schema, Self::Indexed(metadata_index) => &metadata_index.file_schema, } } - fn file_buffers(&self) -> &Vec { + pub(crate) fn file_buffers(&self) -> &Vec { match self { Self::Full(metadata) => &metadata.file_buffers, Self::Indexed(metadata_index) => &metadata_index.file_buffers, } } - fn retained_global_buffers(&self) -> &BTreeMap { + pub(crate) fn retained_global_buffers(&self) -> &BTreeMap { match self { Self::Full(metadata) => &metadata.retained_global_buffers, Self::Indexed(metadata_index) => &metadata_index.retained_global_buffers, } } - fn file_statistics(&self) -> Option { + pub(crate) fn file_statistics(&self) -> Option { let metadata = match self { Self::Full(metadata) => metadata, Self::Indexed(_) => return None, @@ -1931,11 +1448,8 @@ impl FileMetadataProvider { )) } - fn supports_indexed_projection( - projection: &ReaderProjection, - version: LanceFileVersion, - ) -> bool { - if version < LanceFileVersion::V2_1 || projection.schema.fields.is_empty() { + pub(crate) fn projection_matches_indexed_metadata(projection: &ReaderProjection) -> bool { + if projection.schema.fields.is_empty() { return false; } @@ -1949,7 +1463,7 @@ impl FileMetadataProvider { == Some(projection.column_indices.len()) } - fn validate_indexed_projection( + pub(crate) fn validate_indexed_projection_structure( projection: &ReaderProjection, metadata_index: &FileMetadataIndex, ) -> Result<()> { @@ -1974,24 +1488,19 @@ impl FileMetadataProvider { ))); } } - if !Self::supports_indexed_projection(projection, metadata_index.version) { - return Err(Error::not_supported(format!( - "lazy column metadata loading requires a V2.1+ ordinary structural projection without blob or packed-struct fields whose physical-column count matches the projection; got file version {:?}, {} schema fields, and {} column indices", - metadata_index.version, - projection.schema.fields.len(), - projection.column_indices.len() - ))); - } Ok(()) } - fn validate_projection(&self, projection: &ReaderProjection) -> Result<()> { - match self { - Self::Full(metadata) => FileReader::validate_projection(projection, metadata), - Self::Indexed(metadata_index) => { - Self::validate_indexed_projection(projection, metadata_index) - } - } + pub(crate) fn indexed_projection_error( + projection: &ReaderProjection, + metadata_index: &FileMetadataIndex, + ) -> Error { + Error::not_supported(format!( + "lazy column metadata loading requires a V2.1+ ordinary structural projection without blob or packed-struct fields whose physical-column count matches the projection; got file version {:?}, {} schema fields, and {} column indices", + metadata_index.version, + projection.schema.fields.len(), + projection.column_indices.len() + )) } fn column_metadata_range( @@ -2017,12 +1526,16 @@ impl FileMetadataProvider { Ok(position..end) } - async fn load_indexed_column_infos( + pub(crate) async fn load_indexed_column_infos( metadata_index: &FileMetadataIndex, io: &Arc, cache: &Arc, column_indices: &[u32], - ) -> Result>> { + decode_column: F, + ) -> Result>> + where + F: Fn(u32, &pbfile::ColumnMetadata) -> Result>, + { let mut column_infos = vec![None; column_indices.len()]; let mut missing_columns = Vec::new(); @@ -2047,11 +1560,7 @@ impl FileMetadataProvider { missing_columns.into_iter().zip(metadata_bytes) { let column_metadata = pbfile::ColumnMetadata::decode(bytes)?; - let column_info = FileReader::meta_to_col_info( - column_index, - &column_metadata, - metadata_index.version, - )?; + let column_info = decode_column(column_index, &column_metadata)?; let cached = Arc::new(CachedColumnMetadata { column_metadata, column_info: column_info.clone(), @@ -2075,83 +1584,41 @@ impl FileMetadataProvider { }) .collect() } - - async fn prepare_projection( - &self, - projection: &ReaderProjection, - io: &Arc, - cache: &Arc, - ) -> Result { - self.validate_projection(projection)?; - match self { - Self::Full(metadata) => Ok(PreparedProjection { - column_infos: metadata.column_infos.clone(), - decoder_projection: projection.clone(), - }), - Self::Indexed(metadata_index) => { - let column_infos = Self::load_indexed_column_infos( - metadata_index, - io, - cache, - &projection.column_indices, - ) - .await?; - let decoder_projection = ReaderProjection { - schema: projection.schema.clone(), - column_indices: (0..projection.column_indices.len()) - .map(|idx| idx as u32) - .collect(), - }; - Ok(PreparedProjection { - column_infos, - decoder_projection, - }) - } - } - } } -impl FileReadCore { - fn try_new( +impl DecodeEngine { + pub(crate) fn try_new( scheduler: Arc, - base_projection: Option, + base_projection: ReaderProjection, decoder_plugins: Arc, metadata_provider: FileMetadataProvider, + read_projection: Arc, cache: Arc, options: FileReaderOptions, ) -> Result { - if let Some(base_projection) = base_projection.as_ref() { - metadata_provider.validate_projection(base_projection)?; - } - let base_projection = base_projection.unwrap_or(ReaderProjection::from_whole_schema( - metadata_provider.schema().as_ref(), - metadata_provider.version(), - )); Ok(Self { scheduler, base_projection, metadata_provider, + read_projection, decoder_plugins, cache, options, }) } - fn with_scheduler(&self, scheduler: Arc) -> Self { + pub(crate) fn with_scheduler(&self, scheduler: Arc) -> Self { Self { scheduler, base_projection: self.base_projection.clone(), metadata_provider: self.metadata_provider.clone(), + read_projection: self.read_projection.clone(), decoder_plugins: self.decoder_plugins.clone(), cache: self.cache.clone(), options: self.options.clone(), } } - fn version(&self) -> LanceFileVersion { - self.metadata_provider.version() - } - fn num_rows(&self) -> u64 { self.metadata_provider.num_rows() } @@ -2160,7 +1627,7 @@ impl FileReadCore { self.metadata_provider.schema() } - async fn read_global_buffer(&self, index: u32) -> Result { + pub(crate) async fn read_global_buffer(&self, index: u32) -> Result { let file_buffers = self.metadata_provider.file_buffers(); let buffer_desc = file_buffers.get(index as usize).ok_or_else(|| { Error::invalid_input(format!( @@ -2189,82 +1656,6 @@ impl FileReadCore { }) } - // The common length to read across a prepared projection, after validating - // its columns can be combined into rectangular batches. Each top-level field - // is checked for internal consistency (see `validate_field_length`); the - // top-level fields must then share a length, since one read combines them. - // Ordinary files always pass (every column has `num_rows` rows); files - // written with `FileWriter::write_column` whose columns ended up unequal are - // rejected here and must be read separately. - // - // `column_infos` and `decoder_projection.column_indices` line up for both - // metadata providers: the full provider keeps absolute indices into the whole - // file, while the indexed (lazy) provider loads only the projected columns and - // renumbers them 0..N -- in either case `column_infos[column_index]` is the - // requested column. - fn prepared_read_length(&self, prepared: &PreparedProjection) -> Result { - let is_structural = self.version() >= LanceFileVersion::V2_1; - let column_infos = &prepared.column_infos; - let column_len = |column: usize| -> Result { - let info = column_infos.get(column).ok_or_else(|| { - Error::invalid_input(format!( - "projection references column index {} but only {} columns are available", - column, - column_infos.len() - )) - })?; - info.page_infos.iter().try_fold(0_u64, |rows, page| { - let page_rows = match &page.encoding { - PageEncoding::Structural(layout) => match &layout.layout { - Some(pbenc21::page_layout::Layout::SparseLayout(sparse)) => sparse - .structural_layers - .first() - .and_then(|layer| layer.layer.as_ref()) - .map_or(page.num_rows, |layer| match layer { - pbenc21::sparse_structural_layer::Layer::Validity(layer) => { - layer.num_slots - } - pbenc21::sparse_structural_layer::Layer::List(layer) => { - layer.num_slots - } - pbenc21::sparse_structural_layer::Layer::FixedSizeList(layer) => { - layer.num_slots - } - }), - _ => page.num_rows, - }, - _ => page.num_rows, - }; - rows.checked_add(page_rows).ok_or_else(|| { - Error::invalid_input_source("Column row count overflows u64".into()) - }) - }) - }; - let column_indices = &prepared.decoder_projection.column_indices; - let fields = &prepared.decoder_projection.schema.fields; - let mut cursor = 0usize; - let mut field_lengths = Vec::with_capacity(fields.len()); - for field in fields { - let rows = validate_field_length( - field, - is_structural, - true, - column_indices, - &mut cursor, - &column_len, - )?; - field_lengths.push((field.name.as_str(), rows)); - } - if cursor != column_indices.len() { - return Err(Error::invalid_input(format!( - "projection supplied {} column indices but its fields require {}", - column_indices.len(), - cursor - ))); - } - verify_uniform_lengths(&field_lengths) - } - async fn read_range( &self, range: Range, @@ -2288,134 +1679,389 @@ impl FileReadCore { .await } - async fn take_rows( + async fn take_rows( + &self, + indices: Vec, + batch_size: u32, + prepared: PreparedProjection, + ) -> Result> { + FileReader::do_take_rows( + prepared.column_infos, + self.scheduler.clone(), + self.cache.clone(), + self.decoder_plugins.clone(), + indices, + batch_size, + prepared.decoder_projection, + FilterExpression::no_filter(), + self.options.decoder_config.clone(), + self.options.batch_size_bytes, + ) + .await + } + + async fn read_ranges( + &self, + ranges: Vec>, + batch_size: u32, + prepared: PreparedProjection, + filter: FilterExpression, + ) -> Result> { + FileReader::do_read_ranges( + prepared.column_infos, + self.scheduler.clone(), + self.cache.clone(), + self.decoder_plugins.clone(), + ranges, + batch_size, + prepared.decoder_projection, + filter, + self.options.decoder_config.clone(), + self.options.batch_size_bytes, + ) + .await + } + + pub(crate) async fn read_prepared_tasks( + &self, + params: ReadBatchParams, + batch_size: u32, + prepared: PreparedProjection, + read_len: u64, + filter: FilterExpression, + ) -> Result + Send>>> { + let verify_bound = |params: &ReadBatchParams, bound: u64, inclusive: bool| { + if bound > read_len || (bound == read_len && inclusive) { + Err(Error::invalid_input(format!( + "cannot read {params:?} from columns with {read_len} rows" + ))) + } else { + Ok(()) + } + }; + match ¶ms { + ReadBatchParams::Indices(indices) => { + for idx in indices { + match idx { + None => { + return Err(Error::invalid_input("Null value in indices array")); + } + Some(idx) => { + verify_bound(¶ms, idx as u64, true)?; + } + } + } + let indices = indices.iter().map(|idx| idx.unwrap() as u64).collect(); + self.take_rows(indices, batch_size, prepared).await + } + ReadBatchParams::Range(range) => { + verify_bound(¶ms, range.end as u64, false)?; + self.read_range( + range.start as u64..range.end as u64, + batch_size, + prepared, + filter, + ) + .await + } + ReadBatchParams::Ranges(ranges) => { + let mut ranges_u64 = Vec::with_capacity(ranges.len()); + for range in ranges.as_ref() { + verify_bound(¶ms, range.end, false)?; + ranges_u64.push(range.start..range.end); + } + self.read_ranges(ranges_u64, batch_size, prepared, filter) + .await + } + ReadBatchParams::RangeFrom(range) => { + verify_bound(¶ms, range.start as u64, true)?; + self.read_range(range.start as u64..read_len, batch_size, prepared, filter) + .await + } + ReadBatchParams::RangeTo(range) => { + verify_bound(¶ms, range.end as u64, false)?; + self.read_range(0..range.end as u64, batch_size, prepared, filter) + .await + } + ReadBatchParams::RangeFull => { + self.read_range(0..read_len, batch_size, prepared, filter) + .await + } + } + } +} + +impl ProjectedFileReader { + pub(crate) fn base_projection(&self) -> &ReaderProjection { + &self.core.base_projection + } + + pub(crate) async fn read_prepared_tasks( + &self, + params: ReadBatchParams, + batch_size: u32, + prepared: PreparedProjection, + read_len: u64, + filter: FilterExpression, + ) -> Result + Send>>> { + self.core + .read_prepared_tasks(params, batch_size, prepared, read_len, filter) + .await + } + + /// Returns a clone of this reader using a different scheduler. + pub fn with_scheduler(&self, scheduler: Arc) -> Self { + Self { + core: self.core.with_scheduler(scheduler), + } + } + + /// Returns the number of rows in the file. + pub fn num_rows(&self) -> u64 { + self.core.num_rows() + } + + /// Returns the file schema visible to this reader. + pub fn schema(&self) -> &Arc { + self.core.schema() + } + + /// Returns file statistics when this reader has full file metadata. + pub fn file_statistics(&self) -> Option { + self.core.metadata_provider.file_statistics() + } + + #[cfg(test)] + pub(crate) fn metadata_index(&self) -> Option<&Arc> { + match &self.core.metadata_provider { + FileMetadataProvider::Indexed(metadata_index) => Some(metadata_index), + FileMetadataProvider::Full(_) => None, + } + } + + /// Reads a global buffer by index. + pub async fn read_global_buffer(&self, index: u32) -> Result { + self.core.read_global_buffer(index).await + } +} + +impl FileReader { + #[cfg(test)] + fn scheduler(&self) -> Arc { + self.core.scheduler.clone() + } + + pub async fn try_open( + scheduler: FileScheduler, + base_projection: Option, + decoder_plugins: Arc, + cache: &LanceCache, + options: FileReaderOptions, + ) -> Result { + match Self::try_open_for_dispatch( + scheduler, + base_projection, + decoder_plugins, + cache, + options, + ) + .await? + { + versions::OpenedFileReader::V1 { + major_version, + minor_version, + } => Err(Error::version_conflict( + "Attempt to use the Lance current-format reader to read a v1 file".to_string(), + major_version, + minor_version, + )), + versions::OpenedFileReader::Current(reader) => Ok(reader), + } + } + + pub(crate) async fn try_open_for_dispatch( + scheduler: FileScheduler, + base_projection: Option, + decoder_plugins: Arc, + cache: &LanceCache, + options: FileReaderOptions, + ) -> Result { + let metadata = match Self::read_raw_metadata_for_dispatch(&scheduler).await? { + RawFileMetadataOpen::Legacy { + major_version, + minor_version, + } => { + return Ok(versions::OpenedFileReader::V1 { + major_version, + minor_version, + }); + } + RawFileMetadataOpen::Current { version, metadata } => { + Arc::new(versions::finish_metadata(version, metadata)?) + } + }; + let path = scheduler.reader().path().clone(); + let io = Arc::new( + LanceEncodingsIo::new(scheduler).with_read_chunk_size(options.read_chunk_size), + ); + Self::try_open_with_file_metadata( + io, + path, + base_projection, + decoder_plugins, + metadata, + cache, + options, + ) + .await + .map(versions::OpenedFileReader::Current) + } + + pub async fn try_open_with_file_metadata( + scheduler: Arc, + path: Path, + base_projection: Option, + decoder_plugins: Arc, + metadata: Arc, + cache: &LanceCache, + options: FileReaderOptions, + ) -> Result { + if metadata.version == ConcreteFileVersion::V1 { + return Err(Error::version_conflict( + "Attempt to use the Lance current-format reader with v1 metadata".to_string(), + metadata.major_version, + metadata.minor_version, + )); + } + let read_projection = versions::read_projection(metadata.version)?; + let has_explicit_projection = base_projection.is_some(); + let base_projection = base_projection.unwrap_or_else(|| { + versions::reader_projection_from_whole_schema(&metadata.file_schema, metadata.version) + }); + if has_explicit_projection { + Self::validate_projection(&base_projection, &metadata)?; + } + let cache = Arc::new(cache.with_key_prefix(path.as_ref())); + let core = DecodeEngine::try_new( + scheduler, + base_projection, + decoder_plugins, + FileMetadataProvider::Full(metadata.clone()), + read_projection, + cache, + options, + )?; + Ok(Self { core, metadata }) + } + + pub async fn read_all_metadata(scheduler: &FileScheduler) -> Result { + match Self::read_raw_metadata_for_dispatch(scheduler).await? { + RawFileMetadataOpen::Legacy { + major_version, + minor_version, + } => Err(Error::version_conflict( + "Attempt to use the Lance current-format reader to read v1 metadata".to_string(), + major_version, + minor_version, + )), + RawFileMetadataOpen::Current { version, metadata } => { + versions::finish_metadata(version, metadata) + } + } + } + + pub async fn read_metadata_index(scheduler: &FileScheduler) -> Result { + let index = Self::read_raw_metadata_index(scheduler).await?; + versions::finish_metadata_index(index) + } + + pub async fn read_metadata_index_with_schema( + scheduler: &FileScheduler, + file_schema: Arc, + num_rows: u64, + ) -> Result { + let index = + Self::read_raw_metadata_index_with_schema(scheduler, file_schema, num_rows).await?; + versions::finish_metadata_index(index) + } + + pub fn version(&self) -> ConcreteFileVersion { + self.metadata.version + } + + async fn prepare(&self, projection: ReaderProjection) -> Result<(PreparedProjection, u64)> { + self.core + .read_projection + .prepare( + &self.core.metadata_provider, + &projection, + &self.core.scheduler, + &self.core.cache, + ) + .await + } + + pub async fn read_tasks( + &self, + params: ReadBatchParams, + batch_size: u32, + projection: Option, + filter: FilterExpression, + ) -> Result + Send>>> { + let projection = projection.unwrap_or_else(|| self.base_projection().clone()); + let (prepared, read_len) = self.prepare(projection).await?; + self.read_prepared_tasks(params, batch_size, prepared, read_len, filter) + .await + } + + pub async fn read_stream_projected( + &self, + params: ReadBatchParams, + batch_size: u32, + batch_readahead: u32, + projection: ReaderProjection, + filter: FilterExpression, + ) -> Result>> { + let schema = projection.schema.clone(); + let tasks = self + .read_tasks(params, batch_size, Some(projection), filter) + .await?; + Ok(tasks_to_record_batch_stream(schema, tasks, batch_readahead)) + } + + pub fn read_stream_projected_blocking( &self, - indices: Vec, + params: ReadBatchParams, batch_size: u32, - prepared: PreparedProjection, - ) -> Result> { - FileReader::do_take_rows( - prepared.column_infos, - self.scheduler.clone(), - self.cache.clone(), - self.decoder_plugins.clone(), - indices, - batch_size, - prepared.decoder_projection, - FilterExpression::no_filter(), - self.options.decoder_config.clone(), - self.options.batch_size_bytes, - ) - .await + projection: Option, + filter: FilterExpression, + ) -> Result> { + let projection = projection.unwrap_or_else(|| self.base_projection().clone()); + Self::validate_projection(&projection, self.metadata())?; + let prepared = self.full_projection(projection); + let read_len = self.core.read_projection.read_length(&prepared)?; + self.read_prepared_blocking(params, batch_size, prepared, read_len, filter) } - async fn read_ranges( + pub async fn read_stream( &self, - ranges: Vec>, + params: ReadBatchParams, batch_size: u32, - prepared: PreparedProjection, + batch_readahead: u32, filter: FilterExpression, - ) -> Result> { - FileReader::do_read_ranges( - prepared.column_infos, - self.scheduler.clone(), - self.cache.clone(), - self.decoder_plugins.clone(), - ranges, + ) -> Result>> { + self.read_stream_projected( + params, batch_size, - prepared.decoder_projection, + batch_readahead, + self.base_projection().clone(), filter, - self.options.decoder_config.clone(), - self.options.batch_size_bytes, ) .await } - - async fn read_tasks( - &self, - params: ReadBatchParams, - batch_size: u32, - projection: Option, - filter: FilterExpression, - ) -> Result + Send>>> { - let projection = projection.unwrap_or_else(|| self.base_projection.clone()); - let prepared = self - .metadata_provider - .prepare_projection(&projection, &self.scheduler, &self.cache) - .await?; - // All projected columns must share a length: the reader combines them - // into rectangular batches. Ordinary files satisfy this (every column - // has `num_rows` rows); files written with `FileWriter::write_column` - // may not, and such columns must be read separately. `read_len` is that - // common length, which `RangeFull`/`RangeFrom` resolve against (rather - // than `num_rows`, the file's longest column). - let read_len = self.prepared_read_length(&prepared)?; - let verify_bound = |params: &ReadBatchParams, bound: u64, inclusive: bool| { - if bound > read_len || (bound == read_len && inclusive) { - Err(Error::invalid_input(format!( - "cannot read {params:?} from columns with {read_len} rows" - ))) - } else { - Ok(()) - } - }; - match ¶ms { - ReadBatchParams::Indices(indices) => { - for idx in indices { - match idx { - None => { - return Err(Error::invalid_input("Null value in indices array")); - } - Some(idx) => { - verify_bound(¶ms, idx as u64, true)?; - } - } - } - let indices = indices.iter().map(|idx| idx.unwrap() as u64).collect(); - self.take_rows(indices, batch_size, prepared).await - } - ReadBatchParams::Range(range) => { - verify_bound(¶ms, range.end as u64, false)?; - self.read_range( - range.start as u64..range.end as u64, - batch_size, - prepared, - filter, - ) - .await - } - ReadBatchParams::Ranges(ranges) => { - let mut ranges_u64 = Vec::with_capacity(ranges.len()); - for range in ranges.as_ref() { - verify_bound(¶ms, range.end, false)?; - ranges_u64.push(range.start..range.end); - } - self.read_ranges(ranges_u64, batch_size, prepared, filter) - .await - } - ReadBatchParams::RangeFrom(range) => { - verify_bound(¶ms, range.start as u64, true)?; - self.read_range(range.start as u64..read_len, batch_size, prepared, filter) - .await - } - ReadBatchParams::RangeTo(range) => { - verify_bound(¶ms, range.end as u64, false)?; - self.read_range(0..range.end as u64, batch_size, prepared, filter) - .await - } - ReadBatchParams::RangeFull => { - self.read_range(0..read_len, batch_size, prepared, filter) - .await - } - } - } } impl ProjectedFileReader { - /// Opens a data reader backed by indexed column metadata. - /// - /// `base_projection` must be a supported indexed projection. Reads that do - /// not pass an explicit projection use this base projection. pub async fn try_open( scheduler: FileScheduler, base_projection: Option, @@ -2423,13 +2069,16 @@ impl ProjectedFileReader { cache: &LanceCache, options: FileReaderOptions, ) -> Result { - let base_projection = Self::require_indexed_base_projection(base_projection)?; + let base_projection = base_projection.ok_or_else(|| { + Error::invalid_input("ProjectedReader requires an explicit base projection") + })?; let metadata_index = Arc::new(FileReader::read_metadata_index(&scheduler).await?); let path = scheduler.reader().path().clone(); - let encodings_io = - LanceEncodingsIo::new(scheduler).with_read_chunk_size(options.read_chunk_size); + let io = Arc::new( + LanceEncodingsIo::new(scheduler).with_read_chunk_size(options.read_chunk_size), + ); Self::try_open_with_metadata_index( - Arc::new(encodings_io), + io, path, Some(base_projection), decoder_plugins, @@ -2440,11 +2089,6 @@ impl ProjectedFileReader { .await } - /// Opens a data reader from a previously loaded metadata index. - /// - /// `base_projection` must be a supported indexed projection. Use - /// [`Self::try_open_with_file_metadata`] when the default read should cover - /// the whole file schema. pub async fn try_open_with_metadata_index( scheduler: Arc, path: Path, @@ -2454,94 +2098,74 @@ impl ProjectedFileReader { cache: &LanceCache, options: FileReaderOptions, ) -> Result { - let base_projection = Self::require_indexed_base_projection(base_projection)?; + if metadata_index.version == ConcreteFileVersion::V1 { + return Err(Error::version_conflict( + "Attempt to use the Lance projected current-format reader with v1 metadata" + .to_string(), + 0, + 2, + )); + } + let base_projection = base_projection.ok_or_else(|| { + Error::invalid_input("ProjectedReader requires an explicit base projection") + })?; + let read_projection = versions::read_projection(metadata_index.version)?; + read_projection.validate_indexed(&base_projection, &metadata_index)?; let cache = Arc::new(cache.with_key_prefix(path.as_ref())); - let core = FileReadCore::try_new( + let core = DecodeEngine::try_new( scheduler, - Some(base_projection), + base_projection, decoder_plugins, FileMetadataProvider::Indexed(metadata_index), + read_projection, cache, options, )?; Ok(Self { core }) } - fn require_indexed_base_projection( - base_projection: Option, - ) -> Result { - base_projection.ok_or_else(|| { - Error::invalid_input("ProjectedFileReader requires an explicit base projection") - }) - } - pub async fn try_open_with_file_metadata( scheduler: Arc, path: Path, base_projection: Option, decoder_plugins: Arc, - file_metadata: Arc, + metadata: Arc, cache: &LanceCache, options: FileReaderOptions, ) -> Result { + if metadata.version == ConcreteFileVersion::V1 { + return Err(Error::version_conflict( + "Attempt to use the Lance projected current-format reader with v1 metadata" + .to_string(), + metadata.major_version, + metadata.minor_version, + )); + } + let read_projection = versions::read_projection(metadata.version)?; + let has_explicit_projection = base_projection.is_some(); + let base_projection = base_projection.unwrap_or_else(|| { + versions::reader_projection_from_whole_schema(&metadata.file_schema, metadata.version) + }); + if has_explicit_projection { + FileReader::validate_projection(&base_projection, &metadata)?; + } let cache = Arc::new(cache.with_key_prefix(path.as_ref())); - let core = FileReadCore::try_new( + let core = DecodeEngine::try_new( scheduler, base_projection, decoder_plugins, - FileMetadataProvider::Full(file_metadata), + FileMetadataProvider::Full(metadata), + read_projection, cache, options, )?; Ok(Self { core }) } - /// Returns whether a projection can be served by indexed column metadata. - pub fn supports_projection(projection: &ReaderProjection, version: LanceFileVersion) -> bool { - FileMetadataProvider::supports_indexed_projection(projection, version) - } - - /// Returns a clone of this reader using a different scheduler. - pub fn with_scheduler(&self, scheduler: Arc) -> Self { - Self { - core: self.core.with_scheduler(scheduler), - } - } - - /// Returns the Lance file version. - pub fn version(&self) -> LanceFileVersion { - self.core.version() - } - - /// Returns the number of rows in the file. - pub fn num_rows(&self) -> u64 { - self.core.num_rows() - } - - /// Returns the file schema visible to this reader. - pub fn schema(&self) -> &Arc { - self.core.schema() - } - - /// Returns file statistics when this reader has full file metadata. - pub fn file_statistics(&self) -> Option { - self.core.metadata_provider.file_statistics() - } - - #[cfg(test)] - fn metadata_index(&self) -> Option<&Arc> { - match &self.core.metadata_provider { - FileMetadataProvider::Indexed(metadata_index) => Some(metadata_index), - FileMetadataProvider::Full(_) => None, - } - } - - /// Reads a global buffer by index. - pub async fn read_global_buffer(&self, index: u32) -> Result { - self.core.read_global_buffer(index).await + pub fn version(&self) -> ConcreteFileVersion { + self.core.metadata_provider.version() } - /// Creates a stream of read tasks for the requested rows and projection. pub async fn read_tasks( &self, params: ReadBatchParams, @@ -2549,8 +2173,18 @@ impl ProjectedFileReader { projection: Option, filter: FilterExpression, ) -> Result + Send>>> { - self.core - .read_tasks(params, batch_size, projection, filter) + let projection = projection.unwrap_or_else(|| self.base_projection().clone()); + let (prepared, read_len) = self + .core + .read_projection + .prepare( + &self.core.metadata_provider, + &projection, + &self.core.scheduler, + &self.core.cache, + ) + .await?; + self.read_prepared_tasks(params, batch_size, prepared, read_len, filter) .await } } @@ -2605,11 +2239,7 @@ pub fn describe_encoding(page: &pbfile::column_metadata::Page) -> String { } pub trait EncodedBatchReaderExt { - fn try_from_mini_lance( - bytes: Bytes, - schema: &Schema, - version: LanceFileVersion, - ) -> Result + fn try_from_mini_lance(bytes: Bytes, schema: &Schema) -> Result where Self: Sized; fn try_from_self_described_lance(bytes: Bytes) -> Result @@ -2618,16 +2248,13 @@ pub trait EncodedBatchReaderExt { } impl EncodedBatchReaderExt for EncodedBatch { - fn try_from_mini_lance( - bytes: Bytes, - schema: &Schema, - file_version: LanceFileVersion, - ) -> Result + fn try_from_mini_lance(bytes: Bytes, schema: &Schema) -> Result where Self: Sized, { - let projection = ReaderProjection::from_whole_schema(schema, file_version); let footer = FileReader::decode_footer(&bytes)?; + let file_version = FileReader::current_file_version(&footer)?; + let projection = versions::reader_projection_from_whole_schema(schema, file_version); // Next, read the metadata for the columns // This is both the column metadata and the CMO table @@ -2637,12 +2264,7 @@ impl EncodedBatchReaderExt for EncodedBatch { let column_metadatas = FileReader::read_all_column_metadata(column_metadata_bytes, &footer)?; - let file_version = LanceFileVersion::try_from_major_minor( - footer.major_version as u32, - footer.minor_version as u32, - )?; - - let page_table = FileReader::meta_to_col_infos(&column_metadatas, file_version)?; + let page_table = versions::decode_column_metadata(file_version, &column_metadatas)?; Ok(Self { data: bytes, @@ -2661,16 +2283,13 @@ impl EncodedBatchReaderExt for EncodedBatch { Self: Sized, { let footer = FileReader::decode_footer(&bytes)?; - let file_version = LanceFileVersion::try_from_major_minor( - footer.major_version as u32, - footer.minor_version as u32, - )?; + let file_version = FileReader::current_file_version(&footer)?; let gbo_table = FileReader::do_decode_gbo_table( &bytes.slice(footer.global_buff_offsets_start as usize..), &footer, - file_version, )?; + versions::validate_global_buffers(file_version, &gbo_table)?; if gbo_table.is_empty() { return Err(Error::internal( "File did not contain any global buffers, schema expected".to_string(), @@ -2681,7 +2300,7 @@ impl EncodedBatchReaderExt for EncodedBatch { let schema_bytes = bytes.slice(schema_start..(schema_start + schema_size)); let (_, schema) = FileReader::decode_schema(schema_bytes)?; - let projection = ReaderProjection::from_whole_schema(&schema, file_version); + let projection = versions::reader_projection_from_whole_schema(&schema, file_version); // Next, read the metadata for the columns // This is both the column metadata and the CMO table @@ -2691,7 +2310,7 @@ impl EncodedBatchReaderExt for EncodedBatch { let column_metadatas = FileReader::read_all_column_metadata(column_metadata_bytes, &footer)?; - let page_table = FileReader::meta_to_col_infos(&column_metadatas, file_version)?; + let page_table = versions::decode_column_metadata(file_version, &column_metadatas)?; Ok(Self { data: bytes, @@ -2728,12 +2347,11 @@ mod tests { use lance_encoding::{ constants::{STRUCTURAL_ENCODING_META_KEY, STRUCTURAL_ENCODING_SPARSE}, decoder::{ - DecodeBatchScheduler, DecoderPlugins, FilterExpression, PageEncoding, ReadBatchTask, - decode_batch, + DecodeBatchScheduler, DecoderPlugins, EncodedBatchLayout, FilterExpression, + PageEncoding, ReadBatchTask, decode_batch, }, - encoder::{EncodedBatch, EncodingOptions, default_encoding_strategy, encode_batch}, + encoder::{EncodedBatch, EncodingOptions, encode_batch}, format::pb21, - version::LanceFileVersion, }; use lance_io::{stream::RecordBatchStream, utils::CachedFileSize}; use log::debug; @@ -2741,11 +2359,12 @@ mod tests { use tokio::sync::mpsc; use crate::reader::{ - EncodedBatchReaderExt, FileReader, FileReaderOptions, ProjectedFileReader, - ReaderProjection, validate_field_length, verify_uniform_lengths, + EncodedBatchReaderExt, FileReader, FileReaderOptions, ProjectedFileReader, ReaderProjection, }; use crate::testing::{FsFixture, WrittenFile, test_cache, write_lance_file}; - use crate::writer::{EncodedBatchWriteExt, FileWriter, FileWriterOptions}; + use crate::version::ConcreteFileVersion; + use crate::versions; + use crate::writer::FileWriterOptions; use lance_encoding::decoder::DecoderConfig; #[tokio::test] @@ -2793,10 +2412,8 @@ mod tests { write_lance_file( input, &fs, - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_3), - ..Default::default() - }, + ConcreteFileVersion::V2_3, + FileWriterOptions::default(), ) .await; @@ -2814,10 +2431,10 @@ mod tests { ) .await .unwrap(); - assert_eq!(file_reader.metadata.column_infos.len(), 2); + assert_eq!(file_reader.metadata().column_infos.len(), 2); assert!( file_reader - .metadata + .metadata() .column_infos .iter() .flat_map(|column| column.page_infos.iter()) @@ -2877,7 +2494,15 @@ mod tests { assert_eq!(take, vec![batch.take(&indices).unwrap()]); } - async fn create_some_file(fs: &FsFixture, version: LanceFileVersion) -> WrittenFile { + fn footer_version(bytes: &[u8]) -> (u16, u16) { + let version_start = bytes.len() - 8; + ( + u16::from_le_bytes([bytes[version_start], bytes[version_start + 1]]), + u16::from_le_bytes([bytes[version_start + 2], bytes[version_start + 3]]), + ) + } + + async fn create_some_file(fs: &FsFixture, version: ConcreteFileVersion) -> WrittenFile { let location_type = DataType::Struct(Fields::from(vec![ Field::new("x", DataType::Float64, true), Field::new("y", DataType::Float64, true), @@ -2889,20 +2514,12 @@ mod tests { .col("location", array::rand_type(&location_type)) .col("categories", array::rand_type(&categories_type)) .col("binary", array::rand_type(&DataType::Binary)); - if version <= LanceFileVersion::V2_0 { + if version == ConcreteFileVersion::V2_0 { reader = reader.col("large_bin", array::rand_type(&DataType::LargeBinary)); } let reader = reader.into_reader_rows(RowCount::from(1000), BatchCount::from(100)); - write_lance_file( - reader, - fs, - FileWriterOptions { - format_version: Some(version), - ..Default::default() - }, - ) - .await + write_lance_file(reader, fs, version, FileWriterOptions::default()).await } async fn create_wide_direct_file(fs: &FsFixture, num_columns: usize) -> WrittenFile { @@ -2915,10 +2532,8 @@ mod tests { write_lance_file( reader, fs, - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_1), - ..Default::default() - }, + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), ) .await } @@ -2938,10 +2553,8 @@ mod tests { write_lance_file( reader, fs, - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_1), - ..Default::default() - }, + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), ) .await } @@ -2969,10 +2582,8 @@ mod tests { write_lance_file( reader, fs, - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_1), - ..Default::default() - }, + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), ) .await } @@ -3045,7 +2656,24 @@ mod tests { async fn test_round_trip() { let fs = FsFixture::default(); - let WrittenFile { data, .. } = create_some_file(&fs, LanceFileVersion::V2_0).await; + let WrittenFile { data, .. } = create_some_file(&fs, ConcreteFileVersion::V2_0).await; + + let file_size = fs.object_store.size(&fs.tmp_path).await.unwrap() as usize; + let footer = fs + .object_store + .open(&fs.tmp_path) + .await + .unwrap() + .get_range(file_size - 8..file_size) + .await + .unwrap(); + assert_eq!(footer_version(&footer), (0, 3)); + assert_eq!( + crate::determine_file_version(&fs.object_store, &fs.tmp_path, Some(file_size)) + .await + .unwrap(), + ConcreteFileVersion::V2_0 + ); for read_size in [32, 1024, 1024 * 1024] { let file_scheduler = fs @@ -3063,6 +2691,13 @@ mod tests { .await .unwrap(); + assert_eq!( + ( + file_reader.metadata().major_version, + file_reader.metadata().minor_version + ), + (0, 3) + ); let schema = file_reader.schema(); assert_eq!(schema.metadata.get("foo").unwrap(), "bar"); @@ -3084,7 +2719,7 @@ mod tests { #[test_log::test(tokio::test)] async fn test_encoded_batch_round_trip( // TODO: Add V2_1 (currently fails) - #[values(LanceFileVersion::V2_0)] version: LanceFileVersion, + #[values(ConcreteFileVersion::V2_0)] version: ConcreteFileVersion, ) { let data = gen_batch() .col("x", array::rand::()) @@ -3099,22 +2734,22 @@ mod tests { max_page_bytes: 32 * 1024 * 1024, keep_original_array: true, buffer_alignment: 64, - version, }; - let encoding_strategy = default_encoding_strategy(version); + let encoding_strategy = lance_encoding::array_encoding::ArrayFieldEncodingStrategy::new(); let encoded_batch = encode_batch( &data, lance_schema.clone(), - encoding_strategy.as_ref(), + &encoding_strategy, &encoding_options, ) .await .unwrap(); // Test self described - let bytes = encoded_batch.try_to_self_described_lance(version).unwrap(); + let bytes = versions::encode_self_described_batch(version, &encoded_batch).unwrap(); + assert_eq!(footer_version(&bytes), (2, 0)); let decoded_batch = EncodedBatch::try_from_self_described_lance(bytes).unwrap(); @@ -3123,7 +2758,7 @@ mod tests { &FilterExpression::no_filter(), Arc::::default(), false, - version, + EncodedBatchLayout::Array, None, ) .await @@ -3132,16 +2767,16 @@ mod tests { assert_eq!(data, decoded); // Test mini - let bytes = encoded_batch.try_to_mini_lance(version).unwrap(); + let bytes = versions::encode_mini_batch(version, &encoded_batch).unwrap(); + assert_eq!(footer_version(&bytes), (2, 0)); let decoded_batch = - EncodedBatch::try_from_mini_lance(bytes, lance_schema.as_ref(), LanceFileVersion::V2_0) - .unwrap(); + EncodedBatch::try_from_mini_lance(bytes, lance_schema.as_ref()).unwrap(); let decoded = decode_batch( &decoded_batch, &FilterExpression::no_filter(), Arc::::default(), false, - version, + EncodedBatchLayout::Array, None, ) .await @@ -3153,8 +2788,12 @@ mod tests { #[rstest] #[test_log::test(tokio::test)] async fn test_projection( - #[values(LanceFileVersion::V2_0, LanceFileVersion::V2_1, LanceFileVersion::V2_2)] - version: LanceFileVersion, + #[values( + ConcreteFileVersion::V2_0, + ConcreteFileVersion::V2_1, + ConcreteFileVersion::V2_2 + )] + version: ConcreteFileVersion, ) { let fs = FsFixture::default(); @@ -3201,15 +2840,15 @@ mod tests { let projected_schema = written_file.schema.project(&columns).unwrap(); let projection = if use_field_ids { - ReaderProjection::from_field_ids( - file_reader.metadata.version(), + versions::reader_projection_from_field_ids( + file_reader.metadata().version(), &projected_schema, &field_id_mapping, ) .unwrap() } else { - ReaderProjection::from_column_names( - file_reader.metadata.version(), + versions::reader_projection_from_column_names( + file_reader.metadata().version(), &written_file.schema, &columns, ) @@ -3331,8 +2970,8 @@ mod tests { .open_file(&fs.tmp_path, &CachedFileSize::unknown()) .await .unwrap(); - let projection = ReaderProjection::from_column_names( - LanceFileVersion::V2_1, + let projection = versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, &written_file.schema, &["c10"], ) @@ -3390,8 +3029,8 @@ mod tests { let fs = FsFixture::default(); let written_file = create_wide_direct_file(&fs, 512).await; - let projection = ReaderProjection::from_column_names( - LanceFileVersion::V2_1, + let projection = versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, &written_file.schema, &["c0"], ) @@ -3568,20 +3207,13 @@ mod tests { async fn test_lazy_reader_fixed_size_list_projection_matches_eager_reader() { let fs = FsFixture::default(); let written_file = create_wide_fixed_size_list_file(&fs, 512).await; - let projection = ReaderProjection::from_column_names( - LanceFileVersion::V2_1, + let projection = versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, &written_file.schema, &["c17", "c509"], ) .unwrap(); - assert!(ProjectedFileReader::supports_projection( - &projection, - LanceFileVersion::V2_1 - )); - assert!(!ProjectedFileReader::supports_projection( - &projection, - LanceFileVersion::V2_0 - )); + assert!(projection.prefers_indexed_metadata(512)); assert_lazy_projection_matches_eager_and_reads_metadata_subset( &fs, projection, @@ -3590,12 +3222,44 @@ mod tests { .await; } + #[tokio::test] + async fn test_v2_0_rejects_indexed_metadata_reader() { + let fs = FsFixture::default(); + let written_file = create_some_file(&fs, ConcreteFileVersion::V2_0).await; + let projection = versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_0, + &written_file.schema, + &["score"], + ) + .unwrap(); + assert!(projection.prefers_indexed_metadata(100)); + + let file_scheduler = fs + .scheduler + .open_file(&fs.tmp_path, &CachedFileSize::unknown()) + .await + .unwrap(); + let err = ProjectedFileReader::try_open( + file_scheduler, + Some(projection), + Arc::::default(), + &test_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap_err(); + assert!( + matches!(err, lance_core::Error::NotSupported { .. }), + "expected V2.0 indexed metadata open to fail, got {err:?}" + ); + } + #[tokio::test] async fn test_lazy_reader_nested_projection_compacts_physical_columns() { let fs = FsFixture::default(); let written_file = create_wide_structural_file(&fs, 128).await; - let projection = ReaderProjection::from_column_names( - LanceFileVersion::V2_1, + let projection = versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, &written_file.schema, &["s97.y", "l4", "s3"], ) @@ -3621,10 +3285,7 @@ mod tests { .any(|indices| indices[0] > indices[1]), "the projection must reorder physical columns to exercise compact remapping" ); - assert!(ProjectedFileReader::supports_projection( - &projection, - LanceFileVersion::V2_1 - )); + assert!(projection.prefers_indexed_metadata(128 * 4)); let actual = assert_lazy_projection_matches_eager_and_reads_metadata_subset( &fs, projection, "nested", ) @@ -3672,19 +3333,16 @@ mod tests { #[tokio::test] async fn test_lazy_reader_rejects_opaque_projection(#[case] metadata_key: &str) { let fs = FsFixture::default(); - let written_file = create_some_file(&fs, LanceFileVersion::V2_1).await; + let written_file = create_some_file(&fs, ConcreteFileVersion::V2_1).await; - let ordinary_projection = ReaderProjection::from_column_names( - LanceFileVersion::V2_1, + let ordinary_projection = versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, &written_file.schema, &["location.x"], ) .unwrap(); assert_eq!(ordinary_projection.schema.fields[0].children.len(), 1); - assert!(ProjectedFileReader::supports_projection( - &ordinary_projection, - LanceFileVersion::V2_1 - )); + assert!(ordinary_projection.prefers_indexed_metadata(100)); let file_scheduler = fs .scheduler @@ -3709,10 +3367,7 @@ mod tests { Arc::make_mut(&mut projection.schema).fields[0] .metadata .insert(metadata_key.to_string(), "true".to_string()); - assert!(!ProjectedFileReader::supports_projection( - &projection, - LanceFileVersion::V2_1 - )); + assert!(!projection.prefers_indexed_metadata(100)); let err = ProjectedFileReader::try_open( file_scheduler, @@ -3729,7 +3384,7 @@ mod tests { ); } - // The projection-length validation lives in `FileReadCore`, shared by the + // The projection-length validation lives in `DecodeEngine`, shared by the // eager and the lazy (indexed) metadata providers. The indexed provider loads // only the projected columns and renumbers them 0..N, so this checks that the // renumbered `column_infos`/`column_indices` still line up for the length @@ -3747,14 +3402,10 @@ mod tests { let lance_schema = Schema::try_from(arrow_schema.as_ref()).unwrap(); let fs = FsFixture::default(); - let options = FileWriterOptions { - format_version: Some(LanceFileVersion::V2_1), - ..Default::default() - }; - let mut writer = FileWriter::try_new( + let mut writer = versions::v2_1::create_writer( fs.object_store.create(&fs.tmp_path).await.unwrap(), lance_schema.clone(), - options, + FileWriterOptions::default(), ) .unwrap(); // "a" has 5 rows, "c" has 1 -- an unequal-length file. @@ -3775,9 +3426,12 @@ mod tests { .unwrap(); let cache = test_cache(); let open_indexed = |names: &[&str]| { - let projection = - ReaderProjection::from_column_names(LanceFileVersion::V2_1, &lance_schema, names) - .unwrap(); + let projection = versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, + &lance_schema, + names, + ) + .unwrap(); ProjectedFileReader::try_open( file_scheduler.clone(), Some(projection), @@ -3840,7 +3494,7 @@ mod tests { async fn test_compressing_buffer() { let fs = FsFixture::default(); - let written_file = create_some_file(&fs, LanceFileVersion::V2_0).await; + let written_file = create_some_file(&fs, ConcreteFileVersion::V2_0).await; let file_scheduler = fs .scheduler .open_file(&fs.tmp_path, &CachedFileSize::unknown()) @@ -3895,7 +3549,7 @@ mod tests { #[tokio::test] async fn test_read_all() { let fs = FsFixture::default(); - let WrittenFile { data, .. } = create_some_file(&fs, LanceFileVersion::V2_0).await; + let WrittenFile { data, .. } = create_some_file(&fs, ConcreteFileVersion::V2_0).await; let total_rows = data.iter().map(|batch| batch.num_rows()).sum::(); let file_scheduler = fs @@ -3932,8 +3586,12 @@ mod tests { #[rstest] #[tokio::test] async fn test_blocking_take( - #[values(LanceFileVersion::V2_0, LanceFileVersion::V2_1, LanceFileVersion::V2_2)] - version: LanceFileVersion, + #[values( + ConcreteFileVersion::V2_0, + ConcreteFileVersion::V2_1, + ConcreteFileVersion::V2_2 + )] + version: ConcreteFileVersion, ) { let fs = FsFixture::default(); let WrittenFile { data, schema, .. } = create_some_file(&fs, version).await; @@ -3946,7 +3604,10 @@ mod tests { .unwrap(); let file_reader = FileReader::try_open( file_scheduler.clone(), - Some(ReaderProjection::from_column_names(version, &schema, &["score"]).unwrap()), + Some( + versions::reader_projection_from_column_names(version, &schema, &["score"]) + .unwrap(), + ), Arc::::default(), &test_cache(), FileReaderOptions::default(), @@ -3977,7 +3638,7 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn test_drop_in_progress() { let fs = FsFixture::default(); - let WrittenFile { data, .. } = create_some_file(&fs, LanceFileVersion::V2_0).await; + let WrittenFile { data, .. } = create_some_file(&fs, ConcreteFileVersion::V2_0).await; let total_rows = data.iter().map(|batch| batch.num_rows()).sum::(); let file_scheduler = fs @@ -4026,7 +3687,7 @@ mod tests { // if the stream was dropped before it finished. let fs = FsFixture::default(); - let written_file = create_some_file(&fs, LanceFileVersion::V2_0).await; + let written_file = create_some_file(&fs, ConcreteFileVersion::V2_0).await; let total_rows = written_file .data .iter() @@ -4048,11 +3709,11 @@ mod tests { .await .unwrap(); - let projection = - ReaderProjection::from_whole_schema(&written_file.schema, LanceFileVersion::V2_0); - let column_infos = file_reader - .collect_columns_from_projection(&projection) - .unwrap(); + let projection = versions::reader_projection_from_whole_schema( + &written_file.schema, + ConcreteFileVersion::V2_0, + ); + let column_infos = file_reader.metadata().column_infos.clone(); let mut decode_scheduler = DecodeBatchScheduler::try_new( &projection.schema, &projection.column_indices, @@ -4060,7 +3721,7 @@ mod tests { &vec![], total_rows as u64, Arc::::default(), - file_reader.core.scheduler.clone(), + file_reader.scheduler(), test_cache(), &FilterExpression::no_filter(), &DecoderConfig::default(), @@ -4080,14 +3741,14 @@ mod tests { range, &FilterExpression::no_filter(), tx, - file_reader.core.scheduler.clone(), + file_reader.scheduler(), ) } #[tokio::test] async fn test_read_empty_range() { let fs = FsFixture::default(); - create_some_file(&fs, LanceFileVersion::V2_0).await; + create_some_file(&fs, ConcreteFileVersion::V2_0).await; let file_scheduler = fs .scheduler @@ -4145,7 +3806,7 @@ mod tests { )])) .unwrap(); - let mut file_writer = FileWriter::try_new( + let mut file_writer = versions::v2_1::create_writer( fs.object_store.create(&fs.tmp_path).await.unwrap(), lance_schema, FileWriterOptions::default(), @@ -4214,7 +3875,7 @@ mod tests { #[tokio::test] async fn test_read_global_buffer_no_user_buffers() { let fs = FsFixture::default(); - create_some_file(&fs, LanceFileVersion::V2_1).await; + create_some_file(&fs, ConcreteFileVersion::V2_1).await; let file_scheduler = fs .scheduler @@ -4243,12 +3904,12 @@ mod tests { #[tokio::test] async fn test_deep_size_of_includes_column_metadata( #[values( - LanceFileVersion::V2_0, - LanceFileVersion::V2_1, - LanceFileVersion::V2_2, - LanceFileVersion::V2_3 + ConcreteFileVersion::V2_0, + ConcreteFileVersion::V2_1, + ConcreteFileVersion::V2_2, + ConcreteFileVersion::V2_3 )] - version: LanceFileVersion, + version: ConcreteFileVersion, ) { // Regression test: CachedFileMetadata::deep_size_of must account for // column_metadatas and column_infos, otherwise the moka cache weigher @@ -4344,21 +4005,11 @@ mod tests { let run = |dt: DataType, indices: &[u32], lengths: Vec| -> lance_core::Result { let arrow = ArrowSchema::new(vec![Field::new("s", dt, true)]); let schema = Schema::try_from(&arrow).unwrap(); - let column_len = |c: usize| Ok(lengths[c]); - let mut cursor = 0usize; - let mut field_lengths = Vec::new(); - for field in &schema.fields { - let rows = validate_field_length( - field, - is_structural, - true, - indices, - &mut cursor, - &column_len, - )?; - field_lengths.push((field.name.as_str(), rows)); + if is_structural { + versions::v2_1::test_projection_length(&schema, indices, &lengths) + } else { + versions::v2_0::test_projection_length(&schema, indices, &lengths) } - verify_uniform_lengths(&field_lengths) }; let struct_ty = || { @@ -4398,24 +4049,14 @@ mod tests { schema: Arc::new(schema), column_indices: vec![0], }; - let column_len = |column: usize| { - assert_eq!(column, 0); - Ok(3) - }; - let mut cursor = 0usize; - - let rows = validate_field_length( - &projection.schema.fields[0], - false, - true, + let rows = versions::v2_0::test_projection_length( + &projection.schema, &projection.column_indices, - &mut cursor, - &column_len, + &[3], ) .unwrap(); assert_eq!(rows, 3); - assert_eq!(cursor, 1); } #[test] @@ -4427,16 +4068,11 @@ mod tests { -> lance_core::Result { let arrow = ArrowSchema::new(vec![Field::new("f", dt, true)]); let schema = Schema::try_from(&arrow).unwrap(); - let column_len = |c: usize| Ok(lengths[c]); - let mut cursor = 0usize; - validate_field_length( - &schema.fields[0], - is_structural, - true, - indices, - &mut cursor, - &column_len, - ) + if is_structural { + versions::v2_1::test_projection_length(&schema, indices, &lengths) + } else { + versions::v2_0::test_projection_length(&schema, indices, &lengths) + } }; // A list's items have a different cardinality than its rows; that gap diff --git a/rust/lance-file/src/reader/structural.rs b/rust/lance-file/src/reader/structural.rs new file mode 100644 index 00000000000..18bda6eb933 --- /dev/null +++ b/rust/lance-file/src/reader/structural.rs @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::{collections::BTreeMap, sync::Arc}; + +use async_trait::async_trait; +use bytes::Bytes; +use lance_core::{ + Error, Result, + cache::LanceCache, + datatypes::{Field, Schema}, +}; +use lance_encoding::{ + EncodingsIo, + decoder::{ColumnInfo, PageEncoding, PageInfo}, + format::{pb, pb21}, +}; +use prost::{Message, Name}; + +use crate::{ + format::pbfile, + reader::{ + BufferDescriptor, FileMetadataIndex, FileMetadataProvider, FileReader, PreparedProjection, + ReadProjection, ReaderProjection, normalized_column_num_rows, verify_uniform_lengths, + }, + writer::PAGE_BUFFER_ALIGNMENT, +}; + +fn fetch_encoding(encoding: &pbfile::Encoding) -> Result { + match &encoding.location { + Some(pbfile::encoding::Location::Indirect(_)) => Err(Error::invalid_input_source( + "Indirect file encodings are not supported".into(), + )), + Some(pbfile::encoding::Location::Direct(encoding)) => { + let envelope = prost_types::Any::decode(Bytes::from(encoding.encoding.clone())) + .map_err(|error| { + Error::invalid_input_source( + format!("Invalid direct {} encoding envelope: {error}", M::NAME).into(), + ) + })?; + envelope.to_msg::().map_err(|error| { + Error::invalid_input_source( + format!("Invalid direct {} encoding: {error}", M::NAME).into(), + ) + }) + } + Some(pbfile::encoding::Location::None(_)) => Err(Error::invalid_input_source( + format!("Missing {} encoding description", M::NAME).into(), + )), + None => Err(Error::invalid_input_source( + format!("Missing {} encoding location", M::NAME).into(), + )), + } +} + +/// Decode structural page syntax before an exact reader validates its grammar. +pub fn decode_page_layout( + column_index: u32, + page_index: usize, + page: &pbfile::column_metadata::Page, +) -> Result { + fetch_encoding(page.encoding.as_ref().ok_or_else(|| { + Error::invalid_input_source( + format!( + "Column {} page {} is missing its encoding", + column_index, page_index + ) + .into(), + ) + })?) +} + +/// Build normalized page metadata after exact grammar validation. +pub fn build_page_info( + column_index: u32, + page_index: usize, + page: &pbfile::column_metadata::Page, + page_layout: pb21::PageLayout, +) -> Result { + if page.buffer_offsets.len() != page.buffer_sizes.len() { + return Err(Error::invalid_input_source( + format!( + "Column {} page {} has {} buffer offsets but {} buffer sizes", + column_index, + page_index, + page.buffer_offsets.len(), + page.buffer_sizes.len() + ) + .into(), + )); + } + let buffer_offsets_and_sizes = Arc::from( + page.buffer_offsets + .iter() + .zip(&page.buffer_sizes) + .map(|(offset, size)| { + if offset % PAGE_BUFFER_ALIGNMENT as u64 != 0 { + return Err(Error::invalid_input_source( + format!( + "Column {} page {} buffer offset {} is not aligned to {} bytes", + column_index, page_index, offset, PAGE_BUFFER_ALIGNMENT + ) + .into(), + )); + } + Ok((*offset, *size)) + }) + .collect::>>()?, + ); + Ok(PageInfo { + buffer_offsets_and_sizes, + encoding: PageEncoding::Structural(page_layout), + num_rows: page.length, + priority: page.priority, + }) +} + +/// Finish normalized column metadata after all pages have been validated. +pub fn build_column_info( + column_index: u32, + metadata: &pbfile::ColumnMetadata, + page_infos: Vec, +) -> Result> { + if metadata.buffer_offsets.len() != metadata.buffer_sizes.len() { + return Err(Error::invalid_input_source( + format!( + "Column {} has {} buffer offsets but {} buffer sizes", + column_index, + metadata.buffer_offsets.len(), + metadata.buffer_sizes.len() + ) + .into(), + )); + } + let buffer_offsets_and_sizes = Arc::from( + metadata + .buffer_offsets + .iter() + .zip(&metadata.buffer_sizes) + .map(|(offset, size)| (*offset, *size)) + .collect::>(), + ); + let encoding: pb::ColumnEncoding = + fetch_encoding(metadata.encoding.as_ref().ok_or_else(|| { + Error::invalid_input_source( + format!("Column {} is missing its encoding", column_index).into(), + ) + })?)?; + Ok(Arc::new(ColumnInfo { + index: column_index, + page_infos: Arc::from(page_infos), + buffer_offsets_and_sizes, + encoding, + })) +} + +/// Validate the structural global-buffer alignment contract. +pub fn validate_global_buffers(buffers: &[BufferDescriptor]) -> Result<()> { + for (buffer_index, buffer) in buffers.iter().enumerate() { + if buffer.position % PAGE_BUFFER_ALIGNMENT as u64 != 0 { + return Err(Error::invalid_input_source( + format!( + "Global buffer {} position {} is not aligned to {} bytes", + buffer_index, buffer.position, PAGE_BUFFER_ALIGNMENT + ) + .into(), + )); + } + } + Ok(()) +} + +fn field_column_shape(field: &Field) -> (bool, bool) { + if field.is_blob() || field.is_packed_struct() { + return (true, false); + } + (field.children.is_empty(), !field.children.is_empty()) +} + +/// Count physical columns in the leaf-column layout used by v2.1+. +pub fn physical_column_count(field: &Field) -> usize { + if field.children.is_empty() || field.is_blob() || field.is_packed_struct() { + 1 + } else { + field.children.iter().map(physical_column_count).sum() + } +} + +fn append_physical_fields( + fields: &[Field], + field_ids: &mut Vec, + column_indices: &mut Vec, + next_column: &mut i32, +) { + for field in fields { + if field.children.is_empty() || field.is_blob() || field.is_packed_struct() { + field_ids.push(field.id); + column_indices.push(*next_column); + *next_column += 1; + } else { + append_physical_fields(&field.children, field_ids, column_indices, next_column); + } + } +} + +/// Build persisted field-to-column entries for the v2.1+ leaf layout. +pub fn data_file_columns(schema: &Schema) -> (Vec, Vec) { + let mut field_ids = Vec::new(); + let mut column_indices = Vec::new(); + append_physical_fields(&schema.fields, &mut field_ids, &mut column_indices, &mut 0); + (field_ids, column_indices) +} + +/// Build the field-id lookup for the v2.1+ leaf layout. +pub fn field_id_to_column_index(schema: &Schema) -> BTreeMap { + let (field_ids, column_indices) = data_file_columns(schema); + field_ids + .into_iter() + .zip(column_indices) + .filter_map(|(field_id, column_index)| { + (column_index >= 0).then_some((field_id as u32, column_index as u32)) + }) + .collect() +} + +fn append_field_ids( + fields: &[Field], + field_id_to_column_index: &BTreeMap, + column_indices: &mut Vec, +) { + for field in fields { + let (contributes, recurse) = field_column_shape(field); + if contributes + && let Some(column_index) = field_id_to_column_index.get(&(field.id as u32)).copied() + { + column_indices.push(column_index); + } + if recurse { + append_field_ids(&field.children, field_id_to_column_index, column_indices); + } + } +} + +/// Build the leaf-column projection selected by a v2.1+ exact reader. +pub fn projection_from_field_ids( + schema: &Schema, + field_id_to_column_index: &BTreeMap, +) -> ReaderProjection { + let mut column_indices = Vec::new(); + append_field_ids( + &schema.fields, + field_id_to_column_index, + &mut column_indices, + ); + ReaderProjection { + schema: Arc::new(schema.clone()), + column_indices, + } +} + +/// Project names using a caller-selected leaf-column mapping. +pub fn projection_from_column_names( + schema: &Schema, + column_names: &[&str], + field_id_to_column_index: &BTreeMap, +) -> Result { + let projected = schema.project(column_names)?; + Ok(projection_from_field_ids( + &projected, + field_id_to_column_index, + )) +} + +fn children_share_parent_length(field: &Field) -> bool { + field.logical_type.is_struct() +} + +fn validate_field_length Result>( + field: &Field, + comparable: bool, + column_indices: &[u32], + cursor: &mut usize, + column_len: &F, +) -> Result { + let (contributes, recurse) = field_column_shape(field); + let mut field_rows = None; + if contributes { + let column = *column_indices.get(*cursor).ok_or_else(|| { + Error::invalid_input(format!( + "projection supplied fewer column indices than its fields require (ran out at field '{}')", + field.name + )) + })?; + *cursor += 1; + field_rows = Some(column_len(column as usize)?); + } + if recurse { + let enforce_children = comparable && children_share_parent_length(field); + for child in &field.children { + let child_rows = + validate_field_length(child, enforce_children, column_indices, cursor, column_len)?; + let expected = *field_rows.get_or_insert(child_rows); + if enforce_children && child_rows != expected { + return Err(Error::invalid_input(format!( + "cannot read field '{}': its children have differing lengths (child '{}' has {} rows, but the field has {}); a struct's children must all have the same length", + field.name, child.name, child_rows, expected + ))); + } + } + } + field_rows.ok_or_else(|| { + Error::invalid_input(format!( + "projected field '{}' maps to no columns", + field.name + )) + }) +} + +/// Determine the normalized logical read length for a structural projection. +pub fn prepared_read_length(prepared: &PreparedProjection) -> Result { + let column_len = |column: usize| { + let info = prepared.column_infos.get(column).ok_or_else(|| { + Error::invalid_input(format!( + "projection references column index {} but only {} columns are available", + column, + prepared.column_infos.len() + )) + })?; + normalized_column_num_rows(info) + }; + projection_length( + &prepared.decoder_projection.schema, + &prepared.decoder_projection.column_indices, + &column_len, + ) +} + +fn projection_length Result>( + schema: &Schema, + column_indices: &[u32], + column_len: &F, +) -> Result { + let mut cursor = 0; + let mut field_lengths = Vec::with_capacity(schema.fields.len()); + for field in &schema.fields { + let rows = validate_field_length(field, true, column_indices, &mut cursor, column_len)?; + field_lengths.push((field.name.as_str(), rows)); + } + if cursor != column_indices.len() { + return Err(Error::invalid_input(format!( + "projection supplied {} column indices but its fields require {}", + column_indices.len(), + cursor + ))); + } + verify_uniform_lengths(&field_lengths) +} + +pub type DecodeColumn = fn(u32, &pbfile::ColumnMetadata) -> Result>; + +#[derive(Debug)] +struct StructuralReadProjection { + decode_column: DecodeColumn, +} + +/// Compose structural projection execution with an exact column grammar. +pub fn read_projection(decode_column: DecodeColumn) -> Arc { + Arc::new(StructuralReadProjection { decode_column }) +} + +#[async_trait] +impl ReadProjection for StructuralReadProjection { + fn validate_indexed( + &self, + projection: &ReaderProjection, + metadata_index: &FileMetadataIndex, + ) -> Result<()> { + FileMetadataProvider::validate_indexed_projection_structure(projection, metadata_index)?; + if FileMetadataProvider::projection_matches_indexed_metadata(projection) { + Ok(()) + } else { + Err(FileMetadataProvider::indexed_projection_error( + projection, + metadata_index, + )) + } + } + + fn read_length(&self, prepared: &PreparedProjection) -> Result { + prepared_read_length(prepared) + } + + async fn prepare( + &self, + metadata_provider: &FileMetadataProvider, + projection: &ReaderProjection, + io: &Arc, + cache: &Arc, + ) -> Result<(PreparedProjection, u64)> { + let prepared = match metadata_provider { + FileMetadataProvider::Full(metadata) => { + FileReader::validate_projection(projection, metadata)?; + PreparedProjection { + column_infos: metadata.column_infos.clone(), + decoder_projection: projection.clone(), + } + } + FileMetadataProvider::Indexed(metadata_index) => { + self.validate_indexed(projection, metadata_index)?; + let column_infos = FileMetadataProvider::load_indexed_column_infos( + metadata_index, + io, + cache, + &projection.column_indices, + self.decode_column, + ) + .await?; + PreparedProjection { + column_infos, + decoder_projection: ReaderProjection { + schema: projection.schema.clone(), + column_indices: (0..projection.column_indices.len()) + .map(|index| index as u32) + .collect(), + }, + } + } + }; + let read_len = self.read_length(&prepared)?; + Ok((prepared, read_len)) + } +} + +#[cfg(test)] +pub fn test_projection_length( + schema: &Schema, + column_indices: &[u32], + column_lengths: &[u64], +) -> Result { + projection_length(schema, column_indices, &|column| { + column_lengths.get(column).copied().ok_or_else(|| { + Error::invalid_input(format!("missing synthetic length for column {column}")) + }) + }) +} diff --git a/rust/lance-file/src/testing.rs b/rust/lance-file/src/testing.rs index 7f554e39d31..65b340e7641 100644 --- a/rust/lance-file/src/testing.rs +++ b/rust/lance-file/src/testing.rs @@ -16,7 +16,8 @@ use lance_io::{ }; use crate::reader::{FileReader, FileReaderOptions}; -use crate::writer::{FileWriter, FileWriterOptions}; +use crate::version::ConcreteFileVersion; +use crate::{versions, writer::FileWriterOptions}; pub struct FsFixture { pub tmp_path: TempObjFile, @@ -47,13 +48,15 @@ pub struct WrittenFile { pub async fn write_lance_file( data: impl RecordBatchReader, fs: &FsFixture, + version: ConcreteFileVersion, options: FileWriterOptions, ) -> WrittenFile { let writer = fs.object_store.create(&fs.tmp_path).await.unwrap(); let lance_schema = lance_core::datatypes::Schema::try_from(data.schema().as_ref()).unwrap(); - let mut file_writer = FileWriter::try_new(writer, lance_schema.clone(), options).unwrap(); + let mut file_writer = + versions::create_writer(version, writer, lance_schema.clone(), options).unwrap(); let data = data .collect::, ArrowError>>() diff --git a/rust/lance-file/src/version.rs b/rust/lance-file/src/version.rs new file mode 100644 index 00000000000..b453559117c --- /dev/null +++ b/rust/lance-file/src/version.rs @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; + +use lance_core::deepsize::{Context, DeepSizeOf}; +use lance_core::{Error, Result}; + +pub const LEGACY_FORMAT_VERSION: &str = "0.1"; +pub const V2_FORMAT_2_0: &str = "2.0"; +pub const V2_FORMAT_2_1: &str = "2.1"; +pub const V2_FORMAT_2_2: &str = "2.2"; +pub const V2_FORMAT_2_3: &str = "2.3"; + +/// Resolve the current stable release policy to an exact file version. +pub const fn stable_file_version() -> ConcreteFileVersion { + ConcreteFileVersion::V2_1 +} + +/// Resolve the current next release policy to an exact file version. +pub const fn next_file_version() -> ConcreteFileVersion { + ConcreteFileVersion::V2_3 +} + +/// A caller-facing Lance file-version request. +/// +/// `Stable` and `Next` are release selectors. They resolve to an exact +/// [`ConcreteFileVersion`] before file or dataset dispatch and are never persisted. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub enum LanceFileVersion { + /// The legacy v1 format. + Legacy, + /// Exact v2.0. + V2_0, + /// Exact v2.1 and the current default. + #[default] + V2_1, + /// The latest stable release. + Stable, + /// Exact v2.2. + V2_2, + /// The latest unstable release. + Next, + /// Exact v2.3. + V2_3, +} + +impl DeepSizeOf for LanceFileVersion { + fn deep_size_of_children(&self, _context: &mut Context) -> usize { + 0 + } +} + +impl LanceFileVersion { + /// Resolve this request through the current release policy. + pub const fn resolve(self) -> ConcreteFileVersion { + match self { + Self::Legacy => ConcreteFileVersion::V1, + Self::V2_0 => ConcreteFileVersion::V2_0, + Self::V2_1 => ConcreteFileVersion::V2_1, + Self::Stable => stable_file_version(), + Self::V2_2 => ConcreteFileVersion::V2_2, + Self::Next => next_file_version(), + Self::V2_3 => ConcreteFileVersion::V2_3, + } + } + + /// Whether this request resolves to an unstable exact format. + pub const fn is_unstable(self) -> bool { + self.resolve().is_unstable() + } +} + +impl Display for LanceFileVersion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Legacy => LEGACY_FORMAT_VERSION, + Self::V2_0 => V2_FORMAT_2_0, + Self::V2_1 => V2_FORMAT_2_1, + Self::V2_2 => V2_FORMAT_2_2, + Self::V2_3 => V2_FORMAT_2_3, + Self::Stable => "stable", + Self::Next => "next", + }) + } +} + +impl FromStr for LanceFileVersion { + type Err = Error; + + fn from_str(value: &str) -> Result { + match value.to_lowercase().as_str() { + LEGACY_FORMAT_VERSION | "legacy" => Ok(Self::Legacy), + V2_FORMAT_2_0 | "0.3" => Ok(Self::V2_0), + V2_FORMAT_2_1 => Ok(Self::V2_1), + V2_FORMAT_2_2 => Ok(Self::V2_2), + V2_FORMAT_2_3 => Ok(Self::V2_3), + "stable" => Ok(Self::Stable), + "next" => Ok(Self::Next), + _ => Err(unknown_version(value)), + } + } +} + +/// The exact persisted identity of a Lance file format. +/// +/// Unlike [`LanceFileVersion`], this type cannot represent release selectors such as +/// `stable` or `next`. Exact versions deliberately have no ordering because format +/// capabilities are not implied by release order. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ConcreteFileVersion { + /// The legacy v1 file format. + V1, + /// The v2.0 file format. + V2_0, + /// The v2.1 file format. + V2_1, + /// The v2.2 file format. + V2_2, + /// The v2.3 file format. + V2_3, +} + +impl DeepSizeOf for ConcreteFileVersion { + fn deep_size_of_children(&self, _context: &mut Context) -> usize { + 0 + } +} + +impl ConcreteFileVersion { + /// Convert this exact identity to the corresponding exact public selector. + /// + /// This never produces the release selectors `stable` or `next`. + pub const fn to_selector(self) -> LanceFileVersion { + match self { + Self::V1 => LanceFileVersion::Legacy, + Self::V2_0 => LanceFileVersion::V2_0, + Self::V2_1 => LanceFileVersion::V2_1, + Self::V2_2 => LanceFileVersion::V2_2, + Self::V2_3 => LanceFileVersion::V2_3, + } + } + + /// Whether this exact format is covered only by the unstable release policy. + pub const fn is_unstable(self) -> bool { + matches!(self, Self::V2_3) + } + + /// Decode the exact version string stored in a dataset manifest. + /// + /// Public selector aliases such as `legacy`, `0.3`, `stable`, and `next` are + /// intentionally rejected because manifests only store canonical exact versions. + pub fn from_manifest_string(value: &str) -> Result { + match value { + LEGACY_FORMAT_VERSION => Ok(Self::V1), + V2_FORMAT_2_0 => Ok(Self::V2_0), + V2_FORMAT_2_1 => Ok(Self::V2_1), + V2_FORMAT_2_2 => Ok(Self::V2_2), + V2_FORMAT_2_3 => Ok(Self::V2_3), + _ => Err(unknown_version(value)), + } + } + + /// Encode this exact version as the canonical string stored in a dataset manifest. + pub const fn to_manifest_string(self) -> &'static str { + match self { + Self::V1 => LEGACY_FORMAT_VERSION, + Self::V2_0 => V2_FORMAT_2_0, + Self::V2_1 => V2_FORMAT_2_1, + Self::V2_2 => V2_FORMAT_2_2, + Self::V2_3 => V2_FORMAT_2_3, + } + } + + /// Decode the major/minor version stored in `DataFile` metadata. + /// + /// Legacy manifests may omit these fields and decode to `(0, 0)`, so all legacy + /// v1 number pairs accepted by the historical decoder remain valid inputs. The + /// historical generic decoder also accepted the standard v2.0 footer pair `(0, 3)`; + /// decoding retains that compatibility while encoding always emits `(2, 0)`. + pub fn from_data_file_numbers(major: u32, minor: u32) -> Result { + match (major, minor) { + (0, 0..=2) => Ok(Self::V1), + (0, 3) | (2, 0) => Ok(Self::V2_0), + (2, 1) => Ok(Self::V2_1), + (2, 2) => Ok(Self::V2_2), + (2, 3) => Ok(Self::V2_3), + _ => Err(unknown_version(format_args!("{}.{}", major, minor))), + } + } + + /// Encode the canonical major/minor pair stored in `DataFile` metadata. + pub const fn to_data_file_numbers(self) -> (u32, u32) { + match self { + Self::V1 => (0, 2), + Self::V2_0 => (2, 0), + Self::V2_1 => (2, 1), + Self::V2_2 => (2, 2), + Self::V2_3 => (2, 3), + } + } + + /// Decode the major/minor version stored in a Lance file footer. + /// + /// V2.0 has two accepted representations: `(0, 3)` from the standard file writer + /// and `(2, 0)` from self-described and mini-lance writers. + pub fn from_footer_numbers(major: u16, minor: u16) -> Result { + match (major, minor) { + (0, 0..=2) => Ok(Self::V1), + (0, 3) | (2, 0) => Ok(Self::V2_0), + (2, 1) => Ok(Self::V2_1), + (2, 2) => Ok(Self::V2_2), + (2, 3) => Ok(Self::V2_3), + _ => Err(unknown_version(format_args!("{}.{}", major, minor))), + } + } + + /// Encode the footer numbers emitted by the standard Lance file writer. + pub const fn to_standard_footer_numbers(self) -> (u16, u16) { + match self { + Self::V1 => (0, 2), + Self::V2_0 => (0, 3), + Self::V2_1 => (2, 1), + Self::V2_2 => (2, 2), + Self::V2_3 => (2, 3), + } + } + + /// Encode the footer numbers emitted by self-described and mini-lance writers. + pub const fn to_embedded_footer_numbers(self) -> (u16, u16) { + match self { + Self::V1 => (0, 2), + Self::V2_0 => (2, 0), + Self::V2_1 => (2, 1), + Self::V2_2 => (2, 2), + Self::V2_3 => (2, 3), + } + } +} + +impl Display for ConcreteFileVersion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(self.to_manifest_string()) + } +} + +fn unknown_version(value: impl Display) -> Error { + Error::invalid_input_source(format!("Unknown Lance storage version: {}", value).into()) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use lance_io::object_store::ObjectStore; + use object_store::path::Path; + + use super::*; + + const EXACT_VERSIONS: [ConcreteFileVersion; 5] = [ + ConcreteFileVersion::V1, + ConcreteFileVersion::V2_0, + ConcreteFileVersion::V2_1, + ConcreteFileVersion::V2_2, + ConcreteFileVersion::V2_3, + ]; + + #[test] + fn selector_resolution_is_exact() { + let cases = [ + (LanceFileVersion::Legacy, ConcreteFileVersion::V1), + (LanceFileVersion::V2_0, ConcreteFileVersion::V2_0), + (LanceFileVersion::V2_1, ConcreteFileVersion::V2_1), + (LanceFileVersion::Stable, ConcreteFileVersion::V2_1), + (LanceFileVersion::V2_2, ConcreteFileVersion::V2_2), + (LanceFileVersion::Next, ConcreteFileVersion::V2_3), + (LanceFileVersion::V2_3, ConcreteFileVersion::V2_3), + ]; + + for (selector, expected) in cases { + assert_eq!(selector.resolve(), expected); + } + } + + #[test] + fn public_selector_aliases_remain_unchanged() { + let cases = [ + ("0.1", LanceFileVersion::Legacy), + ("legacy", LanceFileVersion::Legacy), + ("2.0", LanceFileVersion::V2_0), + ("0.3", LanceFileVersion::V2_0), + ("2.1", LanceFileVersion::V2_1), + ("stable", LanceFileVersion::Stable), + ("2.2", LanceFileVersion::V2_2), + ("next", LanceFileVersion::Next), + ("2.3", LanceFileVersion::V2_3), + ]; + + for (value, expected) in cases { + assert_eq!(LanceFileVersion::from_str(value).unwrap(), expected); + } + } + + #[test] + fn manifest_codec_only_accepts_canonical_exact_versions() { + for version in EXACT_VERSIONS { + let encoded = version.to_manifest_string(); + assert_eq!( + ConcreteFileVersion::from_manifest_string(encoded).unwrap(), + version + ); + } + + for selector_or_alias in ["legacy", "0.3", "stable", "next"] { + assert!(ConcreteFileVersion::from_manifest_string(selector_or_alias).is_err()); + } + } + + #[test] + fn data_file_codec_preserves_wire_numbers() { + let cases = [ + (ConcreteFileVersion::V1, (0, 2)), + (ConcreteFileVersion::V2_0, (2, 0)), + (ConcreteFileVersion::V2_1, (2, 1)), + (ConcreteFileVersion::V2_2, (2, 2)), + (ConcreteFileVersion::V2_3, (2, 3)), + ]; + + for (version, encoded) in cases { + assert_eq!(version.to_data_file_numbers(), encoded); + assert_eq!( + ConcreteFileVersion::from_data_file_numbers(encoded.0, encoded.1).unwrap(), + version + ); + } + for minor in 0..=2 { + assert_eq!( + ConcreteFileVersion::from_data_file_numbers(0, minor).unwrap(), + ConcreteFileVersion::V1 + ); + } + assert_eq!( + ConcreteFileVersion::from_data_file_numbers(0, 3).unwrap(), + ConcreteFileVersion::V2_0 + ); + } + + #[test] + fn footer_codec_preserves_both_v2_0_writer_representations() { + let standard_cases = [ + (ConcreteFileVersion::V1, (0, 2)), + (ConcreteFileVersion::V2_0, (0, 3)), + (ConcreteFileVersion::V2_1, (2, 1)), + (ConcreteFileVersion::V2_2, (2, 2)), + (ConcreteFileVersion::V2_3, (2, 3)), + ]; + let embedded_cases = [ + (ConcreteFileVersion::V1, (0, 2)), + (ConcreteFileVersion::V2_0, (2, 0)), + (ConcreteFileVersion::V2_1, (2, 1)), + (ConcreteFileVersion::V2_2, (2, 2)), + (ConcreteFileVersion::V2_3, (2, 3)), + ]; + + for (version, encoded) in standard_cases { + assert_eq!(version.to_standard_footer_numbers(), encoded); + assert_eq!( + ConcreteFileVersion::from_footer_numbers(encoded.0, encoded.1).unwrap(), + version + ); + } + for (version, encoded) in embedded_cases { + assert_eq!(version.to_embedded_footer_numbers(), encoded); + assert_eq!( + ConcreteFileVersion::from_footer_numbers(encoded.0, encoded.1).unwrap(), + version + ); + } + for minor in 0..=2 { + assert_eq!( + ConcreteFileVersion::from_footer_numbers(0, minor).unwrap(), + ConcreteFileVersion::V1 + ); + } + } + + #[tokio::test] + async fn file_version_detection_accepts_all_legacy_footer_aliases() { + let object_store = ObjectStore::memory(); + for minor in 0u16..=2 { + let path = Path::from(format!("legacy-{minor}.lance")); + let mut footer = Vec::with_capacity(8); + footer.extend_from_slice(&0u16.to_le_bytes()); + footer.extend_from_slice(&minor.to_le_bytes()); + footer.extend_from_slice(crate::format::MAGIC); + object_store.put(&path, &footer).await.unwrap(); + + assert_eq!( + crate::determine_file_version(&object_store, &path, Some(footer.len())) + .await + .unwrap(), + ConcreteFileVersion::V1 + ); + } + } +} diff --git a/rust/lance-file/src/versions/mod.rs b/rust/lance-file/src/versions/mod.rs new file mode 100644 index 00000000000..410608ee312 --- /dev/null +++ b/rust/lance-file/src/versions/mod.rs @@ -0,0 +1,362 @@ +//! Exact file-format composition roots. +//! +//! Each version module lists the mechanisms used by that file format. Callers +//! resolve release selectors before entering this module. Prefer APIs under a +//! concrete module such as [`v2_1`] when the version is statically known. The +//! root functions perform the single exhaustive dispatch for runtime versions. + +use bytes::Bytes; +use lance_core::{Error, Result, datatypes::Schema}; +use lance_encoding::{ + decoder::{ColumnInfo, DecoderPlugins, PageInfo}, + encoder::EncodedBatch, +}; +use lance_io::{scheduler::FileScheduler, traits::Writer}; +use std::{collections::BTreeMap, future::Future, sync::Arc}; + +use crate::{ + format::pbfile, + reader::{ + BufferDescriptor, CachedFileMetadata, FileMetadataIndex, FileMetadataProvider, FileReader, + FileReaderOptions, ProjectedFileReader, RawFileMetadata, ReadProjection, ReaderProjection, + }, + version::ConcreteFileVersion, + writer::{FileWriter, FileWriterOptions}, +}; +use lance_core::cache::LanceCache; + +pub mod v1; +pub mod v2_0; +pub mod v2_1; +pub mod v2_2; +pub mod v2_3; + +pub(crate) fn read_projection(version: ConcreteFileVersion) -> Result> { + match version { + ConcreteFileVersion::V1 => Err(Error::internal( + "current reader composition received Lance v1".to_string(), + )), + ConcreteFileVersion::V2_0 => Ok(v2_0::read_projection()), + ConcreteFileVersion::V2_1 => Ok(v2_1::read_projection()), + ConcreteFileVersion::V2_2 => Ok(v2_2::read_projection()), + ConcreteFileVersion::V2_3 => Ok(v2_3::read_projection()), + } +} + +/// A self-described file reader selected by the exact footer version. +pub enum OpenedFileReader { + /// A v1 file. The persisted footer numbers are retained for diagnostics. + V1 { + /// The major version stored in the footer. + major_version: u16, + /// The minor version stored in the footer. + minor_version: u16, + }, + /// A current-format reader selected from the exact footer identity. + Current(FileReader), +} + +pub(crate) fn finish_metadata( + version: ConcreteFileVersion, + metadata: RawFileMetadata, +) -> Result { + match version { + ConcreteFileVersion::V1 => Err(Error::internal( + "current metadata dispatch received a Lance v1 file".to_string(), + )), + ConcreteFileVersion::V2_0 => v2_0::finish_metadata(metadata), + ConcreteFileVersion::V2_1 => v2_1::finish_metadata(metadata), + ConcreteFileVersion::V2_2 => v2_2::finish_metadata(metadata), + ConcreteFileVersion::V2_3 => v2_3::finish_metadata(metadata), + } +} + +pub(crate) fn finish_metadata_index(index: FileMetadataIndex) -> Result { + match index.version { + ConcreteFileVersion::V1 => Err(Error::version_conflict( + "Attempt to use the Lance current-format reader with a v1 metadata index".to_string(), + 0, + 2, + )), + ConcreteFileVersion::V2_0 => v2_0::finish_metadata_index(index), + ConcreteFileVersion::V2_1 => v2_1::finish_metadata_index(index), + ConcreteFileVersion::V2_2 => v2_2::finish_metadata_index(index), + ConcreteFileVersion::V2_3 => v2_3::finish_metadata_index(index), + } +} + +pub(crate) fn decode_column_metadata( + version: ConcreteFileVersion, + column_metadatas: &[pbfile::ColumnMetadata], +) -> Result>> { + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported( + "self-described batches are not part of the Lance v1 grammar".to_string(), + )), + ConcreteFileVersion::V2_0 => v2_0::decode_column_metadata(column_metadatas), + ConcreteFileVersion::V2_1 => v2_1::decode_column_metadata(column_metadatas), + ConcreteFileVersion::V2_2 => v2_2::decode_column_metadata(column_metadatas), + ConcreteFileVersion::V2_3 => v2_3::decode_column_metadata(column_metadatas), + } +} + +pub(crate) fn validate_global_buffers( + version: ConcreteFileVersion, + buffers: &[BufferDescriptor], +) -> Result<()> { + match version { + ConcreteFileVersion::V1 => Ok(()), + ConcreteFileVersion::V2_0 => v2_0::validate_global_buffers(buffers), + ConcreteFileVersion::V2_1 => v2_1::validate_global_buffers(buffers), + ConcreteFileVersion::V2_2 => v2_2::validate_global_buffers(buffers), + ConcreteFileVersion::V2_3 => v2_3::validate_global_buffers(buffers), + } +} + +pub fn reader_projection_from_field_ids( + version: ConcreteFileVersion, + schema: &Schema, + field_id_to_column_index: &BTreeMap, +) -> Result { + Ok(match version { + ConcreteFileVersion::V1 => v1::projection_from_field_ids(schema, field_id_to_column_index), + ConcreteFileVersion::V2_0 => { + v2_0::projection_from_field_ids(schema, field_id_to_column_index) + } + ConcreteFileVersion::V2_1 => { + v2_1::projection_from_field_ids(schema, field_id_to_column_index) + } + ConcreteFileVersion::V2_2 => { + v2_2::projection_from_field_ids(schema, field_id_to_column_index) + } + ConcreteFileVersion::V2_3 => { + v2_3::projection_from_field_ids(schema, field_id_to_column_index) + } + }) +} + +pub fn reader_projection_from_whole_schema( + schema: &Schema, + version: ConcreteFileVersion, +) -> ReaderProjection { + match version { + ConcreteFileVersion::V1 => v1::projection_from_whole_schema(schema), + ConcreteFileVersion::V2_0 => v2_0::projection_from_whole_schema(schema), + ConcreteFileVersion::V2_1 => v2_1::projection_from_whole_schema(schema), + ConcreteFileVersion::V2_2 => v2_2::projection_from_whole_schema(schema), + ConcreteFileVersion::V2_3 => v2_3::projection_from_whole_schema(schema), + } +} + +pub fn reader_projection_from_column_names( + version: ConcreteFileVersion, + schema: &Schema, + column_names: &[&str], +) -> Result { + match version { + ConcreteFileVersion::V1 => v1::projection_from_column_names(schema, column_names), + ConcreteFileVersion::V2_0 => v2_0::projection_from_column_names(schema, column_names), + ConcreteFileVersion::V2_1 => v2_1::projection_from_column_names(schema, column_names), + ConcreteFileVersion::V2_2 => v2_2::projection_from_column_names(schema, column_names), + ConcreteFileVersion::V2_3 => v2_3::projection_from_column_names(schema, column_names), + } +} + +/// Count the physical columns represented by one field in an exact grammar. +pub fn physical_column_count( + version: ConcreteFileVersion, + field: &lance_core::datatypes::Field, +) -> usize { + match version { + ConcreteFileVersion::V1 => v1::physical_column_count(field), + ConcreteFileVersion::V2_0 => v2_0::physical_column_count(field), + ConcreteFileVersion::V2_1 => v2_1::physical_column_count(field), + ConcreteFileVersion::V2_2 => v2_2::physical_column_count(field), + ConcreteFileVersion::V2_3 => v2_3::physical_column_count(field), + } +} + +/// Build persisted field-to-column entries for an exact grammar. +pub fn data_file_columns(version: ConcreteFileVersion, schema: &Schema) -> (Vec, Vec) { + match version { + ConcreteFileVersion::V1 => v1::data_file_columns(schema), + ConcreteFileVersion::V2_0 => v2_0::data_file_columns(schema), + ConcreteFileVersion::V2_1 => v2_1::data_file_columns(schema), + ConcreteFileVersion::V2_2 => v2_2::data_file_columns(schema), + ConcreteFileVersion::V2_3 => v2_3::data_file_columns(schema), + } +} + +/// Copy one column's external metadata and buffers according to the exact file +/// grammar. +/// +/// The caller supplies the version-free I/O operation. V2.0 may suppress that +/// operation when a structural header page has already been copied. +pub async fn copy_external_metadata_column( + version: ConcreteFileVersion, + schema: &Schema, + column_index: usize, + has_existing_pages: bool, + copy: Copy, +) -> Result<()> +where + Copy: FnOnce() -> CopyFuture + Send, + CopyFuture: Future> + Send, +{ + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported( + "binary-copy metadata operations are not supported for Lance v1".to_string(), + )), + ConcreteFileVersion::V2_0 => { + if v2_0::should_copy_external_metadata_column(schema, column_index, has_existing_pages) + { + copy().await + } else { + Ok(()) + } + } + ConcreteFileVersion::V2_1 | ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => { + copy().await + } + } +} + +/// Normalize one copied column before an exact-version footer is written. +pub fn finalize_external_metadata_column( + version: ConcreteFileVersion, + schema: &Schema, + column_index: usize, + pages: &mut Vec, + num_rows: u64, +) -> Result<()> { + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported( + "binary-copy metadata operations are not supported for Lance v1".to_string(), + )), + ConcreteFileVersion::V2_0 => { + v2_0::finalize_external_metadata_column(schema, column_index, pages, num_rows); + Ok(()) + } + ConcreteFileVersion::V2_1 | ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => Ok(()), + } +} + +/// Open a projected reader while keeping exact metadata-form selection in the +/// file layer. +/// +/// `open_indexed` returns `None` when the loaded index is not selective enough +/// to justify a projected reader. V2.0 never invokes it because indexed +/// metadata is not part of that reader's accepted grammar. +pub async fn open_projected_reader( + version: ConcreteFileVersion, + projection: &ReaderProjection, + prefer_indexed: bool, + open_indexed: OpenIndexed, + open_full: OpenFull, +) -> Result +where + OpenIndexed: FnOnce() -> IndexedFuture + Send, + IndexedFuture: Future>> + Send, + OpenFull: FnOnce() -> FullFuture + Send, + FullFuture: Future> + Send, +{ + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported( + "projected current-format readers cannot open Lance v1 files".to_string(), + )), + ConcreteFileVersion::V2_0 => open_full().await, + ConcreteFileVersion::V2_1 | ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => { + if prefer_indexed + && FileMetadataProvider::projection_matches_indexed_metadata(projection) + && let Some(reader) = open_indexed().await? + { + return Ok(reader); + } + open_full().await + } + } +} + +/// Open a self-described file and dispatch to the matching reader. +/// +/// The current-format reader's optimistic tail read is also used for exact +/// version detection, so current files do not pay for a separate footer probe. +pub async fn open_self_described_reader( + scheduler: FileScheduler, + decoder_plugins: Arc, + cache: &LanceCache, + options: FileReaderOptions, +) -> Result { + FileReader::try_open_for_dispatch(scheduler, None, decoder_plugins, cache, options).await +} + +/// Create a current-format writer for an exact file version. +/// +/// V1 uses [`v1::writer::FileWriter`] directly because its manifest provider is +/// part of the writer type. +pub fn create_writer( + version: ConcreteFileVersion, + object_writer: Box, + schema: Schema, + options: FileWriterOptions, +) -> Result { + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported( + "Lance v1 files must be created with versions::v1::writer::FileWriter".to_string(), + )), + ConcreteFileVersion::V2_0 => { + v2_0::create_writer(object_writer, schema, options).map(Into::into) + } + ConcreteFileVersion::V2_1 => { + v2_1::create_writer(object_writer, schema, options).map(Into::into) + } + ConcreteFileVersion::V2_2 => { + v2_2::create_writer(object_writer, schema, options).map(Into::into) + } + ConcreteFileVersion::V2_3 => { + v2_3::create_writer(object_writer, schema, options).map(Into::into) + } + } +} + +/// Create a lazy current-format writer for an exact file version. +pub fn create_lazy_writer( + version: ConcreteFileVersion, + object_writer: Box, + options: FileWriterOptions, +) -> Result { + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported( + "legacy v1 files require an explicit schema and manifest provider".to_string(), + )), + ConcreteFileVersion::V2_0 => Ok(v2_0::create_lazy_writer(object_writer, options).into()), + ConcreteFileVersion::V2_1 => Ok(v2_1::create_lazy_writer(object_writer, options).into()), + ConcreteFileVersion::V2_2 => Ok(v2_2::create_lazy_writer(object_writer, options).into()), + ConcreteFileVersion::V2_3 => Ok(v2_3::create_lazy_writer(object_writer, options).into()), + } +} + +/// Encode a self-described batch for an exact file version. +pub fn encode_self_described_batch( + version: ConcreteFileVersion, + batch: &EncodedBatch, +) -> Result { + match version { + ConcreteFileVersion::V1 => v1::encode_self_described_batch(batch), + ConcreteFileVersion::V2_0 => v2_0::encode_self_described_batch(batch), + ConcreteFileVersion::V2_1 => v2_1::encode_self_described_batch(batch), + ConcreteFileVersion::V2_2 => v2_2::encode_self_described_batch(batch), + ConcreteFileVersion::V2_3 => v2_3::encode_self_described_batch(batch), + } +} + +/// Encode a mini-lance batch for an exact file version. +pub fn encode_mini_batch(version: ConcreteFileVersion, batch: &EncodedBatch) -> Result { + match version { + ConcreteFileVersion::V1 => v1::encode_mini_batch(batch), + ConcreteFileVersion::V2_0 => v2_0::encode_mini_batch(batch), + ConcreteFileVersion::V2_1 => v2_1::encode_mini_batch(batch), + ConcreteFileVersion::V2_2 => v2_2::encode_mini_batch(batch), + ConcreteFileVersion::V2_3 => v2_3::encode_mini_batch(batch), + } +} diff --git a/rust/lance-file/src/versions/v1/encoding.rs b/rust/lance-file/src/versions/v1/encoding.rs new file mode 100644 index 00000000000..674fd357fad --- /dev/null +++ b/rust/lance-file/src/versions/v1/encoding.rs @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +//! Lance v1 column encoding grammar. +//! +//! These codecs own the v1 wire layout, including schema dictionary payloads. +//! They are intentionally not exposed through the version-free I/O layer. + +use arrow_array::{ + Array, ArrayRef, + types::{BinaryType, LargeBinaryType, LargeUtf8Type, Utf8Type}, +}; +use arrow_schema::DataType; +use async_recursion::async_recursion; + +pub mod binary; +pub mod dictionary; +pub mod plain; + +use lance_arrow::DataTypeExt; +use lance_core::{ + Error, Result, + datatypes::{Field, Schema}, +}; +use lance_io::{ + ReadBatchParams, + traits::{Reader, Writer}, +}; + +use self::{ + binary::{BinaryDecoder, BinaryEncoder}, + plain::{PlainDecoder, PlainEncoder}, +}; + +/// Decode a binary-like array from a v1 values region. +pub async fn read_binary_array( + reader: &dyn Reader, + data_type: &DataType, + nullable: bool, + position: usize, + length: usize, + params: impl Into, +) -> Result { + use arrow_schema::DataType::*; + + let params = params.into(); + match data_type { + Utf8 => { + BinaryDecoder::::new(reader, position, length, nullable) + .get(params) + .await + } + Binary => { + BinaryDecoder::::new(reader, position, length, nullable) + .get(params) + .await + } + LargeUtf8 => { + BinaryDecoder::::new(reader, position, length, nullable) + .get(params) + .await + } + LargeBinary => { + BinaryDecoder::::new(reader, position, length, nullable) + .get(params) + .await + } + _ => Err(lance_core::Error::invalid_input(format!( + "unsupported v1 binary data type: {data_type}" + ))), + } +} + +/// Decode a fixed-stride array from a v1 values region. +pub async fn read_fixed_stride_array( + reader: &dyn Reader, + data_type: &DataType, + position: usize, + length: usize, + params: impl Into, +) -> Result { + if !lance_arrow::DataTypeExt::is_fixed_stride(data_type) { + return Err(lance_core::Error::schema(format!( + "{data_type} is not a fixed stride type" + ))); + } + PlainDecoder::new(reader, data_type, position, length)? + .get(params.into()) + .await +} + +/// Persist every schema dictionary using the v1 value codecs. +pub async fn write_schema_dictionaries(writer: &mut dyn Writer, schema: &mut Schema) -> Result<()> { + let max_field_id = schema.max_field_id().unwrap_or(-1); + for field_id in 0..=max_field_id { + let Some(field) = schema.mut_field_by_id(field_id) else { + continue; + }; + if !field.data_type().is_dictionary() { + continue; + } + + let dict_info = field.dictionary.as_mut().ok_or_else(|| { + Error::io(format!( + "v1 dictionary field '{}' is missing dictionary metadata", + field.name + )) + })?; + let values = dict_info.values.as_ref().ok_or_else(|| { + Error::invalid_input(format!( + "v1 dictionary field '{}' is missing dictionary values", + field.name + )) + })?; + + let data_type = values.data_type(); + let position = if data_type.is_numeric() { + PlainEncoder::new(writer, data_type) + .encode(&[values]) + .await? + } else if data_type.is_binary_like() { + BinaryEncoder::new(writer).encode(&[values]).await? + } else { + return Err(Error::schema(format!( + "v1 dictionary values do not support data type {data_type}" + ))); + }; + dict_info.offset = position; + dict_info.length = values.len(); + } + Ok(()) +} + +#[async_recursion] +async fn populate_field_dictionary(field: &mut Field, reader: &dyn Reader) -> Result<()> { + if let DataType::Dictionary(_, value_type) = field.data_type() { + let dict_info = field.dictionary.as_mut().ok_or_else(|| { + Error::io(format!( + "v1 dictionary field '{}' is missing dictionary metadata", + field.name + )) + })?; + let values = if value_type.is_binary_like() { + read_binary_array( + reader, + value_type.as_ref(), + true, + dict_info.offset, + dict_info.length, + .., + ) + .await? + } else if matches!( + value_type.as_ref(), + DataType::Int8 + | DataType::Int16 + | DataType::Int32 + | DataType::Int64 + | DataType::UInt8 + | DataType::UInt16 + | DataType::UInt32 + | DataType::UInt64 + ) { + read_fixed_stride_array( + reader, + value_type.as_ref(), + dict_info.offset, + dict_info.length, + .., + ) + .await? + } else { + return Err(Error::schema(format!( + "v1 dictionary values do not support data type {value_type}" + ))); + }; + dict_info.values = Some(values); + } else { + for child in &mut field.children { + populate_field_dictionary(child, reader).await?; + } + } + Ok(()) +} + +/// Load every persisted v1 schema dictionary into its in-memory field. +pub async fn populate_schema_dictionaries(schema: &mut Schema, reader: &dyn Reader) -> Result<()> { + for field in &mut schema.fields { + populate_field_dictionary(field, reader).await?; + } + Ok(()) +} diff --git a/rust/lance-io/src/encodings/binary.rs b/rust/lance-file/src/versions/v1/encoding/binary.rs similarity index 90% rename from rust/lance-io/src/encodings/binary.rs rename to rust/lance-file/src/versions/v1/encoding/binary.rs index ecd14f0d462..38aaa0de72d 100644 --- a/rust/lance-io/src/encodings/binary.rs +++ b/rust/lance-file/src/versions/v1/encoding/binary.rs @@ -5,7 +5,7 @@ //! use std::marker::PhantomData; -use std::ops::{Range, RangeFrom, RangeFull, RangeTo}; +use std::ops::Range; use std::sync::Arc; use arrow_arith::numeric::sub; @@ -23,16 +23,17 @@ use arrow_buffer::{ArrowNativeType, Buffer, MutableBuffer, ScalarBuffer, bit_uti use arrow_cast::cast::cast; use arrow_data::ArrayDataBuilder; use arrow_schema::DataType; -use async_trait::async_trait; use bytes::Bytes; use futures::{StreamExt, TryStreamExt}; use lance_arrow::BufferExt; use tokio::io::AsyncWriteExt; -use super::ReadBatchParams; -use super::{AsyncIndex, Decoder, Encoder, plain::PlainDecoder}; -use crate::traits::{Reader, Writer}; +use super::plain::PlainDecoder; use lance_core::Result; +use lance_io::{ + ReadBatchParams, + traits::{Reader, Writer}, +}; /// Encoder for Var-binary encoding. pub struct BinaryEncoder<'a> { @@ -87,9 +88,8 @@ impl<'a> BinaryEncoder<'a> { } } -#[async_trait] -impl Encoder for BinaryEncoder<'_> { - async fn encode(&mut self, arrs: &[&dyn Array]) -> Result { +impl BinaryEncoder<'_> { + pub async fn encode(&mut self, arrs: &[&dyn Array]) -> Result { assert!(!arrs.is_empty()); let data_type = arrs[0].data_type(); match data_type { @@ -97,12 +97,10 @@ impl Encoder for BinaryEncoder<'_> { DataType::Binary => self.encode_typed_arr::(arrs).await, DataType::LargeUtf8 => self.encode_typed_arr::(arrs).await, DataType::LargeBinary => self.encode_typed_arr::(arrs).await, - _ => { - return Err(lance_core::Error::invalid_input(format!( - "Unsupported data type for binary encoding: {}", - data_type - ))); - } + _ => Err(lance_core::Error::invalid_input(format!( + "Unsupported data type for binary encoding: {}", + data_type + ))), } } } @@ -134,7 +132,8 @@ impl<'a, T: ByteArrayType> BinaryDecoder<'a, T> { /// ```rust /// use arrow_array::types::Utf8Type; /// use object_store::path::Path; - /// use lance_io::{local::LocalObjectReader, encodings::binary::BinaryDecoder, traits::Reader}; + /// use lance_file::versions::v1::encoding::binary::BinaryDecoder; + /// use lance_io::{local::LocalObjectReader, traits::Reader}; /// /// async { /// let reader = LocalObjectReader::open_local_path("/tmp/foo.lance", 2048, None).await.unwrap(); @@ -285,16 +284,15 @@ fn plan_take_chunks( Ok(chunks) } -#[async_trait] -impl Decoder for BinaryDecoder<'_, T> { - async fn decode(&self) -> Result { +impl BinaryDecoder<'_, T> { + pub async fn decode(&self) -> Result { self.get(..).await } /// Take the values at the given indices. /// /// This function assumes indices are sorted. - async fn take(&self, indices: &UInt32Array) -> Result { + pub async fn take(&self, indices: &UInt32Array) -> Result { if indices.is_empty() { return Ok(new_empty_array(&T::DATA_TYPE)); } @@ -393,64 +391,17 @@ impl Decoder for BinaryDecoder<'_, T> { } } -#[async_trait] -impl AsyncIndex for BinaryDecoder<'_, T> { - type Output = Result; - - async fn get(&self, index: usize) -> Self::Output { - self.get(index..index + 1).await - } -} - -#[async_trait] -impl AsyncIndex> for BinaryDecoder<'_, T> { - type Output = Result; - - async fn get(&self, index: RangeFrom) -> Self::Output { - self.get(index.start..self.length).await - } -} - -#[async_trait] -impl AsyncIndex> for BinaryDecoder<'_, T> { - type Output = Result; - - async fn get(&self, index: RangeTo) -> Self::Output { - self.get(0..index.end).await - } -} - -#[async_trait] -impl AsyncIndex for BinaryDecoder<'_, T> { - type Output = Result; - - async fn get(&self, _: RangeFull) -> Self::Output { - self.get(0..self.length).await - } -} - -#[async_trait] -impl AsyncIndex for BinaryDecoder<'_, T> { - type Output = Result; - - async fn get(&self, params: ReadBatchParams) -> Self::Output { - match params { - ReadBatchParams::Range(r) => self.get(r).await, - // Ranges not supported in v1 files - ReadBatchParams::Ranges(_) => unimplemented!(), - ReadBatchParams::RangeFull => self.get(..).await, - ReadBatchParams::RangeTo(r) => self.get(r).await, - ReadBatchParams::RangeFrom(r) => self.get(r).await, - ReadBatchParams::Indices(indices) => self.take(&indices).await, +impl BinaryDecoder<'_, T> { + async fn decode_range(&self, index: Range) -> Result { + if index.end > self.length { + return Err(lance_core::Error::invalid_input(format!( + "v1 binary row range {}..{} exceeds length {}", + index.start, index.end, self.length + ))); + } + if index.is_empty() { + return Ok(new_empty_array(&T::DATA_TYPE)); } - } -} - -#[async_trait] -impl AsyncIndex> for BinaryDecoder<'_, T> { - type Output = Result; - - async fn get(&self, index: Range) -> Self::Output { let position_decoder = PlainDecoder::new( self.reader, &DataType::Int64, @@ -462,6 +413,19 @@ impl AsyncIndex> for BinaryDecoder<'_, T> { self.get_range(int64_positions, 0..index.len()).await } + + pub async fn get(&self, params: impl Into) -> Result { + match params.into() { + ReadBatchParams::Range(range) => self.decode_range(range).await, + ReadBatchParams::Ranges(_) => Err(lance_core::Error::invalid_input( + "multiple ranges are not supported by v1 binary encoding", + )), + ReadBatchParams::RangeFull => self.decode_range(0..self.length).await, + ReadBatchParams::RangeTo(range) => self.decode_range(0..range.end).await, + ReadBatchParams::RangeFrom(range) => self.decode_range(range.start..self.length).await, + ReadBatchParams::Indices(indices) => self.take(&indices).await, + } + } } #[cfg(test)] @@ -474,7 +438,7 @@ mod tests { use arrow_select::concat::concat; use lance_core::utils::tempfile::TempStdFile; - use crate::local::LocalObjectReader; + use lance_io::local::LocalObjectReader; async fn write_test_data( path: impl AsRef, diff --git a/rust/lance-io/src/encodings/dictionary.rs b/rust/lance-file/src/versions/v1/encoding/dictionary.rs similarity index 88% rename from rust/lance-io/src/encodings/dictionary.rs rename to rust/lance-file/src/versions/v1/encoding/dictionary.rs index b51adf66a59..0056632f725 100644 --- a/rust/lance-io/src/encodings/dictionary.rs +++ b/rust/lance-file/src/versions/v1/encoding/dictionary.rs @@ -14,18 +14,14 @@ use arrow_array::types::{ }; use arrow_array::{Array, ArrayRef, DictionaryArray, PrimitiveArray, UInt32Array}; use arrow_schema::DataType; -use async_trait::async_trait; -use crate::{ +use lance_core::{Error, Result}; +use lance_io::{ ReadBatchParams, traits::{Reader, Writer}, }; -use lance_core::{Error, Result}; -use super::AsyncIndex; -use super::plain::PlainEncoder; -use crate::encodings::plain::PlainDecoder; -use crate::encodings::{Decoder, Encoder}; +use super::plain::{PlainDecoder, PlainEncoder}; /// Encoder for Dictionary encoding. pub struct DictionaryEncoder<'a> { @@ -60,9 +56,8 @@ impl<'a> DictionaryEncoder<'a> { } } -#[async_trait] -impl Encoder for DictionaryEncoder<'_> { - async fn encode(&mut self, array: &[&dyn Array]) -> Result { +impl DictionaryEncoder<'_> { + pub async fn encode(&mut self, array: &[&dyn Array]) -> Result { use DataType::*; match self.key_type { @@ -165,36 +160,17 @@ impl<'a> DictionaryDecoder<'a> { } } -#[async_trait] -impl Decoder for DictionaryDecoder<'_> { - async fn decode(&self) -> Result { +impl DictionaryDecoder<'_> { + pub async fn decode(&self) -> Result { self.decode_impl(..).await } - async fn take(&self, indices: &UInt32Array) -> Result { + pub async fn take(&self, indices: &UInt32Array) -> Result { self.decode_impl(indices.clone()).await } -} - -#[async_trait] -impl AsyncIndex for DictionaryDecoder<'_> { - type Output = Result; - - async fn get(&self, _index: usize) -> Self::Output { - Err(Error::not_supported_source( - "DictionaryDecoder does not support get()" - .to_string() - .into(), - )) - } -} - -#[async_trait] -impl AsyncIndex for DictionaryDecoder<'_> { - type Output = Result; - async fn get(&self, params: ReadBatchParams) -> Self::Output { - self.decode_impl(params.clone()).await + pub async fn get(&self, params: impl Into) -> Result { + self.decode_impl(params).await } } @@ -202,10 +178,10 @@ impl AsyncIndex for DictionaryDecoder<'_> { mod tests { use super::*; - use crate::local::LocalObjectReader; use arrow_array::StringArray; use arrow_buffer::ArrowNativeType; use lance_core::utils::tempfile::TempStdFile; + use lance_io::local::LocalObjectReader; use tokio::io::AsyncWriteExt; async fn test_dict_decoder_for_type() { diff --git a/rust/lance-io/src/encodings/plain.rs b/rust/lance-file/src/versions/v1/encoding/plain.rs similarity index 88% rename from rust/lance-io/src/encodings/plain.rs rename to rust/lance-file/src/versions/v1/encoding/plain.rs index a5ec97c7beb..c084682cf39 100644 --- a/rust/lance-io/src/encodings/plain.rs +++ b/rust/lance-file/src/versions/v1/encoding/plain.rs @@ -6,33 +6,28 @@ //! Plain encoding works with fixed stride types, i.e., `boolean`, `i8...i64`, `f16...f64`, //! it stores the array directly in the file. It offers O(1) read access. -use std::ops::{Range, RangeFrom, RangeFull, RangeTo}; +use std::ops::Range; use std::slice::from_raw_parts; use std::sync::Arc; -use crate::{ - ReadBatchParams, - traits::{Reader, Writer}, -}; use arrow_arith::numeric::sub; use arrow_array::{ Array, ArrayRef, BooleanArray, FixedSizeBinaryArray, FixedSizeListArray, UInt8Array, - UInt32Array, builder::BooleanBuilder, cast::AsArray, make_array, new_empty_array, + UInt32Array, builder::BooleanBuilder, cast::AsArray, new_empty_array, }; -use arrow_buffer::{Buffer, bit_util}; -use arrow_data::{ArrayDataBuilder, BufferSpec, layout}; +use arrow_buffer::bit_util; use arrow_schema::{DataType, Field}; use arrow_select::{concat::concat, take::take}; use async_recursion::async_recursion; -use async_trait::async_trait; -use bytes::Bytes; use futures::stream::{self, StreamExt, TryStreamExt}; use lance_arrow::*; use lance_core::{Error, Result}; +use lance_io::{ + ReadBatchParams, + traits::{Reader, Writer}, +}; use tokio::io::AsyncWriteExt; -use crate::encodings::{AsyncIndex, Decoder}; - /// Encoder for plain encoding. /// pub struct PlainEncoder<'a> { @@ -175,51 +170,6 @@ fn get_byte_range(data_type: &DataType, row_range: Range) -> Range } } -pub fn bytes_to_array( - data_type: &DataType, - bytes: Bytes, - len: usize, - offset: usize, -) -> Result { - let layout = layout(data_type); - - if layout.buffers.len() != 1 { - return Err(Error::internal(format!( - "Can only convert datatypes that require one buffer, found {:?}", - data_type - ))); - } - - let buf: Buffer = if let BufferSpec::FixedWidth { - byte_width, - alignment, - } = &layout.buffers[0] - { - // this code is taken from - // https://github.com/apache/arrow-rs/blob/master/arrow-data/src/data.rs#L748-L768 - let len_plus_offset = len + offset; - let min_buffer_size = len_plus_offset.saturating_mul(*byte_width); - - // alignment or size isn't right -- just make a copy - if bytes.len() < min_buffer_size { - Buffer::copy_bytes_bytes(bytes, min_buffer_size) - } else { - Buffer::from_bytes_bytes(bytes, *alignment as u64) - } - } else { - // cases we don't handle, just copy - Buffer::from_slice_ref(bytes) - }; - - let array_data = ArrayDataBuilder::new(data_type.clone()) - .len(len) - .offset(offset) - .null_count(0) - .add_buffer(buf) - .build()?; - Ok(make_array(array_data)) -} - impl<'a> PlainDecoder<'a> { pub fn new( reader: &'a dyn Reader, @@ -258,7 +208,12 @@ impl<'a> PlainDecoder<'a> { } else { 0 }; - bytes_to_array(self.data_type, data, end - start, offset) + Ok(array_from_fixed_width_bytes( + self.data_type, + data, + end - start, + offset, + )?) } async fn decode_fixed_size_list( @@ -383,13 +338,12 @@ fn make_chunked_requests( chunked_ranges } -#[async_trait] -impl Decoder for PlainDecoder<'_> { - async fn decode(&self) -> Result { +impl PlainDecoder<'_> { + pub async fn decode(&self) -> Result { self.get(0..self.length).await } - async fn take(&self, indices: &UInt32Array) -> Result { + pub async fn take(&self, indices: &UInt32Array) -> Result { if indices.is_empty() { return Ok(new_empty_array(self.data_type)); } @@ -419,23 +373,9 @@ impl Decoder for PlainDecoder<'_> { let references = arrays.iter().map(|a| a.as_ref()).collect::>(); Ok(concat(&references)?) } -} - -#[async_trait] -impl AsyncIndex for PlainDecoder<'_> { - // TODO: should this return a Scalar value? - type Output = Result; - - async fn get(&self, index: usize) -> Self::Output { - self.get(index..index + 1).await - } -} - -#[async_trait] -impl AsyncIndex> for PlainDecoder<'_> { - type Output = Result; - async fn get(&self, index: Range) -> Self::Output { + #[async_recursion] + async fn decode_range(&self, index: Range) -> Result { if index.is_empty() { return Ok(new_empty_array(self.data_type)); } @@ -451,47 +391,16 @@ impl AsyncIndex> for PlainDecoder<'_> { _ => self.decode_primitive(index.start, index.end).await, } } -} - -#[async_trait] -impl AsyncIndex> for PlainDecoder<'_> { - type Output = Result; - - async fn get(&self, index: RangeFrom) -> Self::Output { - self.get(index.start..self.length).await - } -} - -#[async_trait] -impl AsyncIndex> for PlainDecoder<'_> { - type Output = Result; - - async fn get(&self, index: RangeTo) -> Self::Output { - self.get(0..index.end).await - } -} - -#[async_trait] -impl AsyncIndex for PlainDecoder<'_> { - type Output = Result; - - async fn get(&self, _: RangeFull) -> Self::Output { - self.get(0..self.length).await - } -} -#[async_trait] -impl AsyncIndex for PlainDecoder<'_> { - type Output = Result; - - async fn get(&self, params: ReadBatchParams) -> Self::Output { - match params { - ReadBatchParams::Range(r) => self.get(r).await, - // Ranges not supported in v1 files - ReadBatchParams::Ranges(_) => unimplemented!(), - ReadBatchParams::RangeFull => self.get(..).await, - ReadBatchParams::RangeTo(r) => self.get(r).await, - ReadBatchParams::RangeFrom(r) => self.get(r).await, + pub async fn get(&self, params: impl Into) -> Result { + match params.into() { + ReadBatchParams::Range(range) => self.decode_range(range).await, + ReadBatchParams::Ranges(_) => Err(Error::invalid_input( + "multiple ranges are not supported by v1 plain encoding", + )), + ReadBatchParams::RangeFull => self.decode_range(0..self.length).await, + ReadBatchParams::RangeTo(range) => self.decode_range(0..range.end).await, + ReadBatchParams::RangeFrom(range) => self.decode_range(range.start..self.length).await, ReadBatchParams::Indices(indices) => self.take(&indices).await, } } @@ -502,11 +411,14 @@ mod tests { use std::ops::Deref; use arrow_array::*; + use arrow_buffer::Buffer; + use arrow_data::ArrayDataBuilder; + use bytes::Bytes; use lance_core::utils::tempfile::TempStdFile; + use lance_io::local::LocalObjectReader; use rand::prelude::*; use super::*; - use crate::local::LocalObjectReader; #[tokio::test] async fn test_encode_decode_primitive_array() { @@ -628,9 +540,9 @@ mod tests { } #[tokio::test] - async fn test_bytes_to_array_padding() { + async fn test_array_from_fixed_width_bytes_padding() { let bytes = Bytes::from_static(&[0x01, 0x00, 0x02, 0x00, 0x03]); - let arr = bytes_to_array(&DataType::UInt16, bytes, 3, 0).unwrap(); + let arr = array_from_fixed_width_bytes(&DataType::UInt16, bytes, 3, 0).unwrap(); let expected = UInt16Array::from(vec![1, 2, 3]); assert_eq!(arr.as_ref(), &expected); @@ -682,7 +594,7 @@ mod tests { } async fn make_array_(data_type: &DataType, buffer: &Buffer) -> ArrayRef { - make_array( + arrow_array::make_array( ArrayDataBuilder::new(data_type.clone()) .len(126) .add_buffer(buffer.clone()) diff --git a/rust/lance-file/src/previous/format/metadata.rs b/rust/lance-file/src/versions/v1/format/metadata.rs similarity index 100% rename from rust/lance-file/src/previous/format/metadata.rs rename to rust/lance-file/src/versions/v1/format/metadata.rs diff --git a/rust/lance-file/src/previous/format/mod.rs b/rust/lance-file/src/versions/v1/format/mod.rs similarity index 100% rename from rust/lance-file/src/previous/format/mod.rs rename to rust/lance-file/src/versions/v1/format/mod.rs diff --git a/rust/lance-file/src/versions/v1/mod.rs b/rust/lance-file/src/versions/v1/mod.rs new file mode 100644 index 00000000000..e5ec87704be --- /dev/null +++ b/rust/lance-file/src/versions/v1/mod.rs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +//! Lance v1 file implementation. +//! +//! This module is the canonical home of the v1 reader, writer, metadata, and +//! page-table grammar. V1 accepts footer versions `(0, 0)` through `(0, 2)` and +//! writes the `(0, 2)` identity used by [`writer::FileWriter`]. + +pub mod encoding; +pub mod format; +pub mod page_table; +pub mod reader; +pub mod writer; + +use std::{collections::BTreeMap, sync::Arc}; + +use bytes::Bytes; +use lance_core::{ + Error, Result, + datatypes::{Field, Schema}, +}; +use lance_encoding::encoder::EncodedBatch; + +use crate::reader::ReaderProjection; + +fn append_field_ids( + fields: &[Field], + field_id_to_column_index: &BTreeMap, + column_indices: &mut Vec, +) { + for field in fields { + if let Some(column_index) = field_id_to_column_index.get(&(field.id as u32)).copied() { + column_indices.push(column_index); + } + if !field.is_blob() && !field.is_packed_struct() { + append_field_ids(&field.children, field_id_to_column_index, column_indices); + } + } +} + +pub fn projection_from_field_ids( + schema: &Schema, + field_id_to_column_index: &BTreeMap, +) -> ReaderProjection { + let mut column_indices = Vec::new(); + append_field_ids( + &schema.fields, + field_id_to_column_index, + &mut column_indices, + ); + ReaderProjection { + schema: Arc::new(schema.clone()), + column_indices, + } +} + +pub fn projection_from_whole_schema(schema: &Schema) -> ReaderProjection { + projection_from_field_ids(schema, &field_id_to_column_index(schema)) +} + +pub fn projection_from_column_names( + schema: &Schema, + column_names: &[&str], +) -> Result { + let field_id_to_column_index = field_id_to_column_index(schema); + let projected = schema.project(column_names)?; + Ok(projection_from_field_ids( + &projected, + &field_id_to_column_index, + )) +} + +/// Count physical columns represented by a field in a v1 footer. +pub fn physical_column_count(field: &Field) -> usize { + if field.is_blob() || field.is_packed_struct() { + 1 + } else { + 1 + field + .children + .iter() + .map(physical_column_count) + .sum::() + } +} + +/// Build persisted field-to-column entries for a v1 data file. +pub fn data_file_columns(schema: &Schema) -> (Vec, Vec) { + let mut field_ids = Vec::new(); + let mut column_indices = Vec::new(); + append_physical_fields(&schema.fields, &mut field_ids, &mut column_indices, &mut 0); + (field_ids, column_indices) +} + +fn field_id_to_column_index(schema: &Schema) -> BTreeMap { + let (field_ids, column_indices) = data_file_columns(schema); + field_ids + .into_iter() + .zip(column_indices) + .map(|(field_id, column_index)| (field_id as u32, column_index as u32)) + .collect() +} + +fn append_physical_fields( + fields: &[Field], + field_ids: &mut Vec, + column_indices: &mut Vec, + next_column: &mut i32, +) { + for field in fields { + field_ids.push(field.id); + column_indices.push(*next_column); + *next_column += 1; + if !field.is_blob() && !field.is_packed_struct() { + append_physical_fields(&field.children, field_ids, column_indices, next_column); + } + } +} + +pub fn encode_self_described_batch(_batch: &EncodedBatch) -> Result { + Err(Error::not_supported( + "Lance v1 files use versions::v1::writer::FileWriter".to_string(), + )) +} + +pub fn encode_mini_batch(_batch: &EncodedBatch) -> Result { + Err(Error::not_supported( + "Lance v1 files use versions::v1::writer::FileWriter".to_string(), + )) +} diff --git a/rust/lance-file/src/previous/page_table.rs b/rust/lance-file/src/versions/v1/page_table.rs similarity index 99% rename from rust/lance-file/src/previous/page_table.rs rename to rust/lance-file/src/versions/v1/page_table.rs index cc246caa585..b2ff750c13a 100644 --- a/rust/lance-file/src/previous/page_table.rs +++ b/rust/lance-file/src/versions/v1/page_table.rs @@ -5,14 +5,14 @@ use arrow_array::builder::Int64Builder; use arrow_array::{Array, Int64Array}; use arrow_schema::DataType; use lance_core::deepsize::DeepSizeOf; -use lance_io::encodings::Decoder; -use lance_io::encodings::plain::PlainDecoder; use std::collections::BTreeMap; use tokio::io::AsyncWriteExt; use lance_core::{Error, Result}; use lance_io::traits::{Reader, Writer}; +use super::encoding::plain::PlainDecoder; + #[derive(Clone, Debug, PartialEq, DeepSizeOf)] pub struct PageInfo { pub position: usize, diff --git a/rust/lance-file/src/previous/reader.rs b/rust/lance-file/src/versions/v1/reader.rs similarity index 97% rename from rust/lance-file/src/previous/reader.rs rename to rust/lance-file/src/versions/v1/reader.rs index 1ab861985e1..c300ea22b16 100644 --- a/rust/lance-file/src/previous/reader.rs +++ b/rust/lance-file/src/versions/v1/reader.rs @@ -25,21 +25,18 @@ use lance_core::cache::{CacheKey, LanceCache}; use lance_core::datatypes::{Field, Schema}; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, Result}; -use lance_io::encodings::AsyncIndex; -use lance_io::encodings::dictionary::DictionaryDecoder; use lance_io::stream::{RecordBatchStream, RecordBatchStreamAdapter}; use lance_io::traits::Reader; -use lance_io::utils::{ - read_fixed_stride_array, read_metadata_offset, read_struct, read_struct_from_buf, -}; +use lance_io::utils::{read_metadata_offset, read_struct, read_struct_from_buf}; use lance_io::{ReadBatchParams, object_store::ObjectStore}; use std::borrow::Cow; use object_store::path::Path; use tracing::instrument; -use crate::previous::format::metadata::Metadata; -use crate::previous::page_table::{PageInfo, PageTable}; +use crate::versions::v1::encoding::{dictionary::DictionaryDecoder, read_fixed_stride_array}; +use crate::versions::v1::format::metadata::Metadata; +use crate::versions::v1::page_table::{PageInfo, PageTable}; /// Lance File Reader. /// @@ -607,7 +604,7 @@ async fn read_binary_array( ) -> Result { let page_info = get_page_info(page_table, field, batch_id)?; - lance_io::utils::read_binary_array( + crate::versions::v1::encoding::read_binary_array( reader.object_reader.as_ref(), &field.data_type(), field.nullable, @@ -785,7 +782,7 @@ where #[cfg(test)] mod tests { - use crate::previous::writer::{FileWriter as PreviousFileWriter, NotSelfDescribing}; + use crate::versions::v1::writer::{FileWriter as V1FileWriter, NotSelfDescribing}; use super::*; @@ -840,7 +837,7 @@ mod tests { } schema.set_dictionary(&batches[0]).unwrap(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -904,7 +901,7 @@ mod tests { )])); let batch = RecordBatch::try_new(arrow_schema.clone(), vec![struct_arr]).unwrap(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -958,7 +955,7 @@ mod tests { .collect::>(); let batches_ref = batches.iter().collect::>(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -985,7 +982,7 @@ mod tests { let schema: Schema = Schema::try_from(arrow_schema.as_ref()).unwrap(); let batch = RecordBatch::try_new(arrow_schema.clone(), vec![struct_array.clone()]).unwrap(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -1110,7 +1107,7 @@ mod tests { // write to a lance file let store = ObjectStore::memory(); let path = Path::from("/takes"); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -1227,7 +1224,7 @@ mod tests { let store = ObjectStore::memory(); let path = Path::from("/take_list"); let schema: Schema = (&arrow_schema).try_into().unwrap(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -1304,7 +1301,7 @@ mod tests { .unwrap(); let schema: Schema = (&arrow_schema).try_into().unwrap(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -1338,7 +1335,7 @@ mod tests { // write to a lance file let store = ObjectStore::memory(); let path = Path::from("/read_range"); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -1365,7 +1362,7 @@ mod tests { let arrow_schema = ArrowSchema::new(vec![ArrowField::new("i", DataType::Int32, true)]); let schema = Schema::try_from(&arrow_schema).unwrap(); - let mut writer = PreviousFileWriter::::try_new( + let mut writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -1425,7 +1422,7 @@ mod tests { false, )])); let schema = Schema::try_from(arrow_schema.as_ref()).unwrap(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -1470,7 +1467,7 @@ mod tests { let partial_schema = schema.project(&["f50"]).unwrap(); let partial_arrow: ArrowSchema = (&partial_schema).into(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, partial_schema.clone(), diff --git a/rust/lance-file/src/previous/writer/mod.rs b/rust/lance-file/src/versions/v1/writer/mod.rs similarity index 94% rename from rust/lance-file/src/previous/writer/mod.rs rename to rust/lance-file/src/versions/v1/writer/mod.rs index ab13e782367..911065d2c2c 100644 --- a/rust/lance-file/src/previous/writer/mod.rs +++ b/rust/lance-file/src/versions/v1/writer/mod.rs @@ -18,17 +18,18 @@ use async_trait::async_trait; use lance_arrow::*; use lance_core::datatypes::{Encoding, Field, NullabilityComparison, Schema, SchemaCompareOptions}; use lance_core::{Error, Result}; -use lance_io::encodings::{ - Encoder, binary::BinaryEncoder, dictionary::DictionaryEncoder, plain::PlainEncoder, -}; use lance_io::object_store::ObjectStore; use lance_io::traits::{WriteExt, Writer}; use object_store::path::Path; use tokio::io::AsyncWriteExt; use crate::format::{MAGIC, MAJOR_VERSION, MINOR_VERSION}; -use crate::previous::format::metadata::{Metadata, StatisticsMetadata}; -use crate::previous::page_table::{PageInfo, PageTable}; +use crate::versions::v1::encoding::{ + binary::BinaryEncoder, dictionary::DictionaryEncoder, plain::PlainEncoder, + write_schema_dictionaries, +}; +use crate::versions::v1::format::metadata::{Metadata, StatisticsMetadata}; +use crate::versions::v1::page_table::{PageInfo, PageTable}; use crate::writer::FileWriteSummary; /// The file format currently includes a "manifest" where it stores the schema for @@ -623,53 +624,6 @@ impl FileWriter { } } - /// Writes the dictionaries (using plain/binary encoding) into the file - /// - /// The offsets and lengths of the written buffers are stored in the given - /// schema so that the dictionaries can be loaded in the future. - async fn write_dictionaries(writer: &mut dyn Writer, schema: &mut Schema) -> Result<()> { - // Write dictionary values. - let max_field_id = schema.max_field_id().unwrap_or(-1); - for field_id in 0..max_field_id + 1 { - if let Some(field) = schema.mut_field_by_id(field_id) - && field.data_type().is_dictionary() - { - let dict_info = field.dictionary.as_mut().ok_or_else(|| { - // and wrap it in here. - Error::io(format!("Lance field {} misses dictionary info", field.name)) - })?; - - let value_arr = dict_info.values.as_ref().ok_or_else(|| { - Error::invalid_input(format!( - "Lance field {} is dictionary type, but misses the dictionary value array", - field.name - )) - })?; - - let data_type = value_arr.data_type(); - let pos = match data_type { - dt if dt.is_numeric() => { - let mut encoder = PlainEncoder::new(writer, dt); - encoder.encode(&[value_arr]).await? - } - dt if dt.is_binary_like() => { - let mut encoder = BinaryEncoder::new(writer); - encoder.encode(&[value_arr]).await? - } - _ => { - return Err(Error::schema(format!( - "Does not support {} as dictionary value type", - value_arr.data_type() - ))); - } - }; - dict_info.offset = pos; - dict_info.length = value_arr.len(); - } - } - Ok(()) - } - async fn write_footer(&mut self) -> Result<()> { // Step 1. Write page table. let field_id_offset = *self.schema.field_ids().iter().min().unwrap(); @@ -683,7 +637,7 @@ impl FileWriter { self.metadata.stats_metadata = self.write_statistics().await?; // Step 3. Write manifest and dictionary values. - Self::write_dictionaries(self.object_writer.as_mut(), &mut self.schema).await?; + write_schema_dictionaries(self.object_writer.as_mut(), &mut self.schema).await?; let pos = M::store_schema(self.object_writer.as_mut(), &self.schema).await?; // Step 4. Write metadata. @@ -756,7 +710,7 @@ mod tests { }; use arrow_select::concat::concat_batches; - use crate::previous::reader::FileReader; + use crate::versions::v1::reader::FileReader; #[tokio::test] async fn test_write_file() { diff --git a/rust/lance-file/src/previous/writer/statistics.rs b/rust/lance-file/src/versions/v1/writer/statistics.rs similarity index 100% rename from rust/lance-file/src/previous/writer/statistics.rs rename to rust/lance-file/src/versions/v1/writer/statistics.rs diff --git a/rust/lance-file/src/versions/v2_0/mod.rs b/rust/lance-file/src/versions/v2_0/mod.rs new file mode 100644 index 00000000000..5003345a70c --- /dev/null +++ b/rust/lance-file/src/versions/v2_0/mod.rs @@ -0,0 +1,153 @@ +//! Lance v2.0 file composition. + +use std::{collections::BTreeMap, sync::Arc}; + +use bytes::Bytes; +use lance_core::{ + Result, + datatypes::{Field, Schema}, +}; +use lance_encoding::{decoder::PageInfo, encoder::EncodedBatch}; +use lance_io::traits::Writer as ObjectWriter; + +use crate::{reader::ReadProjection, writer::FileWriterOptions}; + +mod reader; +mod writer; + +#[cfg(test)] +pub(crate) use reader::test_projection_length; +pub(crate) use reader::{ + decode_column_metadata, finish_metadata, finish_metadata_index, validate_global_buffers, +}; +pub use reader::{ + projection_from_column_names, projection_from_field_ids, projection_from_whole_schema, +}; +pub use writer::Writer; + +pub(crate) fn read_projection() -> Arc { + reader::read_projection() +} + +/// Count physical columns represented by a field in a v2.0 footer. +pub fn physical_column_count(field: &Field) -> usize { + if field.is_blob() || field.is_packed_struct() { + 1 + } else { + 1 + field + .children + .iter() + .map(physical_column_count) + .sum::() + } +} + +/// Build persisted field-to-column entries for a v2.0 data file. +pub fn data_file_columns(schema: &Schema) -> (Vec, Vec) { + let mut field_ids = Vec::new(); + let mut column_indices = Vec::new(); + append_physical_fields(&schema.fields, &mut field_ids, &mut column_indices, &mut 0); + (field_ids, column_indices) +} + +pub(super) fn field_id_to_column_index(schema: &Schema) -> BTreeMap { + let (field_ids, column_indices) = data_file_columns(schema); + field_ids + .into_iter() + .zip(column_indices) + .map(|(field_id, column_index)| (field_id as u32, column_index as u32)) + .collect() +} + +fn append_physical_fields( + fields: &[Field], + field_ids: &mut Vec, + column_indices: &mut Vec, + next_column: &mut i32, +) { + for field in fields { + field_ids.push(field.id); + column_indices.push(*next_column); + *next_column += 1; + if !field.is_blob() && !field.is_packed_struct() { + append_physical_fields(&field.children, field_ids, column_indices, next_column); + } + } +} + +fn is_external_metadata_structural_header( + fields: &[Field], + target_column: usize, + next_column: &mut usize, +) -> Option { + for field in fields { + if *next_column == target_column { + return Some(field.logical_type.is_struct() && !field.is_packed_struct()); + } + *next_column += 1; + if !field.is_blob() + && !field.is_packed_struct() + && let Some(is_header) = + is_external_metadata_structural_header(&field.children, target_column, next_column) + { + return Some(is_header); + } + } + None +} + +pub(super) fn should_copy_external_metadata_column( + schema: &Schema, + column_index: usize, + has_existing_pages: bool, +) -> bool { + let mut next_column = 0; + let is_header = + is_external_metadata_structural_header(&schema.fields, column_index, &mut next_column) + .unwrap_or(false); + !is_header || !has_existing_pages +} + +pub(super) fn finalize_external_metadata_column( + schema: &Schema, + column_index: usize, + pages: &mut Vec, + num_rows: u64, +) { + let mut next_column = 0; + let is_header = + is_external_metadata_structural_header(&schema.fields, column_index, &mut next_column) + .unwrap_or(false); + if is_header && !pages.is_empty() { + pages[0].num_rows = num_rows; + pages[0].priority = 0; + pages.truncate(1); + } +} + +/// Create a v2.0 writer with an explicit schema. +pub fn create_writer( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, +) -> Result { + Writer::try_new(object_writer, schema, options) +} + +/// Create a v2.0 writer whose schema is inferred from the first batch. +pub fn create_lazy_writer( + object_writer: Box, + options: FileWriterOptions, +) -> Writer { + Writer::new_lazy(object_writer, options) +} + +/// Encode a self-described v2.0 batch. +pub fn encode_self_described_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, true) +} + +/// Encode a mini-lance v2.0 batch. +pub fn encode_mini_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, false) +} diff --git a/rust/lance-file/src/versions/v2_0/reader.rs b/rust/lance-file/src/versions/v2_0/reader.rs new file mode 100644 index 00000000000..5a96031ba79 --- /dev/null +++ b/rust/lance-file/src/versions/v2_0/reader.rs @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::{collections::BTreeMap, sync::Arc}; + +use async_trait::async_trait; +use bytes::Bytes; +use lance_core::{ + Error, Result, + cache::LanceCache, + datatypes::{Field, Schema}, +}; +use lance_encoding::{ + EncodingsIo, + decoder::{ColumnInfo, PageEncoding, PageInfo}, + format::pb, +}; +use prost::{Message, Name}; + +use crate::{ + format::pbfile, + reader::{ + BufferDescriptor, CachedFileMetadata, FileMetadataIndex, FileMetadataProvider, FileReader, + PreparedProjection, RawFileMetadata, ReadProjection, ReaderProjection, + normalized_column_num_rows, verify_uniform_lengths, + }, + version::ConcreteFileVersion, +}; + +fn fetch_encoding(encoding: &pbfile::Encoding) -> Result { + match &encoding.location { + Some(pbfile::encoding::Location::Indirect(_)) => Err(Error::invalid_input_source( + "Indirect file encodings are not supported".into(), + )), + Some(pbfile::encoding::Location::Direct(encoding)) => { + let envelope = prost_types::Any::decode(Bytes::from(encoding.encoding.clone())) + .map_err(|error| { + Error::invalid_input_source( + format!("Invalid direct {} encoding envelope: {error}", M::NAME).into(), + ) + })?; + envelope.to_msg::().map_err(|error| { + Error::invalid_input_source( + format!("Invalid direct {} encoding: {error}", M::NAME).into(), + ) + }) + } + Some(pbfile::encoding::Location::None(_)) => Err(Error::invalid_input_source( + format!("Missing {} encoding description", M::NAME).into(), + )), + None => Err(Error::invalid_input_source( + format!("Missing {} encoding location", M::NAME).into(), + )), + } +} + +pub fn decode_column( + column_index: u32, + metadata: &pbfile::ColumnMetadata, +) -> Result> { + let page_infos = metadata + .pages + .iter() + .enumerate() + .map(|(page_index, page)| { + let array_encoding = + fetch_encoding::(page.encoding.as_ref().ok_or_else(|| { + Error::invalid_input_source( + format!( + "Column {} page {} is missing its encoding", + column_index, page_index + ) + .into(), + ) + })?)?; + if page.buffer_offsets.len() != page.buffer_sizes.len() { + return Err(Error::invalid_input_source( + format!( + "Column {} page {} has {} buffer offsets but {} buffer sizes", + column_index, + page_index, + page.buffer_offsets.len(), + page.buffer_sizes.len() + ) + .into(), + )); + } + let buffer_offsets_and_sizes = Arc::from( + page.buffer_offsets + .iter() + .zip(&page.buffer_sizes) + .map(|(offset, size)| (*offset, *size)) + .collect::>(), + ); + Ok(PageInfo { + buffer_offsets_and_sizes, + encoding: PageEncoding::Array(array_encoding), + num_rows: page.length, + priority: page.priority, + }) + }) + .collect::>>()?; + + if metadata.buffer_offsets.len() != metadata.buffer_sizes.len() { + return Err(Error::invalid_input_source( + format!( + "Column {} has {} buffer offsets but {} buffer sizes", + column_index, + metadata.buffer_offsets.len(), + metadata.buffer_sizes.len() + ) + .into(), + )); + } + let buffer_offsets_and_sizes = Arc::from( + metadata + .buffer_offsets + .iter() + .zip(&metadata.buffer_sizes) + .map(|(offset, size)| (*offset, *size)) + .collect::>(), + ); + Ok(Arc::new(ColumnInfo { + index: column_index, + page_infos: Arc::from(page_infos), + buffer_offsets_and_sizes, + encoding: fetch_encoding(metadata.encoding.as_ref().ok_or_else(|| { + Error::invalid_input_source( + format!("Column {} is missing its encoding", column_index).into(), + ) + })?)?, + })) +} + +pub fn decode_column_metadata( + column_metadatas: &[pbfile::ColumnMetadata], +) -> Result>> { + column_metadatas + .iter() + .enumerate() + .map(|(column_index, metadata)| { + let column_index = u32::try_from(column_index).map_err(|_| { + Error::invalid_input_source("File has more than u32::MAX columns".into()) + })?; + decode_column(column_index, metadata) + }) + .collect() +} + +pub async fn prepare_projection( + metadata_provider: &FileMetadataProvider, + projection: &ReaderProjection, + _io: &Arc, + _cache: &Arc, +) -> Result { + match metadata_provider { + FileMetadataProvider::Full(metadata) => { + FileReader::validate_projection(projection, metadata)?; + Ok(PreparedProjection { + column_infos: metadata.column_infos.clone(), + decoder_projection: projection.clone(), + }) + } + FileMetadataProvider::Indexed(metadata_index) => { + FileMetadataProvider::validate_indexed_projection_structure( + projection, + metadata_index, + )?; + Err(FileMetadataProvider::indexed_projection_error( + projection, + metadata_index, + )) + } + } +} + +fn field_column_shape(field: &Field) -> (bool, bool) { + if field.is_blob() || field.is_packed_struct() { + return (true, false); + } + (true, !field.children.is_empty()) +} + +fn append_field_ids( + fields: &[Field], + field_id_to_column_index: &BTreeMap, + column_indices: &mut Vec, +) { + for field in fields { + let (contributes, recurse) = field_column_shape(field); + if contributes + && let Some(column_index) = field_id_to_column_index.get(&(field.id as u32)).copied() + { + column_indices.push(column_index); + } + if recurse { + append_field_ids(&field.children, field_id_to_column_index, column_indices); + } + } +} + +pub fn projection_from_field_ids( + schema: &Schema, + field_id_to_column_index: &BTreeMap, +) -> ReaderProjection { + let mut column_indices = Vec::new(); + append_field_ids( + &schema.fields, + field_id_to_column_index, + &mut column_indices, + ); + ReaderProjection { + schema: Arc::new(schema.clone()), + column_indices, + } +} + +pub fn projection_from_whole_schema(schema: &Schema) -> ReaderProjection { + projection_from_field_ids(schema, &super::field_id_to_column_index(schema)) +} + +pub fn projection_from_column_names( + schema: &Schema, + column_names: &[&str], +) -> Result { + let field_id_to_column_index = super::field_id_to_column_index(schema); + let projected = schema.project(column_names)?; + Ok(projection_from_field_ids( + &projected, + &field_id_to_column_index, + )) +} + +fn children_share_parent_length(field: &Field) -> bool { + field.logical_type.is_struct() +} + +fn validate_field_length Result>( + field: &Field, + comparable: bool, + column_indices: &[u32], + cursor: &mut usize, + column_len: &F, +) -> Result { + let (contributes, recurse) = field_column_shape(field); + let mut field_rows = None; + if contributes { + let column = *column_indices.get(*cursor).ok_or_else(|| { + Error::invalid_input(format!( + "projection supplied fewer column indices than its fields require (ran out at field '{}')", + field.name + )) + })?; + *cursor += 1; + field_rows = Some(column_len(column as usize)?); + } + if recurse { + let enforce_children = comparable && children_share_parent_length(field); + for child in &field.children { + let child_rows = + validate_field_length(child, enforce_children, column_indices, cursor, column_len)?; + let expected = *field_rows.get_or_insert(child_rows); + if enforce_children && child_rows != expected { + return Err(Error::invalid_input(format!( + "cannot read field '{}': its children have differing lengths (child '{}' has {} rows, but the field has {}); a struct's children must all have the same length", + field.name, child.name, child_rows, expected + ))); + } + } + } + field_rows.ok_or_else(|| { + Error::invalid_input(format!( + "projected field '{}' maps to no columns", + field.name + )) + }) +} + +pub fn prepared_read_length(prepared: &PreparedProjection) -> Result { + let column_len = |column: usize| { + let info = prepared.column_infos.get(column).ok_or_else(|| { + Error::invalid_input(format!( + "projection references column index {} but only {} columns are available", + column, + prepared.column_infos.len() + )) + })?; + normalized_column_num_rows(info) + }; + let mut cursor = 0; + let mut field_lengths = Vec::with_capacity(prepared.decoder_projection.schema.fields.len()); + for field in &prepared.decoder_projection.schema.fields { + let rows = validate_field_length( + field, + true, + &prepared.decoder_projection.column_indices, + &mut cursor, + &column_len, + )?; + field_lengths.push((field.name.as_str(), rows)); + } + if cursor != prepared.decoder_projection.column_indices.len() { + return Err(Error::invalid_input(format!( + "projection supplied {} column indices but its fields require {}", + prepared.decoder_projection.column_indices.len(), + cursor + ))); + } + verify_uniform_lengths(&field_lengths) +} + +#[derive(Debug)] +struct V20ReadProjection; + +pub(super) fn read_projection() -> Arc { + Arc::new(V20ReadProjection) +} + +#[async_trait] +impl ReadProjection for V20ReadProjection { + fn validate_indexed( + &self, + projection: &ReaderProjection, + metadata_index: &FileMetadataIndex, + ) -> Result<()> { + FileMetadataProvider::validate_indexed_projection_structure(projection, metadata_index)?; + Err(FileMetadataProvider::indexed_projection_error( + projection, + metadata_index, + )) + } + + fn read_length(&self, prepared: &PreparedProjection) -> Result { + prepared_read_length(prepared) + } + + async fn prepare( + &self, + metadata_provider: &FileMetadataProvider, + projection: &ReaderProjection, + io: &Arc, + cache: &Arc, + ) -> Result<(PreparedProjection, u64)> { + let prepared = prepare_projection(metadata_provider, projection, io, cache).await?; + let read_len = self.read_length(&prepared)?; + Ok((prepared, read_len)) + } +} + +pub fn finish_metadata(raw: RawFileMetadata) -> Result { + if !matches!( + (raw.footer.major_version, raw.footer.minor_version), + (0, 3) | (2, 0) + ) { + return Err(Error::version_conflict( + "Attempt to use the Lance v2.0 reader for a different file version".to_string(), + raw.footer.major_version, + raw.footer.minor_version, + )); + } + let column_infos = decode_column_metadata(&raw.column_metadatas)?; + Ok(CachedFileMetadata { + file_schema: raw.file_schema, + column_metadatas: raw.column_metadatas, + column_infos, + num_rows: raw.num_rows, + file_buffers: raw.file_buffers, + num_data_bytes: raw.num_data_bytes, + num_column_metadata_bytes: raw.num_column_metadata_bytes, + num_global_buffer_bytes: raw.num_global_buffer_bytes, + num_footer_bytes: raw.num_footer_bytes, + major_version: raw.footer.major_version, + minor_version: raw.footer.minor_version, + version: ConcreteFileVersion::V2_0, + file_size_bytes: raw.file_size_bytes, + retained_global_buffers: raw.retained_global_buffers, + }) +} + +pub fn validate_global_buffers(_buffers: &[BufferDescriptor]) -> Result<()> { + Ok(()) +} + +pub fn finish_metadata_index(index: FileMetadataIndex) -> Result { + if index.version == ConcreteFileVersion::V2_0 { + Ok(index) + } else { + let (major, minor) = index.version.to_standard_footer_numbers(); + Err(Error::version_conflict( + "Attempt to use the Lance v2.0 reader for a different metadata index".to_string(), + major, + minor, + )) + } +} + +#[cfg(test)] +pub fn test_projection_length( + schema: &Schema, + column_indices: &[u32], + column_lengths: &[u64], +) -> Result { + let column_len = |column: usize| { + column_lengths.get(column).copied().ok_or_else(|| { + Error::invalid_input(format!("missing synthetic length for column {column}")) + }) + }; + let mut cursor = 0; + let mut field_lengths = Vec::with_capacity(schema.fields.len()); + for field in &schema.fields { + let rows = validate_field_length(field, true, column_indices, &mut cursor, &column_len)?; + field_lengths.push((field.name.as_str(), rows)); + } + verify_uniform_lengths(&field_lengths) +} diff --git a/rust/lance-file/src/versions/v2_0/writer.rs b/rust/lance-file/src/versions/v2_0/writer.rs new file mode 100644 index 00000000000..5f13632b4ed --- /dev/null +++ b/rust/lance-file/src/versions/v2_0/writer.rs @@ -0,0 +1,958 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use core::panic; +use std::collections::HashMap; +use std::sync::Arc; + +use arrow_array::{ArrayRef, RecordBatch}; + +use arrow_data::ArrayData; +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use futures::StreamExt; +use futures::stream::FuturesOrdered; +use lance_core::datatypes::{Field, Schema as LanceSchema}; +use lance_core::utils::bit::pad_bytes; +use lance_core::{Error, Result}; +use lance_encoding::array_encoding::ArrayFieldEncodingStrategy; +use lance_encoding::decoder::PageEncoding; +use lance_encoding::encoder::{ + BatchEncoder, EncodeTask, EncodedBatch, EncodedPage, EncodingOptions, FieldEncoder, + FieldEncodingStrategy, OutOfLineBuffers, +}; +use lance_encoding::repdef::RepDefBuilder; +use lance_io::object_store::ObjectStore; +use lance_io::traits::Writer as ObjectWriter; +use log::{debug, warn}; +use object_store::path::Path; +use prost::Message; +use prost_types::Any; +use tokio::io::AsyncWrite; +use tokio::io::AsyncWriteExt; +use tracing::instrument; + +use crate::datatypes::FieldsWithMeta; +use crate::format::MAGIC; +use crate::format::pb; +use crate::format::pbfile; +use crate::format::pbfile::DirectEncoding; +use crate::writer::{ + ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, FileWriteSummary, FileWriterOptions, + PAGE_BUFFER_ALIGNMENT, +}; + +const PAD_BUFFER: [u8; PAGE_BUFFER_ALIGNMENT] = [72; PAGE_BUFFER_ALIGNMENT]; +// In 2.1+, we split large pages on read instead of write to avoid empty pages +// and small pages issues. However, we keep the write-time limit at 32MB to avoid +// potential regressions in 2.0 format readers. +// +// This limit is not applied in the 2.1 writer +const MAX_PAGE_BYTES: usize = 32 * 1024 * 1024; +// Total in-memory budget for buffering serialized page metadata before flushing +// to the spill file. Divided evenly across columns (with a floor of 64 bytes). +const DEFAULT_SPILL_BUFFER_LIMIT: usize = 256 * 1024; + +/// Spills serialized page metadata to a temporary file to bound memory usage. +/// +/// The spill file is an unstructured sequence of "chunks". Each chunk is a +/// contiguous run of length-delimited protobuf `Page` messages belonging to a +/// single column. Chunks from different columns are interleaved in the order +/// they are flushed (i.e. whenever a column's in-memory buffer exceeds +/// `per_column_limit`). The `column_chunks` index records the (offset, length) +/// of every chunk so each column's pages can be read back and reassembled in +/// order. +struct PageMetadataSpill { + writer: Box, + object_store: Arc, + path: Path, + /// Current write position in the spill file. + position: u64, + /// Per-column buffer of serialized (length-delimited protobuf) page metadata + /// that has not yet been flushed to the spill file. + column_buffers: Vec>, + /// Per-column list of chunks that have been flushed to the spill file. + /// Each entry is (offset, length) pointing into the spill file. + column_chunks: Vec>, + /// Maximum bytes to buffer per column before flushing to the spill file. + per_column_limit: usize, +} + +impl PageMetadataSpill { + async fn new(object_store: Arc, path: Path, num_columns: usize) -> Result { + let writer = object_store.create(&path).await?; + let per_column_limit = (DEFAULT_SPILL_BUFFER_LIMIT / num_columns.max(1)).max(64); + Ok(Self { + writer, + object_store, + path, + position: 0, + column_buffers: vec![Vec::new(); num_columns], + column_chunks: vec![Vec::new(); num_columns], + per_column_limit, + }) + } + + async fn append_page( + &mut self, + column_idx: usize, + page: &pbfile::column_metadata::Page, + ) -> Result<()> { + page.encode_length_delimited(&mut self.column_buffers[column_idx]) + .map_err(|e| { + Error::io_source(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + e, + ))) + })?; + if self.column_buffers[column_idx].len() >= self.per_column_limit { + self.flush_column(column_idx).await?; + } + Ok(()) + } + + async fn flush_column(&mut self, column_idx: usize) -> Result<()> { + let buf = &self.column_buffers[column_idx]; + if buf.is_empty() { + return Ok(()); + } + let len = buf.len(); + self.writer.write_all(buf).await?; + self.column_chunks[column_idx].push((self.position, len as u32)); + self.position += len as u64; + self.column_buffers[column_idx].clear(); + Ok(()) + } + + async fn shutdown_writer(&mut self) -> Result<()> { + for col_idx in 0..self.column_buffers.len() { + self.flush_column(col_idx).await?; + } + ObjectWriter::shutdown(self.writer.as_mut()).await?; + Ok(()) + } +} + +fn decode_spilled_chunk(data: &Bytes) -> Result> { + let mut pages = Vec::new(); + let mut cursor = data.clone(); + while cursor.has_remaining() { + let page = + pbfile::column_metadata::Page::decode_length_delimited(&mut cursor).map_err(|e| { + Error::io_source(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + e, + ))) + })?; + pages.push(page); + } + Ok(pages) +} + +enum PageSpillState { + Pending(Arc, Path), + Active(PageMetadataSpill), +} + +/// A writer for the Lance v2.0 file grammar. +pub struct Writer { + writer: Box, + schema: Option, + column_writers: Vec>, + column_metadata: Vec, + field_id_to_column_indices: Vec<(u32, u32)>, + num_columns: u32, + rows_written: u64, + // The number of rows written for each top-level field (i.e. each entry in + // `column_writers`). With `write_batch` every field advances together and + // these are all equal, but `write_column` advances one field at a time, so + // a single file may end up with columns of differing item counts. + field_rows_written: Vec, + global_buffers: Vec<(u64, u64)>, + schema_metadata: HashMap, + encoding_strategy: Box, + options: FileWriterOptions, + page_spill: Option, +} + +fn initial_column_metadata() -> pbfile::ColumnMetadata { + pbfile::ColumnMetadata { + pages: Vec::new(), + buffer_offsets: Vec::new(), + buffer_sizes: Vec::new(), + encoding: None, + } +} + +impl Writer { + /// Create a new v2.0 writer with a desired output schema. + pub fn try_new( + object_writer: Box, + schema: LanceSchema, + options: FileWriterOptions, + ) -> Result { + let mut writer = Self::new_lazy(object_writer, options); + writer.initialize(schema)?; + Ok(writer) + } + + /// Create a new v2.0 writer without a desired output schema. + /// + /// The output schema will be set based on the first batch of data to arrive. + /// If no data arrives and the writer is finished then the write will fail. + pub fn new_lazy(object_writer: Box, options: FileWriterOptions) -> Self { + Self { + writer: object_writer, + schema: None, + column_writers: Vec::new(), + column_metadata: Vec::new(), + num_columns: 0, + rows_written: 0, + field_rows_written: Vec::new(), + field_id_to_column_indices: Vec::new(), + global_buffers: Vec::new(), + schema_metadata: HashMap::new(), + page_spill: None, + encoding_strategy: Box::new(ArrayFieldEncodingStrategy::new()), + options, + } + } + + /// Spill page metadata to a sidecar file instead of accumulating in memory. + /// + /// This can dramatically reduce memory usage when many writers are open + /// concurrently (e.g. IVF shuffle with thousands of partition writers). + /// The sidecar file is created lazily on the first page write. The caller + /// is responsible for cleaning up `path` (e.g. by placing it in a temp + /// directory that is removed via RAII). + pub fn with_page_metadata_spill(mut self, object_store: Arc, path: Path) -> Self { + self.page_spill = Some(PageSpillState::Pending(object_store, path)); + self + } + + async fn do_write_buffer(writer: &mut (impl AsyncWrite + Unpin), buf: &[u8]) -> Result<()> { + writer.write_all(buf).await?; + let pad_bytes = pad_bytes::(buf.len()); + writer.write_all(&PAD_BUFFER[..pad_bytes]).await?; + Ok(()) + } + + async fn write_page(&mut self, encoded_page: EncodedPage) -> Result<()> { + let buffers = encoded_page.data; + let mut buffer_offsets = Vec::with_capacity(buffers.len()); + let mut buffer_sizes = Vec::with_capacity(buffers.len()); + for buffer in buffers { + buffer_offsets.push(self.writer.tell().await? as u64); + buffer_sizes.push(buffer.len() as u64); + Self::do_write_buffer(&mut self.writer, &buffer).await?; + } + let encoded_encoding = match encoded_page.description { + PageEncoding::Array(array_encoding) => Any::from_msg(&array_encoding)?.encode_to_vec(), + PageEncoding::Structural(page_layout) => Any::from_msg(&page_layout)?.encode_to_vec(), + }; + let page = pbfile::column_metadata::Page { + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(DirectEncoding { + encoding: encoded_encoding, + })), + }), + length: encoded_page.num_rows, + priority: encoded_page.row_number, + }; + let col_idx = encoded_page.column_idx as usize; + if matches!(&self.page_spill, Some(PageSpillState::Pending(..))) { + let Some(PageSpillState::Pending(store, path)) = self.page_spill.take() else { + unreachable!() + }; + self.page_spill = Some(PageSpillState::Active( + PageMetadataSpill::new(store, path, self.num_columns as usize).await?, + )); + } + match &mut self.page_spill { + Some(PageSpillState::Active(spill)) => spill.append_page(col_idx, &page).await?, + None => self.column_metadata[col_idx].pages.push(page), + Some(PageSpillState::Pending(..)) => unreachable!(), + } + Ok(()) + } + + #[instrument(skip_all, level = "debug")] + async fn write_pages(&mut self, mut encoding_tasks: FuturesOrdered) -> Result<()> { + // As soon as an encoding task is done we write it. There is no parallelism + // needed here because "writing" is really just submitting the buffer to the + // underlying write scheduler (either the OS or object_store's scheduler for + // cloud writes). The only time we might truly await on write_page is if the + // scheduler's write queue is full. + // + // Also, there is no point in trying to make write_page parallel anyways + // because we wouldn't want buffers getting mixed up across pages. + while let Some(encoding_task) = encoding_tasks.next().await { + let encoded_page = encoding_task?; + self.write_page(encoded_page).await?; + } + // It's important to flush here, we don't know when the next batch will arrive + // and the underlying cloud store could have writes in progress that won't advance + // until we interact with the writer again. These in-progress writes will time out + // if we don't flush. + self.writer.flush().await?; + Ok(()) + } + + /// Schedule batches of data to be written to the file + pub async fn write_batches( + &mut self, + batches: impl Iterator, + ) -> Result<()> { + for batch in batches { + self.write_batch(batch).await?; + } + Ok(()) + } + + fn verify_field_nullability(arr: &ArrayData, field: &Field) -> Result<()> { + if !field.nullable && arr.null_count() > 0 { + return Err(Error::invalid_input(format!( + "The field `{}` contained null values even though the field is marked non-null in the schema", + field.name + ))); + } + + for (child_field, child_arr) in field.children.iter().zip(arr.child_data()) { + Self::verify_field_nullability(child_arr, child_field)?; + } + + Ok(()) + } + + fn verify_nullability_constraints(&self, batch: &RecordBatch) -> Result<()> { + for (col, field) in batch + .columns() + .iter() + .zip(self.schema.as_ref().unwrap().fields.iter()) + { + Self::verify_field_nullability(&col.to_data(), field)?; + } + Ok(()) + } + + fn initialize(&mut self, mut schema: LanceSchema) -> Result<()> { + let cache_bytes_per_column = if let Some(data_cache_bytes) = self.options.data_cache_bytes { + data_cache_bytes / schema.fields.len() as u64 + } else { + 8 * 1024 * 1024 + }; + + let max_page_bytes = self.options.max_page_bytes.unwrap_or_else(|| { + std::env::var(ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES) + .map(|s| { + s.parse::().unwrap_or_else(|e| { + warn!( + "Failed to parse {}: {}, using default", + ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, e + ); + MAX_PAGE_BYTES as u64 + }) + }) + .unwrap_or(MAX_PAGE_BYTES as u64) + }); + + schema.validate()?; + + let keep_original_array = self.options.keep_original_array.unwrap_or(false); + let encoding_options = EncodingOptions { + cache_bytes_per_column, + max_page_bytes, + keep_original_array, + buffer_alignment: PAGE_BUFFER_ALIGNMENT as u64, + }; + let encoder = + BatchEncoder::try_new(&schema, self.encoding_strategy.as_ref(), &encoding_options)?; + self.num_columns = encoder.num_columns(); + + self.field_rows_written = vec![0; encoder.field_encoders.len()]; + self.column_writers = encoder.field_encoders; + self.column_metadata = vec![initial_column_metadata(); self.num_columns as usize]; + self.field_id_to_column_indices = encoder.field_id_to_column_index; + self.schema_metadata + .extend(std::mem::take(&mut schema.metadata)); + self.schema = Some(schema); + Ok(()) + } + + fn ensure_initialized(&mut self, batch: &RecordBatch) -> Result<&LanceSchema> { + if self.schema.is_none() { + let schema = LanceSchema::try_from(batch.schema().as_ref())?; + self.initialize(schema)?; + } + Ok(self.schema.as_ref().unwrap()) + } + + #[instrument(skip_all, level = "debug")] + fn encode_batch( + &mut self, + batch: &RecordBatch, + external_buffers: &mut OutOfLineBuffers, + ) -> Result>> { + let field_arrays = self + .schema + .as_ref() + .unwrap() + .fields + .iter() + .enumerate() + .map(|(field_idx, field)| { + let array = + batch + .column_by_name(&field.name) + .ok_or(Error::invalid_input_source( + format!( + "Cannot write batch. The batch was missing the column `{}`", + field.name + ) + .into(), + ))?; + Ok((field_idx, array.clone())) + }) + .collect::>>()?; + self.encode_columns(&field_arrays, external_buffers) + } + + // Encode a set of `(field index, array)` pairs, each advancing only its own + // column. Each task captures its field's current row offset at encode time, + // so `advance_columns` must run after this call (never before); the order of + // the returned tasks relative to `write_pages` does not matter. + fn encode_columns( + &mut self, + field_arrays: &[(usize, ArrayRef)], + external_buffers: &mut OutOfLineBuffers, + ) -> Result>> { + // Snapshot the starting row number of each field before borrowing the + // column writers mutably below. + let row_numbers = field_arrays + .iter() + .map(|(field_idx, _)| self.field_rows_written[*field_idx]) + .collect::>(); + field_arrays + .iter() + .zip(row_numbers) + .map(|((field_idx, array), row_number)| { + let repdef = RepDefBuilder::default(); + let num_rows = array.len() as u64; + self.column_writers[*field_idx].maybe_encode( + array.clone(), + external_buffers, + repdef, + row_number, + num_rows, + ) + }) + .collect::>>() + } + + // Advance the per-field row counters after a set of columns has been + // written, keeping `rows_written` (the file's logical length) in sync as the + // longest column. Only the written fields move, so their new totals fold into + // `rows_written` directly without rescanning every field. (`write_batch` + // advances every field uniformly and tracks this inline instead.) + fn advance_columns(&mut self, field_arrays: &[(usize, ArrayRef)]) { + for (field_idx, array) in field_arrays { + let new_total = self.field_rows_written[*field_idx] + array.len() as u64; + self.field_rows_written[*field_idx] = new_total; + self.rows_written = self.rows_written.max(new_total); + } + } + + /// Schedule a batch of data to be written to the file + /// + /// Note: the future returned by this method may complete before the data has been fully + /// flushed to the file (some data may be in the data cache or the I/O cache) + pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> { + debug!( + "write_batch called with {} rows, {} columns, and {} bytes of data", + batch.num_rows(), + batch.num_columns(), + batch.get_array_memory_size() + ); + self.ensure_initialized(batch)?; + self.verify_nullability_constraints(batch)?; + let num_rows = batch.num_rows() as u64; + if num_rows == 0 { + return Ok(()); + } + if num_rows > u32::MAX as u64 { + return Err(Error::invalid_input_source( + "cannot write Lance files with more than 2^32 rows".into(), + )); + } + // First we push each array into its column writer. This may or may not generate enough + // data to trigger an encoding task. We collect any encoding tasks into a queue. + let mut external_buffers = + OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let encoding_tasks = self.encode_batch(batch, &mut external_buffers)?; + // Next, write external buffers + for external_buffer in external_buffers.take_buffers() { + Self::do_write_buffer(&mut self.writer, &external_buffer).await?; + } + + let encoding_tasks = encoding_tasks + .into_iter() + .flatten() + .collect::>(); + + // `write_batch` advances every field by the same amount, so the longest + // column simply grows by `num_rows`. Guard against overflowing the row + // counter. + if self.rows_written.checked_add(num_rows).is_none() { + return Err(Error::invalid_input_source(format!("cannot write batch with {} rows because {} rows have already been written and Lance files cannot contain more than 2^64 rows", num_rows, self.rows_written).into())); + } + for field_rows in self.field_rows_written.iter_mut() { + *field_rows += num_rows; + } + self.rows_written += num_rows; + + self.write_pages(encoding_tasks).await?; + + Ok(()) + } + + /// Write a single column, advancing only that column's row counter. + /// + /// Unlike [`write_batch`](Self::write_batch), which advances every column + /// from a single shared row counter, this method advances one column + /// independently. Used across calls it produces a single file whose columns + /// may have different item counts. + /// + /// `column_index` refers to a top-level field in the writer's schema (the + /// same order as the schema's fields); a nested child cannot be targeted on + /// its own. Because each call writes the whole field from a single array, the + /// children of a struct field always advance together and stay equal-length; + /// only different top-level fields can diverge in length. A column may be + /// written across multiple calls; its values are appended. A field that is + /// never written ends up as a zero-length column. The writer must have been + /// created with an explicit schema (via [`try_new`](Self::try_new)); a lazy + /// schema cannot be inferred here because individual calls need not cover + /// every field. + /// + /// ``` + /// # use arrow_array::{ArrayRef, Int32Array}; + /// # use std::sync::Arc; + /// # use lance_file::writer::FileWriter; + /// # async fn example(writer: &mut FileWriter) -> lance_core::Result<()> { + /// // Field 0 gets three values, field 1 gets one — a non-rectangular file. + /// writer.write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3]))).await?; + /// writer.write_column(1, Arc::new(Int32Array::from(vec![10]))).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> { + let schema = self.schema.as_ref().ok_or_else(|| { + Error::invalid_input_source( + "write_column requires the writer to be created with an explicit schema".into(), + ) + })?; + let field = schema.fields.get(column_index).ok_or_else(|| { + Error::invalid_input_source( + format!( + "write_column: field index {} is out of bounds (schema has {} fields)", + column_index, + schema.fields.len() + ) + .into(), + ) + })?; + if array.len() as u64 > u32::MAX as u64 { + return Err(Error::invalid_input_source( + "cannot write Lance files with more than 2^32 rows".into(), + )); + } + Self::verify_field_nullability(&array.to_data(), field)?; + + // A never-advanced field simply remains a zero-length column, which the + // encoders handle at `finish` time. + if array.is_empty() { + return Ok(()); + } + + let columns = [(column_index, array)]; + let mut external_buffers = + OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let encoding_tasks = self.encode_columns(&columns, &mut external_buffers)?; + for external_buffer in external_buffers.take_buffers() { + Self::do_write_buffer(&mut self.writer, &external_buffer).await?; + } + let encoding_tasks = encoding_tasks + .into_iter() + .flatten() + .collect::>(); + + self.advance_columns(&columns); + self.write_pages(encoding_tasks).await?; + Ok(()) + } + + async fn write_column_metadata( + &mut self, + metadata: pbfile::ColumnMetadata, + ) -> Result<(u64, u64)> { + let metadata_bytes = metadata.encode_to_vec(); + let position = self.writer.tell().await? as u64; + let len = metadata_bytes.len() as u64; + self.writer.write_all(&metadata_bytes).await?; + Ok((position, len)) + } + + async fn write_column_metadatas(&mut self) -> Result> { + let metadatas = std::mem::take(&mut self.column_metadata); + + // If spilling, finalize the spill writer and reopen for reading. + // The spill file itself is cleaned up by the caller (it lives in a + // temp directory managed by the caller's RAII guard). + let spill_state = self.page_spill.take(); + let (spill_chunks, spill_reader) = + if let Some(PageSpillState::Active(mut spill)) = spill_state { + spill.shutdown_writer().await?; + let reader = spill.object_store.open(&spill.path).await?; + let chunks = std::mem::take(&mut spill.column_chunks); + (chunks, Some(reader)) + } else { + (Vec::new(), None) + }; + + let mut metadata_positions = Vec::with_capacity(metadatas.len()); + for (col_idx, mut metadata) in metadatas.into_iter().enumerate() { + if let Some(reader) = &spill_reader { + let mut pages = Vec::new(); + for &(offset, len) in &spill_chunks[col_idx] { + let data = reader + .get_range(offset as usize..(offset as usize + len as usize)) + .await + .map_err(|e| Error::io_source(Box::new(e)))?; + pages.extend(decode_spilled_chunk(&data)?); + } + metadata.pages = pages; + } + metadata_positions.push(self.write_column_metadata(metadata).await?); + } + + Ok(metadata_positions) + } + + fn make_file_descriptor( + schema: &lance_core::datatypes::Schema, + num_rows: u64, + ) -> Result { + let fields_with_meta = FieldsWithMeta::from(schema); + Ok(pb::FileDescriptor { + schema: Some(pb::Schema { + fields: fields_with_meta.fields.0, + metadata: fields_with_meta.metadata, + }), + length: num_rows, + }) + } + + async fn write_global_buffers(&mut self) -> Result> { + let schema = self.schema.as_mut().ok_or(Error::invalid_input("No schema provided on writer open and no data provided. Schema is unknown and file cannot be created"))?; + schema.metadata = std::mem::take(&mut self.schema_metadata); + // Use descriptor layout for blob v2 fields in the footer to avoid exposing logical child fields. + schema + .fields + .iter_mut() + .for_each(|f| f.unload_blobs_recursive()); + + let file_descriptor = Self::make_file_descriptor(schema, self.rows_written)?; + let file_descriptor_bytes = file_descriptor.encode_to_vec(); + let file_descriptor_len = file_descriptor_bytes.len() as u64; + let file_descriptor_position = self.writer.tell().await? as u64; + self.writer.write_all(&file_descriptor_bytes).await?; + let mut gbo_table = Vec::with_capacity(1 + self.global_buffers.len()); + gbo_table.push((file_descriptor_position, file_descriptor_len)); + gbo_table.append(&mut self.global_buffers); + Ok(gbo_table) + } + + /// Add a metadata entry to the schema + /// + /// This method is useful because sometimes the metadata is not known until after the + /// data has been written. This method allows you to alter the schema metadata. It + /// must be called before `finish` is called. + pub fn add_schema_metadata(&mut self, key: impl Into, value: impl Into) { + self.schema_metadata.insert(key.into(), value.into()); + } + + /// Prepare the writer when column data and metadata were produced externally. + /// + /// This is useful for flows that copy already-encoded pages (e.g., binary copy + /// during compaction) where the column buffers have been written directly and we + /// only need to write the footer and schema metadata. The provided + /// `column_metadata` must describe the buffers already persisted by the + /// underlying `ObjectWriter`, and `rows_written` should reflect the total number + /// of rows in those buffers. + pub fn initialize_with_external_metadata( + &mut self, + schema: lance_core::datatypes::Schema, + column_metadata: Vec, + rows_written: u64, + ) { + self.schema = Some(schema); + self.num_columns = column_metadata.len() as u32; + self.column_metadata = column_metadata; + self.rows_written = rows_written; + } + + /// Adds a global buffer to the file + /// + /// The global buffer can contain any arbitrary bytes. It will be written to the disk + /// immediately. This method returns the index of the global buffer (this will always + /// start at 1 and increment by 1 each time this method is called) + pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result { + let position = self.writer.tell().await? as u64; + let len = buffer.len() as u64; + Self::do_write_buffer(&mut self.writer, &buffer).await?; + self.global_buffers.push((position, len)); + Ok(self.global_buffers.len() as u32) + } + + async fn finish_writers(&mut self) -> Result<()> { + let mut col_idx = 0; + for mut writer in std::mem::take(&mut self.column_writers) { + let mut external_buffers = + OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let columns = writer.finish(&mut external_buffers).await?; + for buffer in external_buffers.take_buffers() { + self.writer.write_all(&buffer).await?; + } + debug_assert_eq!( + columns.len(), + writer.num_columns() as usize, + "Expected {} columns from column at index {} and got {}", + writer.num_columns(), + col_idx, + columns.len() + ); + for column in columns { + for page in column.final_pages { + self.write_page(page).await?; + } + let column_metadata = &mut self.column_metadata[col_idx]; + let mut buffer_pos = self.writer.tell().await? as u64; + for buffer in column.column_buffers { + column_metadata.buffer_offsets.push(buffer_pos); + let mut size = 0; + Self::do_write_buffer(&mut self.writer, &buffer).await?; + size += buffer.len() as u64; + buffer_pos += size; + column_metadata.buffer_sizes.push(size); + } + let encoded_encoding = Any::from_msg(&column.encoding)?.encode_to_vec(); + column_metadata.encoding = Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { + encoding: encoded_encoding, + })), + }); + col_idx += 1; + } + } + if col_idx != self.column_metadata.len() { + panic!( + "Column writers finished with {} columns but we expected {}", + col_idx, + self.column_metadata.len() + ); + } + Ok(()) + } + + /// Finishes writing the file + /// + /// This method will wait until all data has been flushed to the file. Then it + /// will write the file metadata and the footer. It will not return until all + /// data has been flushed and the file has been closed. + /// + /// Returns a summary of the completed file write. + pub async fn finish(&mut self) -> Result { + // 1. flush any remaining data and write out those pages + let mut external_buffers = + OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let encoding_tasks = self + .column_writers + .iter_mut() + .map(|writer| writer.flush(&mut external_buffers)) + .collect::>>()?; + for external_buffer in external_buffers.take_buffers() { + Self::do_write_buffer(&mut self.writer, &external_buffer).await?; + } + let encoding_tasks = encoding_tasks + .into_iter() + .flatten() + .collect::>(); + self.write_pages(encoding_tasks).await?; + + if !self.column_writers.is_empty() { + self.finish_writers().await?; + } + + // 3. write global buffers (we write the schema here) + let global_buffer_offsets = self.write_global_buffers().await?; + let num_global_buffers = global_buffer_offsets.len() as u32; + + // 4. write the column metadatas + let column_metadata_start = self.writer.tell().await? as u64; + let metadata_positions = self.write_column_metadatas().await?; + + // 5. write the column metadata offset table + let cmo_table_start = self.writer.tell().await? as u64; + for (meta_pos, meta_len) in metadata_positions { + self.writer.write_u64_le(meta_pos).await?; + self.writer.write_u64_le(meta_len).await?; + } + + // 6. write global buffers offset table + let gbo_table_start = self.writer.tell().await? as u64; + for (gbo_pos, gbo_len) in global_buffer_offsets { + self.writer.write_u64_le(gbo_pos).await?; + self.writer.write_u64_le(gbo_len).await?; + } + + // 7. write the footer + self.writer.write_u64_le(column_metadata_start).await?; + self.writer.write_u64_le(cmo_table_start).await?; + self.writer.write_u64_le(gbo_table_start).await?; + self.writer.write_u32_le(num_global_buffers).await?; + self.writer.write_u32_le(self.num_columns).await?; + self.writer.write_u16_le(0).await?; + self.writer.write_u16_le(3).await?; + self.writer.write_all(MAGIC).await?; + + // 7. close the writer + let write_result = ObjectWriter::shutdown(self.writer.as_mut()).await?; + + Ok(FileWriteSummary { + num_rows: self.rows_written, + size_bytes: write_result.size as u64, + }) + } + + pub async fn abort(&mut self) { + // For multipart uploads, ObjectWriter's Drop impl will abort + // the upload when the writer is dropped. + } + + pub async fn tell(&mut self) -> Result { + Ok(self.writer.tell().await? as u64) + } + + /// Append a buffer whose metadata is supplied by the caller. + pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> { + let start = self.tell().await?; + self.writer.write_all(bytes).await?; + Ok((start, bytes.len() as u64)) + } + + pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] { + &self.field_id_to_column_indices + } +} + +// Creates a lance footer and appends it to the encoded data +// +// The logic here is very similar to logic in the FileWriter except we +// are using BufMut (put_xyz) instead of AsyncWrite (write_xyz). +pub fn concat_lance_footer(batch: &EncodedBatch, write_schema: bool) -> Result { + // Estimating 1MiB for file footer + let mut data = BytesMut::with_capacity(batch.data.len() + 1024 * 1024); + data.put(batch.data.clone()); + // write global buffers (we write the schema here) + let global_buffers = if write_schema { + let schema_start = data.len() as u64; + let lance_schema = lance_core::datatypes::Schema::try_from(batch.schema.as_ref())?; + let descriptor = Writer::make_file_descriptor(&lance_schema, batch.num_rows)?; + let descriptor_bytes = descriptor.encode_to_vec(); + let descriptor_len = descriptor_bytes.len() as u64; + data.put(descriptor_bytes.as_slice()); + + vec![(schema_start, descriptor_len)] + } else { + vec![] + }; + let col_metadata_start = data.len() as u64; + + let mut col_metadata_positions = Vec::new(); + // Write column metadata + for col in &batch.page_table { + let position = data.len() as u64; + let pages = col + .page_infos + .iter() + .map(|page_info| { + let encoded_encoding = match &page_info.encoding { + PageEncoding::Array(array_encoding) => { + Any::from_msg(array_encoding)?.encode_to_vec() + } + PageEncoding::Structural(page_layout) => { + Any::from_msg(page_layout)?.encode_to_vec() + } + }; + let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = page_info + .buffer_offsets_and_sizes + .as_ref() + .iter() + .cloned() + .unzip(); + Ok(pbfile::column_metadata::Page { + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(DirectEncoding { + encoding: encoded_encoding, + })), + }), + length: page_info.num_rows, + priority: page_info.priority, + }) + }) + .collect::>>()?; + let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = + col.buffer_offsets_and_sizes.iter().cloned().unzip(); + let encoded_col_encoding = Any::from_msg(&col.encoding)?.encode_to_vec(); + let column = pbfile::ColumnMetadata { + pages, + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { + encoding: encoded_col_encoding, + })), + }), + }; + let column_bytes = column.encode_to_vec(); + col_metadata_positions.push((position, column_bytes.len() as u64)); + data.put(column_bytes.as_slice()); + } + // Write column metadata offsets table + let cmo_table_start = data.len() as u64; + for (meta_pos, meta_len) in col_metadata_positions { + data.put_u64_le(meta_pos); + data.put_u64_le(meta_len); + } + // Write global buffers offsets table + let gbo_table_start = data.len() as u64; + let num_global_buffers = global_buffers.len() as u32; + for (gbo_pos, gbo_len) in global_buffers { + data.put_u64_le(gbo_pos); + data.put_u64_le(gbo_len); + } + + // write the footer + data.put_u64_le(col_metadata_start); + data.put_u64_le(cmo_table_start); + data.put_u64_le(gbo_table_start); + data.put_u32_le(num_global_buffers); + data.put_u32_le(batch.page_table.len() as u32); + data.put_u16_le(2); + data.put_u16_le(0); + data.put(MAGIC.as_slice()); + + Ok(data.freeze()) +} diff --git a/rust/lance-file/src/versions/v2_1/compression.rs b/rust/lance-file/src/versions/v2_1/compression.rs new file mode 100644 index 00000000000..051e131e9a9 --- /dev/null +++ b/rust/lance-file/src/versions/v2_1/compression.rs @@ -0,0 +1,127 @@ +use lance_core::{Error, Result, datatypes::Field}; +use lance_encoding::{ + compression::{ + BlockCompressor, CompressionStrategy, field_metadata_params, finalize_miniblock_compressor, + reject_packed_struct_per_value, try_bitpacking_block, try_bitpacking_miniblock, + try_byte_stream_split_miniblock, try_fixed_packed_struct_miniblock, + try_fixed_u8_rle_miniblock, try_raw_block, try_raw_fixed_size_list_miniblock, + try_raw_fixed_width_miniblock, try_raw_per_value, try_uncompressed_fixed_width_miniblock, + try_variable_width_miniblock, try_variable_width_per_value, + }, + compression_config::{CompressionFieldParams, CompressionParams}, + data::DataBlock, + encodings::logical::primitive::{fullzip::PerValueCompressor, miniblock::MiniBlockCompressor}, + format::pb21::CompressiveEncoding, +}; + +#[derive(Debug, Clone)] +pub(super) struct Strategy { + params: CompressionParams, +} + +impl Strategy { + pub(super) fn new(params: CompressionParams) -> Self { + Self { params } + } + + fn field_params(&self, field: &Field) -> CompressionFieldParams { + let mut params = self + .params + .get_field_params(&field.name, &field.data_type()); + let mut metadata = field_metadata_params(field); + if metadata + .minichunk_size + .is_some_and(|size| size >= 32 * 1024) + { + log::warn!( + "minichunk_size '{}' is too large for the selected u16 miniblock layout, using default", + metadata.minichunk_size.unwrap() + ); + metadata.minichunk_size = None; + } + params.merge(&metadata); + params + } +} + +impl CompressionStrategy for Strategy { + fn create_miniblock_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + let compressor = + if let Some(compressor) = try_uncompressed_fixed_width_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_byte_stream_split_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_fixed_u8_rle_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_bitpacking_miniblock(data) { + compressor + } else if let Some(compressor) = try_raw_fixed_width_miniblock(data) { + compressor + } else if let Some(compressor) = try_variable_width_miniblock(field, data, ¶ms)? { + compressor + } else if let Some(compressor) = try_fixed_packed_struct_miniblock(data)? { + compressor + } else if let Some(compressor) = try_raw_fixed_size_list_miniblock(data) { + compressor + } else { + return Err(Error::not_supported_source( + format!( + "Mini-block compression not yet supported for block type {}", + data.name() + ) + .into(), + )); + }; + finalize_miniblock_compressor(data, compressor, ¶ms) + } + + fn create_per_value( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + if let Some(compressor) = try_raw_per_value(data) { + return Ok(compressor); + } + if let Some(compressor) = reject_packed_struct_per_value(field, data)? { + return Ok(compressor); + } + if let Some(compressor) = try_variable_width_per_value(field, data, ¶ms)? { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Per-value compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } + + fn create_block_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result<(Box, CompressiveEncoding)> { + let _params = self.field_params(field); + if let Some(compressor) = try_bitpacking_block(data) { + return Ok(compressor); + } + if let Some(compressor) = try_raw_block(data) { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Block compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } +} diff --git a/rust/lance-file/src/versions/v2_1/mod.rs b/rust/lance-file/src/versions/v2_1/mod.rs new file mode 100644 index 00000000000..63caea79165 --- /dev/null +++ b/rust/lance-file/src/versions/v2_1/mod.rs @@ -0,0 +1,177 @@ +//! Lance v2.1 file composition. + +use std::{collections::BTreeMap, sync::Arc}; + +use bytes::Bytes; +use lance_core::{ + Error, Result, + datatypes::{Field, Schema}, +}; +use lance_encoding::{ + compression_config::CompressionParams, + encoder::{ + ColumnIndexSequence, EncodedBatch, FieldEncoder, FieldEncodingContext, + FieldEncodingStrategy, + structural::{ + PrimitiveFieldEncoding, PrimitivePageEncoding, try_create_binary_blob, try_create_list, + try_create_struct, + }, + }, +}; +use lance_io::traits::Writer as ObjectWriter; + +use crate::{ + reader::{ReadProjection, structural}, + writer::FileWriterOptions, +}; + +mod compression; +mod reader; +mod writer; + +pub use reader::{ + projection_from_column_names, projection_from_field_ids, projection_from_whole_schema, +}; +pub use writer::Writer; + +#[cfg(test)] +pub(crate) use reader::test_projection_length; +pub(crate) use reader::{ + decode_column_metadata, finish_metadata, finish_metadata_index, validate_global_buffers, +}; + +pub(crate) fn read_projection() -> Arc { + structural::read_projection(reader::decode_column) +} + +/// Count physical columns represented by a field in a v2.1 footer. +pub fn physical_column_count(field: &Field) -> usize { + structural::physical_column_count(field) +} + +/// Build persisted field-to-column entries for a v2.1 data file. +pub fn data_file_columns(schema: &Schema) -> (Vec, Vec) { + structural::data_file_columns(schema) +} + +pub(super) fn field_id_to_column_index(schema: &Schema) -> BTreeMap { + structural::field_id_to_column_index(schema) +} + +#[derive(Debug)] +struct FieldStrategy { + primitive: PrimitiveFieldEncoding, +} + +impl FieldEncodingStrategy for FieldStrategy { + fn create_field_encoder( + &self, + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, + ) -> Result> { + if let Some(encoder) = + try_create_binary_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if field.is_blob() { + return Err(Error::invalid_input_source( + format!( + "Blob encoding is not available for field '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )); + } + if let Some(encoder) = self.primitive.try_create(field, column_index, context)? { + return Ok(encoder); + } + if matches!( + field.data_type(), + arrow_schema::DataType::FixedSizeList(item, _) + if matches!(item.data_type(), arrow_schema::DataType::Struct(_)) + ) { + return Err(Error::not_supported_source( + "FixedSizeList is not enabled by the selected file format".into(), + )); + } + if matches!(field.data_type(), arrow_schema::DataType::Map(_, _)) { + return Err(Error::not_supported_source( + "Map data type is not enabled by the selected file format".into(), + )); + } + if let Some(encoder) = try_create_list(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_struct(field, column_index, context)? { + return Ok(encoder); + } + Err(Error::not_supported_source( + format!( + "Lance v2.1 has no field encoding for '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )) + } +} + +/// Compose the v2.1 field encoding mechanisms. +pub fn encoding_strategy(params: CompressionParams) -> Arc { + let compression = Arc::new(compression::Strategy::new(params)); + Arc::new(FieldStrategy { + primitive: PrimitiveFieldEncoding::new([ + PrimitivePageEncoding::reject_sparse(), + PrimitivePageEncoding::dense_u16(compression), + ]), + }) +} + +/// Create a v2.1 writer with an explicit schema. +pub fn create_writer( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, +) -> Result { + Writer::try_new(object_writer, schema, options) +} + +/// Create a v2.1 writer with explicit compression tuning. +pub fn create_writer_with_compression( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + compression: CompressionParams, +) -> Result { + Writer::try_new_with_compression(object_writer, schema, options, compression) +} + +/// Create a v2.1 writer whose schema is inferred from the first batch. +pub fn create_lazy_writer( + object_writer: Box, + options: FileWriterOptions, +) -> Writer { + Writer::new_lazy(object_writer, options) +} + +/// Create a lazy v2.1 writer with explicit compression tuning. +pub fn create_lazy_writer_with_compression( + object_writer: Box, + options: FileWriterOptions, + compression: CompressionParams, +) -> Writer { + Writer::new_lazy_with_compression(object_writer, options, compression) +} + +/// Encode a self-described v2.1 batch. +pub fn encode_self_described_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, true) +} + +/// Encode a mini-lance v2.1 batch. +pub fn encode_mini_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, false) +} diff --git a/rust/lance-file/src/versions/v2_1/reader.rs b/rust/lance-file/src/versions/v2_1/reader.rs new file mode 100644 index 00000000000..0c11e5f8976 --- /dev/null +++ b/rust/lance-file/src/versions/v2_1/reader.rs @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::{collections::BTreeMap, sync::Arc}; + +use lance_core::{Error, Result, datatypes::Schema}; +use lance_encoding::{decoder::ColumnInfo, format::pb21}; + +use crate::{ + format::pbfile, + reader::{ + BufferDescriptor, CachedFileMetadata, FileMetadataIndex, RawFileMetadata, ReaderProjection, + structural, + }, + version::ConcreteFileVersion, +}; + +fn required<'a, T>(value: Option<&'a T>, label: &str) -> Result<&'a T> { + value.ok_or_else(|| { + Error::invalid_input_source( + format!("Lance v2.1 {label} is missing its nested encoding").into(), + ) + }) +} + +fn validate_compressive_encoding(encoding: &pb21::CompressiveEncoding) -> Result<()> { + use pb21::compressive_encoding::Compression; + + match encoding.compression.as_ref() { + Some(Compression::Flat(_)) + | Some(Compression::InlineBitpacking(_)) + | Some(Compression::Constant(_)) => Ok(()), + Some(Compression::Variable(variable)) => validate_compressive_encoding(required( + variable.offsets.as_deref(), + "variable offsets", + )?), + Some(Compression::OutOfLineBitpacking(bitpacking)) => { + validate_compressive_encoding(required( + bitpacking.values.as_deref(), + "out-of-line bitpacking values", + )?) + } + Some(Compression::Fsst(fsst)) => { + validate_compressive_encoding(required(fsst.values.as_deref(), "FSST values")?) + } + Some(Compression::Dictionary(dictionary)) => { + validate_compressive_encoding(required( + dictionary.indices.as_deref(), + "dictionary indices", + )?)?; + validate_compressive_encoding(required( + dictionary.items.as_deref(), + "dictionary items", + )?) + } + Some(Compression::Rle(rle)) => { + let values = required(rle.values.as_deref(), "RLE values")?; + let run_lengths = required(rle.run_lengths.as_deref(), "RLE run lengths")?; + let fixed_values = matches!( + values.compression.as_ref(), + Some(Compression::Flat(flat)) + if matches!(flat.bits_per_value, 8 | 16 | 32 | 64) + && flat.data.is_none() + ); + let fixed_u8_lengths = matches!( + run_lengths.compression.as_ref(), + Some(Compression::Flat(flat)) + if flat.bits_per_value == 8 && flat.data.is_none() + ); + if !fixed_values || !fixed_u8_lengths { + return Err(Error::invalid_input_source( + "Lance v2.1 RLE requires flat values and flat u8 run lengths".into(), + )); + } + Ok(()) + } + Some(Compression::ByteStreamSplit(split)) => validate_compressive_encoding(required( + split.values.as_deref(), + "byte-stream-split values", + )?), + Some(Compression::General(general)) => validate_compressive_encoding(required( + general.values.as_deref(), + "general-compression values", + )?), + Some(Compression::FixedSizeList(list)) => validate_compressive_encoding(required( + list.values.as_deref(), + "fixed-size-list values", + )?), + Some(Compression::PackedStruct(packed)) => validate_compressive_encoding(required( + packed.values.as_deref(), + "packed-struct values", + )?), + Some(Compression::VariablePackedStruct(_)) => Err(Error::invalid_input_source( + "Variable packed struct compression is not part of the Lance v2.1 grammar".into(), + )), + None => Err(Error::invalid_input_source( + "Lance v2.1 compressive encoding is missing its compression variant".into(), + )), + } +} + +fn validate_page_layout(layout: &pb21::PageLayout) -> Result<()> { + use pb21::page_layout::Layout; + + match layout.layout.as_ref() { + Some(Layout::MiniBlockLayout(miniblock)) => { + if miniblock.has_large_chunk { + return Err(Error::invalid_input_source( + "Large miniblock chunks are not part of the Lance v2.1 grammar".into(), + )); + } + if let Some(rep) = miniblock.rep_compression.as_ref() { + validate_compressive_encoding(rep)?; + } + if let Some(def) = miniblock.def_compression.as_ref() { + validate_compressive_encoding(def)?; + } + validate_compressive_encoding(required( + miniblock.value_compression.as_ref(), + "miniblock values", + )?)?; + if let Some(dictionary) = miniblock.dictionary.as_ref() { + validate_compressive_encoding(dictionary)?; + } + Ok(()) + } + Some(Layout::FullZipLayout(fullzip)) => validate_compressive_encoding(required( + fullzip.value_compression.as_ref(), + "full-zip values", + )?), + Some(Layout::ConstantLayout(constant)) => { + if constant.inline_value.is_some() { + Err(Error::invalid_input_source( + "Lance v2.1 only accepts the all-null form of constant page layout".into(), + )) + } else if constant.rep_compression.is_some() || constant.def_compression.is_some() { + Err(Error::invalid_input_source( + "Compressed constant-page levels are not part of the Lance v2.1 grammar".into(), + )) + } else { + Ok(()) + } + } + Some(Layout::BlobLayout(blob)) => { + let inner = blob.inner_layout.as_deref().ok_or_else(|| { + Error::invalid_input_source( + "Lance v2.1 blob page layout is missing its inner layout".into(), + ) + })?; + validate_page_layout(inner) + } + Some(Layout::SparseLayout(_)) => Err(Error::invalid_input_source( + "Sparse page layout is not part of the Lance v2.1 grammar".into(), + )), + None => Err(Error::invalid_input_source( + "Lance v2.1 page is missing its page layout".into(), + )), + } +} + +pub fn decode_column( + column_index: u32, + metadata: &pbfile::ColumnMetadata, +) -> Result> { + let page_infos = metadata + .pages + .iter() + .enumerate() + .map(|(page_index, page)| { + let page_layout = structural::decode_page_layout(column_index, page_index, page)?; + validate_page_layout(&page_layout)?; + structural::build_page_info(column_index, page_index, page, page_layout) + }) + .collect::>>()?; + structural::build_column_info(column_index, metadata, page_infos) +} + +pub fn decode_column_metadata( + column_metadatas: &[pbfile::ColumnMetadata], +) -> Result>> { + column_metadatas + .iter() + .enumerate() + .map(|(column_index, metadata)| { + let column_index = u32::try_from(column_index).map_err(|_| { + Error::invalid_input_source("File has more than u32::MAX columns".into()) + })?; + decode_column(column_index, metadata) + }) + .collect() +} + +pub fn projection_from_field_ids( + schema: &Schema, + field_id_to_column_index: &BTreeMap, +) -> ReaderProjection { + structural::projection_from_field_ids(schema, field_id_to_column_index) +} + +pub fn projection_from_whole_schema(schema: &Schema) -> ReaderProjection { + structural::projection_from_field_ids(schema, &super::field_id_to_column_index(schema)) +} + +pub fn projection_from_column_names( + schema: &Schema, + column_names: &[&str], +) -> Result { + structural::projection_from_column_names( + schema, + column_names, + &super::field_id_to_column_index(schema), + ) +} + +pub fn finish_metadata(raw: RawFileMetadata) -> Result { + if (raw.footer.major_version, raw.footer.minor_version) != (2, 1) { + return Err(Error::version_conflict( + "Attempt to use the Lance v2.1 reader for a different file version".to_string(), + raw.footer.major_version, + raw.footer.minor_version, + )); + } + validate_global_buffers(&raw.file_buffers)?; + let column_infos = decode_column_metadata(&raw.column_metadatas)?; + Ok(CachedFileMetadata { + file_schema: raw.file_schema, + column_metadatas: raw.column_metadatas, + column_infos, + num_rows: raw.num_rows, + file_buffers: raw.file_buffers, + num_data_bytes: raw.num_data_bytes, + num_column_metadata_bytes: raw.num_column_metadata_bytes, + num_global_buffer_bytes: raw.num_global_buffer_bytes, + num_footer_bytes: raw.num_footer_bytes, + major_version: raw.footer.major_version, + minor_version: raw.footer.minor_version, + version: ConcreteFileVersion::V2_1, + file_size_bytes: raw.file_size_bytes, + retained_global_buffers: raw.retained_global_buffers, + }) +} + +pub fn validate_global_buffers(buffers: &[BufferDescriptor]) -> Result<()> { + structural::validate_global_buffers(buffers) +} + +pub fn finish_metadata_index(index: FileMetadataIndex) -> Result { + if index.version != ConcreteFileVersion::V2_1 { + let (major, minor) = index.version.to_standard_footer_numbers(); + return Err(Error::version_conflict( + "Attempt to use the Lance v2.1 reader for a different metadata index".to_string(), + major, + minor, + )); + } + validate_global_buffers(&index.file_buffers)?; + Ok(index) +} + +#[cfg(test)] +pub fn test_projection_length( + schema: &Schema, + column_indices: &[u32], + column_lengths: &[u64], +) -> Result { + structural::test_projection_length(schema, column_indices, column_lengths) +} + +#[cfg(test)] +mod grammar_tests { + use super::*; + use pb21::{ + CompressiveEncoding, FullZipLayout, General, PageLayout, VariablePackedStruct, + compressive_encoding::Compression, page_layout::Layout, + }; + + #[test] + fn rejects_nested_variable_packed_struct() { + let variable_packed = CompressiveEncoding { + compression: Some(Compression::VariablePackedStruct(VariablePackedStruct { + fields: Vec::new(), + })), + }; + let wrapped = CompressiveEncoding { + compression: Some(Compression::General(Box::new(General { + compression: None, + values: Some(Box::new(variable_packed)), + }))), + }; + let layout = PageLayout { + layout: Some(Layout::FullZipLayout(FullZipLayout { + value_compression: Some(wrapped), + ..Default::default() + })), + }; + + let error = validate_page_layout(&layout).unwrap_err(); + assert!( + error + .to_string() + .contains("Variable packed struct compression is not part") + ); + } +} diff --git a/rust/lance-file/src/versions/v2_1/writer.rs b/rust/lance-file/src/versions/v2_1/writer.rs new file mode 100644 index 00000000000..1599a9fd6e1 --- /dev/null +++ b/rust/lance-file/src/versions/v2_1/writer.rs @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::sync::Arc; + +use arrow_array::{ArrayRef, RecordBatch}; +use bytes::{BufMut, Bytes}; +use lance_core::{Result, datatypes::Schema}; +use lance_encoding::{ + compression_config::CompressionParams, + encoder::{BatchEncoder, EncodedBatch}, +}; +use lance_io::{object_store::ObjectStore, traits::Writer as ObjectWriter}; +use object_store::path::Path; +use tokio::io::AsyncWriteExt; + +use crate::{ + format::{MAGIC, pbfile}, + writer::{ + FileWriteSummary, FileWriterOptions, + structural::{EncodedBatchBody, EncodingPipeline, StructuralFileSink, encode_batch_body}, + }, +}; + +use super::encoding_strategy; + +/// A writer for the Lance v2.1 file grammar. +/// +/// The concrete writer owns the v2.1 encoding composition, finish ordering, +/// and exact footer identity. Shared components only execute the structural +/// encoding and I/O mechanisms selected here. +pub struct Writer { + sink: StructuralFileSink, + encoding: EncodingPipeline, + compression: CompressionParams, +} + +impl Writer { + /// Create a v2.1 writer with an explicit schema. + pub fn try_new( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + ) -> Result { + Self::try_new_with_compression(object_writer, schema, options, Default::default()) + } + + /// Create a v2.1 writer with explicit compression tuning. + pub fn try_new_with_compression( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + compression: CompressionParams, + ) -> Result { + let mut writer = Self::new_lazy_with_compression(object_writer, options, compression); + writer.initialize(schema)?; + Ok(writer) + } + + /// Create a v2.1 writer whose schema is inferred from the first batch. + pub fn new_lazy(object_writer: Box, options: FileWriterOptions) -> Self { + Self::new_lazy_with_compression(object_writer, options, Default::default()) + } + + /// Create a lazy v2.1 writer with explicit compression tuning. + pub fn new_lazy_with_compression( + object_writer: Box, + options: FileWriterOptions, + compression: CompressionParams, + ) -> Self { + Self { + sink: StructuralFileSink::new(object_writer), + encoding: EncodingPipeline::new(options), + compression, + } + } + + fn initialize(&mut self, schema: Schema) -> Result<()> { + let encoding_options = self.encoding.encoding_options(&schema); + schema.validate()?; + let strategy = encoding_strategy(self.compression.clone()); + let encoder = BatchEncoder::try_new(&schema, strategy.as_ref(), &encoding_options)?; + self.encoding.initialize(schema, encoder, &mut self.sink); + Ok(()) + } + + fn ensure_initialized(&mut self, batch: &RecordBatch) -> Result<()> { + if !self.encoding.is_initialized() { + self.initialize(Schema::try_from(batch.schema().as_ref())?)?; + } + Ok(()) + } + + /// Spill page metadata to a sidecar file instead of retaining it in memory. + pub fn with_page_metadata_spill(mut self, object_store: Arc, path: Path) -> Self { + self.sink.with_page_metadata_spill(object_store, path); + self + } + + /// Schedule batches to be written in iteration order. + pub async fn write_batches( + &mut self, + batches: impl Iterator, + ) -> Result<()> { + for batch in batches { + self.write_batch(batch).await?; + } + Ok(()) + } + + /// Schedule one record batch for writing. + pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> { + self.ensure_initialized(batch)?; + self.encoding.write_batch(batch, &mut self.sink).await + } + + /// Write one top-level field, advancing only that field's row count. + pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> { + self.encoding + .write_column(column_index, array, &mut self.sink) + .await + } + + /// Append a buffer whose page or column metadata is supplied externally. + pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> { + self.sink.write_external_buffer(bytes).await + } + + /// Add an entry to the schema metadata written in the file descriptor. + pub fn add_schema_metadata(&mut self, key: impl Into, value: impl Into) { + self.encoding.add_schema_metadata(key, value); + } + + /// Prepare the writer for encoded column data produced externally. + pub fn initialize_with_external_metadata( + &mut self, + schema: Schema, + column_metadata: Vec, + rows_written: u64, + ) { + self.encoding + .initialize_with_external_metadata(schema, rows_written); + self.sink.initialize_with_external_metadata(column_metadata); + } + + /// Add an arbitrary global buffer and return its one-based index. + pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result { + self.sink.add_global_buffer(buffer).await + } + + /// Finish the v2.1 file and close its object writer. + pub async fn finish(&mut self) -> Result { + // The order below is the v2.1 wire contract. + self.encoding.flush(&mut self.sink).await?; + self.encoding.finish_encoders(&mut self.sink).await?; + + let descriptor = self.encoding.make_file_descriptor()?; + let global_buffer_offsets = self.sink.write_global_buffers(descriptor).await?; + let num_global_buffers = global_buffer_offsets.len() as u32; + + let column_metadata_start = self.sink.tell().await?; + let column_metadata_offsets = self.sink.write_column_metadatas().await?; + let column_metadata_offsets_start = self + .sink + .write_offset_table(&column_metadata_offsets) + .await?; + let global_buffer_offsets_start = + self.sink.write_offset_table(&global_buffer_offsets).await?; + let num_columns = self.sink.num_columns(); + + let output = self.sink.output_mut(); + output.write_u64_le(column_metadata_start).await?; + output.write_u64_le(column_metadata_offsets_start).await?; + output.write_u64_le(global_buffer_offsets_start).await?; + output.write_u32_le(num_global_buffers).await?; + output.write_u32_le(num_columns).await?; + output.write_u16_le(2).await?; + output.write_u16_le(1).await?; + output.write_all(MAGIC).await?; + + Ok(FileWriteSummary { + num_rows: self.encoding.rows_written(), + size_bytes: self.sink.shutdown().await?, + }) + } + + /// Abandon this write. + pub async fn abort(&mut self) { + // Dropping a multipart ObjectWriter aborts the upload. + } + + /// Return the current object-writer position. + pub async fn tell(&mut self) -> Result { + self.sink.tell().await + } + + /// Return the field-id to physical-column mapping selected by v2.1. + pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] { + self.encoding.field_id_to_column_indices() + } +} + +/// Append a self-described or mini-lance v2.1 footer to an encoded batch. +pub fn concat_lance_footer(batch: &EncodedBatch, write_schema: bool) -> Result { + let EncodedBatchBody { + mut data, + column_metadata_start, + column_metadata_offsets_start, + global_buffer_offsets_start, + num_global_buffers, + num_columns, + } = encode_batch_body(batch, write_schema)?; + + data.put_u64_le(column_metadata_start); + data.put_u64_le(column_metadata_offsets_start); + data.put_u64_le(global_buffer_offsets_start); + data.put_u32_le(num_global_buffers); + data.put_u32_le(num_columns); + data.put_u16_le(2); + data.put_u16_le(1); + data.extend_from_slice(MAGIC); + Ok(data.freeze()) +} diff --git a/rust/lance-file/src/versions/v2_2/compression.rs b/rust/lance-file/src/versions/v2_2/compression.rs new file mode 100644 index 00000000000..a374adc4bec --- /dev/null +++ b/rust/lance-file/src/versions/v2_2/compression.rs @@ -0,0 +1,127 @@ +use std::sync::Arc; + +use lance_core::{Error, Result, datatypes::Field}; +use lance_encoding::{ + compression::{ + BlockCompressor, CompressionStrategy, field_metadata_params, finalize_miniblock_compressor, + try_bitpacking_block, try_bitpacking_miniblock, try_byte_stream_split_miniblock, + try_fixed_packed_struct_miniblock, try_fixed_u8_rle_block, try_fixed_u8_rle_miniblock, + try_general_block, try_raw_block, try_raw_fixed_size_list_miniblock, + try_raw_fixed_width_miniblock, try_raw_per_value, try_uncompressed_fixed_width_miniblock, + try_variable_packed_struct_per_value, try_variable_width_miniblock, + try_variable_width_per_value, + }, + compression_config::{CompressionFieldParams, CompressionParams}, + data::DataBlock, + encodings::logical::primitive::{fullzip::PerValueCompressor, miniblock::MiniBlockCompressor}, + format::pb21::CompressiveEncoding, +}; + +#[derive(Debug, Clone)] +pub(super) struct Strategy { + params: CompressionParams, +} + +impl Strategy { + pub(super) fn new(params: CompressionParams) -> Self { + Self { params } + } + + fn field_params(&self, field: &Field) -> CompressionFieldParams { + let mut params = self + .params + .get_field_params(&field.name, &field.data_type()); + params.merge(&field_metadata_params(field)); + params + } +} + +impl CompressionStrategy for Strategy { + fn create_miniblock_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + let compressor = + if let Some(compressor) = try_uncompressed_fixed_width_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_byte_stream_split_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_fixed_u8_rle_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_bitpacking_miniblock(data) { + compressor + } else if let Some(compressor) = try_raw_fixed_width_miniblock(data) { + compressor + } else if let Some(compressor) = try_variable_width_miniblock(field, data, ¶ms)? { + compressor + } else if let Some(compressor) = try_fixed_packed_struct_miniblock(data)? { + compressor + } else if let Some(compressor) = try_raw_fixed_size_list_miniblock(data) { + compressor + } else { + return Err(Error::not_supported_source( + format!( + "Mini-block compression not yet supported for block type {}", + data.name() + ) + .into(), + )); + }; + finalize_miniblock_compressor(data, compressor, ¶ms) + } + + fn create_per_value( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + if let Some(compressor) = try_raw_per_value(data) { + return Ok(compressor); + } + if let Some(compressor) = + try_variable_packed_struct_per_value(Arc::new(self.clone()), field, data)? + { + return Ok(compressor); + } + if let Some(compressor) = try_variable_width_per_value(field, data, ¶ms)? { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Per-value compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } + + fn create_block_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result<(Box, CompressiveEncoding)> { + let params = self.field_params(field); + if let Some(compressor) = try_fixed_u8_rle_block(data, ¶ms)? { + return Ok(compressor); + } + if let Some(compressor) = try_bitpacking_block(data) { + return Ok(compressor); + } + if let Some(compressor) = try_general_block(data, ¶ms)? { + return Ok(compressor); + } + if let Some(compressor) = try_raw_block(data) { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Block compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } +} diff --git a/rust/lance-file/src/versions/v2_2/mod.rs b/rust/lance-file/src/versions/v2_2/mod.rs new file mode 100644 index 00000000000..af0085509a6 --- /dev/null +++ b/rust/lance-file/src/versions/v2_2/mod.rs @@ -0,0 +1,174 @@ +//! Lance v2.2 file composition. + +use std::{collections::BTreeMap, sync::Arc}; + +use bytes::Bytes; +use lance_core::{ + Error, Result, + datatypes::{Field, Schema}, +}; +use lance_encoding::{ + compression_config::CompressionParams, + encoder::{ + ColumnIndexSequence, EncodedBatch, FieldEncoder, FieldEncodingContext, + FieldEncodingStrategy, + structural::{ + PrimitiveFieldEncoding, PrimitivePageEncoding, try_create_binary_blob, try_create_list, + try_create_map, try_create_struct, try_create_structural_blob, + try_create_structural_fixed_size_list, + }, + }, +}; +use lance_io::traits::Writer as ObjectWriter; + +use crate::{ + reader::{ReadProjection, structural}, + writer::FileWriterOptions, +}; + +mod compression; +mod reader; +mod writer; + +pub(crate) use reader::{ + decode_column_metadata, finish_metadata, finish_metadata_index, validate_global_buffers, +}; +pub use reader::{ + projection_from_column_names, projection_from_field_ids, projection_from_whole_schema, +}; + +pub(crate) fn read_projection() -> Arc { + structural::read_projection(reader::decode_column) +} +pub use writer::Writer; + +/// Count physical columns represented by a field in a v2.2 footer. +pub fn physical_column_count(field: &Field) -> usize { + structural::physical_column_count(field) +} + +/// Build persisted field-to-column entries for a v2.2 data file. +pub fn data_file_columns(schema: &Schema) -> (Vec, Vec) { + structural::data_file_columns(schema) +} + +pub(super) fn field_id_to_column_index(schema: &Schema) -> BTreeMap { + structural::field_id_to_column_index(schema) +} + +#[derive(Debug)] +struct FieldStrategy { + primitive: PrimitiveFieldEncoding, +} + +impl FieldEncodingStrategy for FieldStrategy { + fn create_field_encoder( + &self, + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, + ) -> Result> { + if let Some(encoder) = + try_create_binary_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if let Some(encoder) = + try_create_structural_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if field.is_blob() { + return Err(Error::invalid_input_source( + format!( + "Blob encoding is not available for field '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )); + } + if let Some(encoder) = try_create_map(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_structural_fixed_size_list(field, column_index, context)? + { + return Ok(encoder); + } + if let Some(encoder) = self.primitive.try_create(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_list(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_struct(field, column_index, context)? { + return Ok(encoder); + } + Err(Error::not_supported_source( + format!( + "Lance v2.2 has no field encoding for '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )) + } +} + +/// Compose the v2.2 field encoding mechanisms. +pub fn encoding_strategy(params: CompressionParams) -> Arc { + let compression = Arc::new(compression::Strategy::new(params)); + Arc::new(FieldStrategy { + primitive: PrimitiveFieldEncoding::new([ + PrimitivePageEncoding::reject_sparse(), + PrimitivePageEncoding::constant(), + PrimitivePageEncoding::dense_u32(compression), + ]), + }) +} + +/// Create a v2.2 writer with an explicit schema. +pub fn create_writer( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, +) -> Result { + Writer::try_new(object_writer, schema, options) +} + +/// Create a v2.2 writer with explicit compression tuning. +pub fn create_writer_with_compression( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + compression: CompressionParams, +) -> Result { + Writer::try_new_with_compression(object_writer, schema, options, compression) +} + +/// Create a v2.2 writer whose schema is inferred from the first batch. +pub fn create_lazy_writer( + object_writer: Box, + options: FileWriterOptions, +) -> Writer { + Writer::new_lazy(object_writer, options) +} + +/// Create a lazy v2.2 writer with explicit compression tuning. +pub fn create_lazy_writer_with_compression( + object_writer: Box, + options: FileWriterOptions, + compression: CompressionParams, +) -> Writer { + Writer::new_lazy_with_compression(object_writer, options, compression) +} + +/// Encode a self-described v2.2 batch. +pub fn encode_self_described_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, true) +} + +/// Encode a mini-lance v2.2 batch. +pub fn encode_mini_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, false) +} diff --git a/rust/lance-file/src/versions/v2_2/reader.rs b/rust/lance-file/src/versions/v2_2/reader.rs new file mode 100644 index 00000000000..81a88720c77 --- /dev/null +++ b/rust/lance-file/src/versions/v2_2/reader.rs @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::{collections::BTreeMap, sync::Arc}; + +use lance_core::{Error, Result, datatypes::Schema}; +use lance_encoding::{decoder::ColumnInfo, format::pb21}; + +use crate::{ + format::pbfile, + reader::{ + BufferDescriptor, CachedFileMetadata, FileMetadataIndex, RawFileMetadata, ReaderProjection, + structural, + }, + version::ConcreteFileVersion, +}; + +fn required<'a, T>(value: Option<&'a T>, label: &str) -> Result<&'a T> { + value.ok_or_else(|| { + Error::invalid_input_source( + format!("Lance v2.2 {label} is missing its nested encoding").into(), + ) + }) +} + +fn validate_compressive_encoding(encoding: &pb21::CompressiveEncoding) -> Result<()> { + use pb21::compressive_encoding::Compression; + + match encoding.compression.as_ref() { + Some(Compression::Flat(_)) + | Some(Compression::InlineBitpacking(_)) + | Some(Compression::Constant(_)) => Ok(()), + Some(Compression::Variable(variable)) => validate_compressive_encoding(required( + variable.offsets.as_deref(), + "variable offsets", + )?), + Some(Compression::OutOfLineBitpacking(bitpacking)) => { + validate_compressive_encoding(required( + bitpacking.values.as_deref(), + "out-of-line bitpacking values", + )?) + } + Some(Compression::Fsst(fsst)) => { + validate_compressive_encoding(required(fsst.values.as_deref(), "FSST values")?) + } + Some(Compression::Dictionary(dictionary)) => { + validate_compressive_encoding(required( + dictionary.indices.as_deref(), + "dictionary indices", + )?)?; + validate_compressive_encoding(required( + dictionary.items.as_deref(), + "dictionary items", + )?) + } + Some(Compression::Rle(rle)) => { + let values = required(rle.values.as_deref(), "RLE values")?; + let run_lengths = required(rle.run_lengths.as_deref(), "RLE run lengths")?; + let fixed_values = matches!( + values.compression.as_ref(), + Some(Compression::Flat(flat)) + if matches!(flat.bits_per_value, 8 | 16 | 32 | 64) + && flat.data.is_none() + ); + let fixed_u8_lengths = matches!( + run_lengths.compression.as_ref(), + Some(Compression::Flat(flat)) + if flat.bits_per_value == 8 && flat.data.is_none() + ); + if !fixed_values || !fixed_u8_lengths { + return Err(Error::invalid_input_source( + "Lance v2.2 RLE requires flat values and flat u8 run lengths".into(), + )); + } + Ok(()) + } + Some(Compression::ByteStreamSplit(split)) => validate_compressive_encoding(required( + split.values.as_deref(), + "byte-stream-split values", + )?), + Some(Compression::General(general)) => validate_compressive_encoding(required( + general.values.as_deref(), + "general-compression values", + )?), + Some(Compression::FixedSizeList(list)) => validate_compressive_encoding(required( + list.values.as_deref(), + "fixed-size-list values", + )?), + Some(Compression::PackedStruct(packed)) => validate_compressive_encoding(required( + packed.values.as_deref(), + "packed-struct values", + )?), + Some(Compression::VariablePackedStruct(packed)) => { + for field in &packed.fields { + validate_compressive_encoding(required( + field.value.as_ref(), + "variable packed-struct field", + )?)?; + } + Ok(()) + } + None => Err(Error::invalid_input_source( + "Lance v2.2 compressive encoding is missing its compression variant".into(), + )), + } +} + +fn validate_page_layout(layout: &pb21::PageLayout) -> Result<()> { + use pb21::page_layout::Layout; + + match layout.layout.as_ref() { + Some(Layout::MiniBlockLayout(miniblock)) => { + if !miniblock.has_large_chunk { + return Err(Error::invalid_input_source( + "Lance v2.2 miniblock pages require the u32 chunk grammar".into(), + )); + } + if let Some(rep) = miniblock.rep_compression.as_ref() { + validate_compressive_encoding(rep)?; + } + if let Some(def) = miniblock.def_compression.as_ref() { + validate_compressive_encoding(def)?; + } + validate_compressive_encoding(required( + miniblock.value_compression.as_ref(), + "miniblock values", + )?)?; + if let Some(dictionary) = miniblock.dictionary.as_ref() { + validate_compressive_encoding(dictionary)?; + } + Ok(()) + } + Some(Layout::FullZipLayout(fullzip)) => validate_compressive_encoding(required( + fullzip.value_compression.as_ref(), + "full-zip values", + )?), + Some(Layout::ConstantLayout(constant)) => { + if let Some(rep) = constant.rep_compression.as_ref() { + validate_compressive_encoding(rep)?; + } + if let Some(def) = constant.def_compression.as_ref() { + validate_compressive_encoding(def)?; + } + Ok(()) + } + Some(Layout::BlobLayout(blob)) => { + let inner = blob.inner_layout.as_deref().ok_or_else(|| { + Error::invalid_input_source( + "Lance v2.2 blob page layout is missing its inner layout".into(), + ) + })?; + validate_page_layout(inner) + } + Some(Layout::SparseLayout(_)) => Err(Error::invalid_input_source( + "Sparse page layout is not part of the Lance v2.2 grammar".into(), + )), + None => Err(Error::invalid_input_source( + "Lance v2.2 page is missing its page layout".into(), + )), + } +} + +pub fn decode_column( + column_index: u32, + metadata: &pbfile::ColumnMetadata, +) -> Result> { + let page_infos = metadata + .pages + .iter() + .enumerate() + .map(|(page_index, page)| { + let page_layout = structural::decode_page_layout(column_index, page_index, page)?; + validate_page_layout(&page_layout)?; + structural::build_page_info(column_index, page_index, page, page_layout) + }) + .collect::>>()?; + structural::build_column_info(column_index, metadata, page_infos) +} + +pub fn decode_column_metadata( + column_metadatas: &[pbfile::ColumnMetadata], +) -> Result>> { + column_metadatas + .iter() + .enumerate() + .map(|(column_index, metadata)| { + let column_index = u32::try_from(column_index).map_err(|_| { + Error::invalid_input_source("File has more than u32::MAX columns".into()) + })?; + decode_column(column_index, metadata) + }) + .collect() +} + +pub fn projection_from_field_ids( + schema: &Schema, + field_id_to_column_index: &BTreeMap, +) -> ReaderProjection { + structural::projection_from_field_ids(schema, field_id_to_column_index) +} + +pub fn projection_from_whole_schema(schema: &Schema) -> ReaderProjection { + structural::projection_from_field_ids(schema, &super::field_id_to_column_index(schema)) +} + +pub fn projection_from_column_names( + schema: &Schema, + column_names: &[&str], +) -> Result { + structural::projection_from_column_names( + schema, + column_names, + &super::field_id_to_column_index(schema), + ) +} + +pub fn finish_metadata(raw: RawFileMetadata) -> Result { + if (raw.footer.major_version, raw.footer.minor_version) != (2, 2) { + return Err(Error::version_conflict( + "Attempt to use the Lance v2.2 reader for a different file version".to_string(), + raw.footer.major_version, + raw.footer.minor_version, + )); + } + validate_global_buffers(&raw.file_buffers)?; + let column_infos = decode_column_metadata(&raw.column_metadatas)?; + Ok(CachedFileMetadata { + file_schema: raw.file_schema, + column_metadatas: raw.column_metadatas, + column_infos, + num_rows: raw.num_rows, + file_buffers: raw.file_buffers, + num_data_bytes: raw.num_data_bytes, + num_column_metadata_bytes: raw.num_column_metadata_bytes, + num_global_buffer_bytes: raw.num_global_buffer_bytes, + num_footer_bytes: raw.num_footer_bytes, + major_version: raw.footer.major_version, + minor_version: raw.footer.minor_version, + version: ConcreteFileVersion::V2_2, + file_size_bytes: raw.file_size_bytes, + retained_global_buffers: raw.retained_global_buffers, + }) +} + +pub fn validate_global_buffers(buffers: &[BufferDescriptor]) -> Result<()> { + structural::validate_global_buffers(buffers) +} + +pub fn finish_metadata_index(index: FileMetadataIndex) -> Result { + if index.version != ConcreteFileVersion::V2_2 { + let (major, minor) = index.version.to_standard_footer_numbers(); + return Err(Error::version_conflict( + "Attempt to use the Lance v2.2 reader for a different metadata index".to_string(), + major, + minor, + )); + } + validate_global_buffers(&index.file_buffers)?; + Ok(index) +} + +#[cfg(test)] +mod grammar_tests { + use super::*; + use pb21::{ + CompressiveEncoding, Dictionary, Flat, FullZipLayout, PageLayout, Rle, + compressive_encoding::Compression, page_layout::Layout, + }; + + fn flat(bits_per_value: u64) -> CompressiveEncoding { + CompressiveEncoding { + compression: Some(Compression::Flat(Flat { + bits_per_value, + data: None, + })), + } + } + + #[test] + fn rejects_nested_variable_width_rle() { + let rle = CompressiveEncoding { + compression: Some(Compression::Rle(Box::new(Rle { + values: Some(Box::new(flat(32))), + run_lengths: Some(Box::new(flat(16))), + }))), + }; + let dictionary = CompressiveEncoding { + compression: Some(Compression::Dictionary(Box::new(Dictionary { + indices: Some(Box::new(rle)), + items: Some(Box::new(flat(32))), + num_dictionary_items: 1, + }))), + }; + let layout = PageLayout { + layout: Some(Layout::FullZipLayout(FullZipLayout { + value_compression: Some(dictionary), + ..Default::default() + })), + }; + + let error = validate_page_layout(&layout).unwrap_err(); + assert!( + error + .to_string() + .contains("flat values and flat u8 run lengths") + ); + } +} diff --git a/rust/lance-file/src/versions/v2_2/writer.rs b/rust/lance-file/src/versions/v2_2/writer.rs new file mode 100644 index 00000000000..bfe5494cc1f --- /dev/null +++ b/rust/lance-file/src/versions/v2_2/writer.rs @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::sync::Arc; + +use arrow_array::{ArrayRef, RecordBatch}; +use bytes::{BufMut, Bytes}; +use lance_core::{Result, datatypes::Schema}; +use lance_encoding::{ + compression_config::CompressionParams, + encoder::{BatchEncoder, EncodedBatch}, +}; +use lance_io::{object_store::ObjectStore, traits::Writer as ObjectWriter}; +use object_store::path::Path; +use tokio::io::AsyncWriteExt; + +use crate::{ + format::{MAGIC, pbfile}, + writer::{ + FileWriteSummary, FileWriterOptions, + structural::{EncodedBatchBody, EncodingPipeline, StructuralFileSink, encode_batch_body}, + }, +}; + +use super::encoding_strategy; + +/// A writer for the Lance v2.2 file grammar. +/// +/// The concrete writer owns the v2.2 encoding composition, finish ordering, +/// and exact footer identity. Shared components only execute the structural +/// encoding and I/O mechanisms selected here. +pub struct Writer { + sink: StructuralFileSink, + encoding: EncodingPipeline, + compression: CompressionParams, +} + +impl Writer { + /// Create a v2.2 writer with an explicit schema. + pub fn try_new( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + ) -> Result { + Self::try_new_with_compression(object_writer, schema, options, Default::default()) + } + + /// Create a v2.2 writer with explicit compression tuning. + pub fn try_new_with_compression( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + compression: CompressionParams, + ) -> Result { + let mut writer = Self::new_lazy_with_compression(object_writer, options, compression); + writer.initialize(schema)?; + Ok(writer) + } + + /// Create a v2.2 writer whose schema is inferred from the first batch. + pub fn new_lazy(object_writer: Box, options: FileWriterOptions) -> Self { + Self::new_lazy_with_compression(object_writer, options, Default::default()) + } + + /// Create a lazy v2.2 writer with explicit compression tuning. + pub fn new_lazy_with_compression( + object_writer: Box, + options: FileWriterOptions, + compression: CompressionParams, + ) -> Self { + Self { + sink: StructuralFileSink::new(object_writer), + encoding: EncodingPipeline::new(options), + compression, + } + } + + fn initialize(&mut self, schema: Schema) -> Result<()> { + let encoding_options = self.encoding.encoding_options(&schema); + schema.validate()?; + let strategy = encoding_strategy(self.compression.clone()); + let encoder = BatchEncoder::try_new(&schema, strategy.as_ref(), &encoding_options)?; + self.encoding.initialize(schema, encoder, &mut self.sink); + Ok(()) + } + + fn ensure_initialized(&mut self, batch: &RecordBatch) -> Result<()> { + if !self.encoding.is_initialized() { + self.initialize(Schema::try_from(batch.schema().as_ref())?)?; + } + Ok(()) + } + + /// Spill page metadata to a sidecar file instead of retaining it in memory. + pub fn with_page_metadata_spill(mut self, object_store: Arc, path: Path) -> Self { + self.sink.with_page_metadata_spill(object_store, path); + self + } + + /// Schedule batches to be written in iteration order. + pub async fn write_batches( + &mut self, + batches: impl Iterator, + ) -> Result<()> { + for batch in batches { + self.write_batch(batch).await?; + } + Ok(()) + } + + /// Schedule one record batch for writing. + pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> { + self.ensure_initialized(batch)?; + self.encoding.write_batch(batch, &mut self.sink).await + } + + /// Write one top-level field, advancing only that field's row count. + pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> { + self.encoding + .write_column(column_index, array, &mut self.sink) + .await + } + + /// Append a buffer whose page or column metadata is supplied externally. + pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> { + self.sink.write_external_buffer(bytes).await + } + + /// Add an entry to the schema metadata written in the file descriptor. + pub fn add_schema_metadata(&mut self, key: impl Into, value: impl Into) { + self.encoding.add_schema_metadata(key, value); + } + + /// Prepare the writer for encoded column data produced externally. + pub fn initialize_with_external_metadata( + &mut self, + schema: Schema, + column_metadata: Vec, + rows_written: u64, + ) { + self.encoding + .initialize_with_external_metadata(schema, rows_written); + self.sink.initialize_with_external_metadata(column_metadata); + } + + /// Add an arbitrary global buffer and return its one-based index. + pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result { + self.sink.add_global_buffer(buffer).await + } + + /// Finish the v2.2 file and close its object writer. + pub async fn finish(&mut self) -> Result { + // The order below is the v2.2 wire contract. + self.encoding.flush(&mut self.sink).await?; + self.encoding.finish_encoders(&mut self.sink).await?; + + let descriptor = self.encoding.make_file_descriptor()?; + let global_buffer_offsets = self.sink.write_global_buffers(descriptor).await?; + let num_global_buffers = global_buffer_offsets.len() as u32; + + let column_metadata_start = self.sink.tell().await?; + let column_metadata_offsets = self.sink.write_column_metadatas().await?; + let column_metadata_offsets_start = self + .sink + .write_offset_table(&column_metadata_offsets) + .await?; + let global_buffer_offsets_start = + self.sink.write_offset_table(&global_buffer_offsets).await?; + let num_columns = self.sink.num_columns(); + + let output = self.sink.output_mut(); + output.write_u64_le(column_metadata_start).await?; + output.write_u64_le(column_metadata_offsets_start).await?; + output.write_u64_le(global_buffer_offsets_start).await?; + output.write_u32_le(num_global_buffers).await?; + output.write_u32_le(num_columns).await?; + output.write_u16_le(2).await?; + output.write_u16_le(2).await?; + output.write_all(MAGIC).await?; + + Ok(FileWriteSummary { + num_rows: self.encoding.rows_written(), + size_bytes: self.sink.shutdown().await?, + }) + } + + /// Abandon this write. + pub async fn abort(&mut self) { + // Dropping a multipart ObjectWriter aborts the upload. + } + + /// Return the current object-writer position. + pub async fn tell(&mut self) -> Result { + self.sink.tell().await + } + + /// Return the field-id to physical-column mapping selected by v2.2. + pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] { + self.encoding.field_id_to_column_indices() + } +} + +/// Append a self-described or mini-lance v2.2 footer to an encoded batch. +pub fn concat_lance_footer(batch: &EncodedBatch, write_schema: bool) -> Result { + let EncodedBatchBody { + mut data, + column_metadata_start, + column_metadata_offsets_start, + global_buffer_offsets_start, + num_global_buffers, + num_columns, + } = encode_batch_body(batch, write_schema)?; + + data.put_u64_le(column_metadata_start); + data.put_u64_le(column_metadata_offsets_start); + data.put_u64_le(global_buffer_offsets_start); + data.put_u32_le(num_global_buffers); + data.put_u32_le(num_columns); + data.put_u16_le(2); + data.put_u16_le(2); + data.extend_from_slice(MAGIC); + Ok(data.freeze()) +} diff --git a/rust/lance-file/src/versions/v2_3/compression.rs b/rust/lance-file/src/versions/v2_3/compression.rs new file mode 100644 index 00000000000..108f0fa63a4 --- /dev/null +++ b/rust/lance-file/src/versions/v2_3/compression.rs @@ -0,0 +1,127 @@ +use std::sync::Arc; + +use lance_core::{Error, Result, datatypes::Field}; +use lance_encoding::{ + compression::{ + BlockCompressor, CompressionStrategy, field_metadata_params, finalize_miniblock_compressor, + try_bitpacking_block, try_bitpacking_miniblock, try_byte_stream_split_miniblock, + try_child_rle_miniblock, try_fixed_packed_struct_miniblock, try_general_block, + try_raw_block, try_raw_fixed_size_list_miniblock, try_raw_fixed_width_miniblock, + try_raw_per_value, try_uncompressed_fixed_width_miniblock, + try_variable_packed_struct_per_value, try_variable_rle_block, try_variable_width_miniblock, + try_variable_width_per_value, + }, + compression_config::{CompressionFieldParams, CompressionParams}, + data::DataBlock, + encodings::logical::primitive::{fullzip::PerValueCompressor, miniblock::MiniBlockCompressor}, + format::pb21::CompressiveEncoding, +}; + +#[derive(Debug, Clone)] +pub(super) struct Strategy { + params: CompressionParams, +} + +impl Strategy { + pub(super) fn new(params: CompressionParams) -> Self { + Self { params } + } + + fn field_params(&self, field: &Field) -> CompressionFieldParams { + let mut params = self + .params + .get_field_params(&field.name, &field.data_type()); + params.merge(&field_metadata_params(field)); + params + } +} + +impl CompressionStrategy for Strategy { + fn create_miniblock_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + let compressor = + if let Some(compressor) = try_uncompressed_fixed_width_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_byte_stream_split_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_child_rle_miniblock(data, ¶ms) { + compressor + } else if let Some(compressor) = try_bitpacking_miniblock(data) { + compressor + } else if let Some(compressor) = try_raw_fixed_width_miniblock(data) { + compressor + } else if let Some(compressor) = try_variable_width_miniblock(field, data, ¶ms)? { + compressor + } else if let Some(compressor) = try_fixed_packed_struct_miniblock(data)? { + compressor + } else if let Some(compressor) = try_raw_fixed_size_list_miniblock(data) { + compressor + } else { + return Err(Error::not_supported_source( + format!( + "Mini-block compression not yet supported for block type {}", + data.name() + ) + .into(), + )); + }; + finalize_miniblock_compressor(data, compressor, ¶ms) + } + + fn create_per_value( + &self, + field: &Field, + data: &DataBlock, + ) -> Result> { + let params = self.field_params(field); + if let Some(compressor) = try_raw_per_value(data) { + return Ok(compressor); + } + if let Some(compressor) = + try_variable_packed_struct_per_value(Arc::new(self.clone()), field, data)? + { + return Ok(compressor); + } + if let Some(compressor) = try_variable_width_per_value(field, data, ¶ms)? { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Per-value compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } + + fn create_block_compressor( + &self, + field: &Field, + data: &DataBlock, + ) -> Result<(Box, CompressiveEncoding)> { + let params = self.field_params(field); + if let Some(compressor) = try_variable_rle_block(data, ¶ms)? { + return Ok(compressor); + } + if let Some(compressor) = try_bitpacking_block(data) { + return Ok(compressor); + } + if let Some(compressor) = try_general_block(data, ¶ms)? { + return Ok(compressor); + } + if let Some(compressor) = try_raw_block(data) { + return Ok(compressor); + } + Err(Error::not_supported_source( + format!( + "Block compression not yet supported for block type {}", + data.name() + ) + .into(), + )) + } +} diff --git a/rust/lance-file/src/versions/v2_3/mod.rs b/rust/lance-file/src/versions/v2_3/mod.rs new file mode 100644 index 00000000000..4006ce30de3 --- /dev/null +++ b/rust/lance-file/src/versions/v2_3/mod.rs @@ -0,0 +1,197 @@ +//! Lance v2.3 file composition. + +use std::{ + collections::BTreeMap, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, +}; + +use bytes::Bytes; +use lance_core::{ + Error, Result, + datatypes::{Field, Schema}, +}; +use lance_encoding::{ + compression_config::CompressionParams, + encoder::{ + ColumnIndexSequence, EncodedBatch, FieldEncoder, FieldEncodingContext, + FieldEncodingStrategy, + structural::{ + PrimitiveFieldEncoding, PrimitivePageEncoding, try_create_binary_blob, try_create_list, + try_create_map, try_create_struct, try_create_structural_blob, + try_create_structural_fixed_size_list, + }, + }, +}; +use lance_io::traits::Writer as ObjectWriter; + +use crate::{ + reader::{ReadProjection, structural}, + writer::FileWriterOptions, +}; + +mod compression; +mod reader; +mod writer; + +pub(crate) use reader::{ + decode_column_metadata, finish_metadata, finish_metadata_index, validate_global_buffers, +}; +pub use reader::{ + projection_from_column_names, projection_from_field_ids, projection_from_whole_schema, +}; + +pub(crate) fn read_projection() -> Arc { + structural::read_projection(reader::decode_column) +} +pub use writer::Writer; + +static WARNED_ON_UNSTABLE_FORMAT: AtomicBool = AtomicBool::new(false); + +/// Count physical columns represented by a field in a v2.3 footer. +pub fn physical_column_count(field: &Field) -> usize { + structural::physical_column_count(field) +} + +/// Build persisted field-to-column entries for a v2.3 data file. +pub fn data_file_columns(schema: &Schema) -> (Vec, Vec) { + structural::data_file_columns(schema) +} + +pub(super) fn field_id_to_column_index(schema: &Schema) -> BTreeMap { + structural::field_id_to_column_index(schema) +} + +#[derive(Debug)] +struct FieldStrategy { + primitive: PrimitiveFieldEncoding, +} + +impl FieldEncodingStrategy for FieldStrategy { + fn create_field_encoder( + &self, + field: &Field, + column_index: &mut ColumnIndexSequence, + context: &FieldEncodingContext<'_>, + ) -> Result> { + if let Some(encoder) = + try_create_binary_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if let Some(encoder) = + try_create_structural_blob(&self.primitive, field, column_index, context)? + { + return Ok(encoder); + } + if field.is_blob() { + return Err(Error::invalid_input_source( + format!( + "Blob encoding is not available for field '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )); + } + if let Some(encoder) = try_create_map(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_structural_fixed_size_list(field, column_index, context)? + { + return Ok(encoder); + } + if let Some(encoder) = self.primitive.try_create(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_list(field, column_index, context)? { + return Ok(encoder); + } + if let Some(encoder) = try_create_struct(field, column_index, context)? { + return Ok(encoder); + } + Err(Error::not_supported_source( + format!( + "Lance v2.3 has no field encoding for '{}' with data type {}", + field.name, + field.data_type() + ) + .into(), + )) + } +} + +/// Compose the v2.3 field encoding mechanisms. +pub fn encoding_strategy(params: CompressionParams) -> Arc { + let compression = Arc::new(compression::Strategy::new(params)); + Arc::new(FieldStrategy { + primitive: PrimitiveFieldEncoding::new([ + PrimitivePageEncoding::sparse(compression.clone()), + PrimitivePageEncoding::constant(), + PrimitivePageEncoding::dense_u32(compression), + ]), + }) +} + +fn warn_unstable_format() { + if WARNED_ON_UNSTABLE_FORMAT + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + log::warn!( + "You have requested an unstable format version. Files written with this format version may not be readable in the future! This is a development feature and should only be used for experimentation and never for production data." + ); + } +} + +/// Create a v2.3 writer with an explicit schema. +pub fn create_writer( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, +) -> Result { + warn_unstable_format(); + Writer::try_new(object_writer, schema, options) +} + +/// Create a v2.3 writer with explicit compression tuning. +pub fn create_writer_with_compression( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + compression: CompressionParams, +) -> Result { + warn_unstable_format(); + Writer::try_new_with_compression(object_writer, schema, options, compression) +} + +/// Create a v2.3 writer whose schema is inferred from the first batch. +pub fn create_lazy_writer( + object_writer: Box, + options: FileWriterOptions, +) -> Writer { + warn_unstable_format(); + Writer::new_lazy(object_writer, options) +} + +/// Create a lazy v2.3 writer with explicit compression tuning. +pub fn create_lazy_writer_with_compression( + object_writer: Box, + options: FileWriterOptions, + compression: CompressionParams, +) -> Writer { + warn_unstable_format(); + Writer::new_lazy_with_compression(object_writer, options, compression) +} + +/// Encode a self-described v2.3 batch. +pub fn encode_self_described_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, true) +} + +/// Encode a mini-lance v2.3 batch. +pub fn encode_mini_batch(batch: &EncodedBatch) -> Result { + writer::concat_lance_footer(batch, false) +} diff --git a/rust/lance-file/src/versions/v2_3/reader.rs b/rust/lance-file/src/versions/v2_3/reader.rs new file mode 100644 index 00000000000..cdafbd0b9db --- /dev/null +++ b/rust/lance-file/src/versions/v2_3/reader.rs @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::{collections::BTreeMap, sync::Arc}; + +use lance_core::{Error, Result, datatypes::Schema}; +use lance_encoding::{decoder::ColumnInfo, format::pb21}; + +use crate::{ + format::pbfile, + reader::{ + BufferDescriptor, CachedFileMetadata, FileMetadataIndex, RawFileMetadata, ReaderProjection, + structural, + }, + version::ConcreteFileVersion, +}; + +fn required<'a, T>(value: Option<&'a T>, label: &str) -> Result<&'a T> { + value.ok_or_else(|| { + Error::invalid_input_source( + format!("Lance v2.3 {label} is missing its nested encoding").into(), + ) + }) +} + +fn validate_compressive_encoding(encoding: &pb21::CompressiveEncoding) -> Result<()> { + use pb21::compressive_encoding::Compression; + + match encoding.compression.as_ref() { + Some(Compression::Flat(_)) + | Some(Compression::InlineBitpacking(_)) + | Some(Compression::Constant(_)) => Ok(()), + Some(Compression::Variable(variable)) => validate_compressive_encoding(required( + variable.offsets.as_deref(), + "variable offsets", + )?), + Some(Compression::OutOfLineBitpacking(bitpacking)) => { + validate_compressive_encoding(required( + bitpacking.values.as_deref(), + "out-of-line bitpacking values", + )?) + } + Some(Compression::Fsst(fsst)) => { + validate_compressive_encoding(required(fsst.values.as_deref(), "FSST values")?) + } + Some(Compression::Dictionary(dictionary)) => { + validate_compressive_encoding(required( + dictionary.indices.as_deref(), + "dictionary indices", + )?)?; + validate_compressive_encoding(required( + dictionary.items.as_deref(), + "dictionary items", + )?) + } + Some(Compression::Rle(rle)) => { + validate_compressive_encoding(required(rle.values.as_deref(), "RLE values")?)?; + validate_compressive_encoding(required(rle.run_lengths.as_deref(), "RLE run lengths")?) + } + Some(Compression::ByteStreamSplit(split)) => validate_compressive_encoding(required( + split.values.as_deref(), + "byte-stream-split values", + )?), + Some(Compression::General(general)) => validate_compressive_encoding(required( + general.values.as_deref(), + "general-compression values", + )?), + Some(Compression::FixedSizeList(list)) => validate_compressive_encoding(required( + list.values.as_deref(), + "fixed-size-list values", + )?), + Some(Compression::PackedStruct(packed)) => validate_compressive_encoding(required( + packed.values.as_deref(), + "packed-struct values", + )?), + Some(Compression::VariablePackedStruct(packed)) => { + for field in &packed.fields { + validate_compressive_encoding(required( + field.value.as_ref(), + "variable packed-struct field", + )?)?; + } + Ok(()) + } + None => Err(Error::invalid_input_source( + "Lance v2.3 compressive encoding is missing its compression variant".into(), + )), + } +} + +fn validate_sparse_positions(positions: Option<&pb21::SparsePositionSet>) -> Result<()> { + use pb21::sparse_position_set::Positions; + + if let Some(Positions::Explicit(encoding)) = + positions.and_then(|positions| positions.positions.as_ref()) + { + validate_compressive_encoding(encoding)?; + } + Ok(()) +} + +fn validate_sparse_validity(validity: Option<&pb21::SparseValiditySet>) -> Result<()> { + if let Some(validity) = validity { + validate_sparse_positions(validity.positions.as_ref())?; + } + Ok(()) +} + +fn validate_sparse_layout(layout: &pb21::SparseLayout) -> Result<()> { + use pb21::{sparse_count_set::Counts, sparse_structural_layer::Layer}; + + if !layout.has_large_chunk { + return Err(Error::invalid_input_source( + "Lance v2.3 sparse pages require the u32 chunk grammar".into(), + )); + } + validate_compressive_encoding(required( + layout.value_compression.as_ref(), + "sparse values", + )?)?; + for layer in &layout.structural_layers { + match layer.layer.as_ref() { + Some(Layer::Validity(validity)) => { + validate_sparse_validity(validity.validity.as_ref())? + } + Some(Layer::List(list)) => { + validate_sparse_positions(list.non_empty_positions.as_ref())?; + if let Some(Counts::Explicit(encoding)) = list + .counts + .as_ref() + .and_then(|counts| counts.counts.as_ref()) + { + validate_compressive_encoding(encoding)?; + } + validate_sparse_validity(list.validity.as_ref())?; + } + Some(Layer::FixedSizeList(list)) => validate_sparse_validity(list.validity.as_ref())?, + None => { + return Err(Error::invalid_input_source( + "Lance v2.3 sparse structural layer is missing its layer variant".into(), + )); + } + } + } + Ok(()) +} + +fn validate_page_layout(layout: &pb21::PageLayout) -> Result<()> { + use pb21::page_layout::Layout; + + match layout.layout.as_ref() { + Some(Layout::MiniBlockLayout(miniblock)) => { + if !miniblock.has_large_chunk { + return Err(Error::invalid_input_source( + "Lance v2.3 miniblock pages require the u32 chunk grammar".into(), + )); + } + if let Some(rep) = miniblock.rep_compression.as_ref() { + validate_compressive_encoding(rep)?; + } + if let Some(def) = miniblock.def_compression.as_ref() { + validate_compressive_encoding(def)?; + } + validate_compressive_encoding(required( + miniblock.value_compression.as_ref(), + "miniblock values", + )?)?; + if let Some(dictionary) = miniblock.dictionary.as_ref() { + validate_compressive_encoding(dictionary)?; + } + Ok(()) + } + Some(Layout::FullZipLayout(fullzip)) => validate_compressive_encoding(required( + fullzip.value_compression.as_ref(), + "full-zip values", + )?), + Some(Layout::ConstantLayout(constant)) => { + if let Some(rep) = constant.rep_compression.as_ref() { + validate_compressive_encoding(rep)?; + } + if let Some(def) = constant.def_compression.as_ref() { + validate_compressive_encoding(def)?; + } + Ok(()) + } + Some(Layout::SparseLayout(sparse)) => validate_sparse_layout(sparse), + Some(Layout::BlobLayout(blob)) => { + let inner = blob.inner_layout.as_deref().ok_or_else(|| { + Error::invalid_input_source( + "Lance v2.3 blob page layout is missing its inner layout".into(), + ) + })?; + validate_page_layout(inner) + } + None => Err(Error::invalid_input_source( + "Lance v2.3 page is missing its page layout".into(), + )), + } +} + +pub fn decode_column( + column_index: u32, + metadata: &pbfile::ColumnMetadata, +) -> Result> { + let page_infos = metadata + .pages + .iter() + .enumerate() + .map(|(page_index, page)| { + let page_layout = structural::decode_page_layout(column_index, page_index, page)?; + validate_page_layout(&page_layout)?; + structural::build_page_info(column_index, page_index, page, page_layout) + }) + .collect::>>()?; + structural::build_column_info(column_index, metadata, page_infos) +} + +pub fn decode_column_metadata( + column_metadatas: &[pbfile::ColumnMetadata], +) -> Result>> { + column_metadatas + .iter() + .enumerate() + .map(|(column_index, metadata)| { + let column_index = u32::try_from(column_index).map_err(|_| { + Error::invalid_input_source("File has more than u32::MAX columns".into()) + })?; + decode_column(column_index, metadata) + }) + .collect() +} + +pub fn projection_from_field_ids( + schema: &Schema, + field_id_to_column_index: &BTreeMap, +) -> ReaderProjection { + structural::projection_from_field_ids(schema, field_id_to_column_index) +} + +pub fn projection_from_whole_schema(schema: &Schema) -> ReaderProjection { + structural::projection_from_field_ids(schema, &super::field_id_to_column_index(schema)) +} + +pub fn projection_from_column_names( + schema: &Schema, + column_names: &[&str], +) -> Result { + structural::projection_from_column_names( + schema, + column_names, + &super::field_id_to_column_index(schema), + ) +} + +pub fn finish_metadata(raw: RawFileMetadata) -> Result { + if (raw.footer.major_version, raw.footer.minor_version) != (2, 3) { + return Err(Error::version_conflict( + "Attempt to use the Lance v2.3 reader for a different file version".to_string(), + raw.footer.major_version, + raw.footer.minor_version, + )); + } + validate_global_buffers(&raw.file_buffers)?; + let column_infos = decode_column_metadata(&raw.column_metadatas)?; + Ok(CachedFileMetadata { + file_schema: raw.file_schema, + column_metadatas: raw.column_metadatas, + column_infos, + num_rows: raw.num_rows, + file_buffers: raw.file_buffers, + num_data_bytes: raw.num_data_bytes, + num_column_metadata_bytes: raw.num_column_metadata_bytes, + num_global_buffer_bytes: raw.num_global_buffer_bytes, + num_footer_bytes: raw.num_footer_bytes, + major_version: raw.footer.major_version, + minor_version: raw.footer.minor_version, + version: ConcreteFileVersion::V2_3, + file_size_bytes: raw.file_size_bytes, + retained_global_buffers: raw.retained_global_buffers, + }) +} + +pub fn validate_global_buffers(buffers: &[BufferDescriptor]) -> Result<()> { + structural::validate_global_buffers(buffers) +} + +pub fn finish_metadata_index(index: FileMetadataIndex) -> Result { + if index.version != ConcreteFileVersion::V2_3 { + let (major, minor) = index.version.to_standard_footer_numbers(); + return Err(Error::version_conflict( + "Attempt to use the Lance v2.3 reader for a different metadata index".to_string(), + major, + minor, + )); + } + validate_global_buffers(&index.file_buffers)?; + Ok(index) +} + +#[cfg(test)] +mod grammar_tests { + use super::*; + use pb21::{ + CompressiveEncoding, Dictionary, Flat, FullZipLayout, PageLayout, Rle, + compressive_encoding::Compression, page_layout::Layout, + }; + + fn flat(bits_per_value: u64) -> CompressiveEncoding { + CompressiveEncoding { + compression: Some(Compression::Flat(Flat { + bits_per_value, + data: None, + })), + } + } + + #[test] + fn accepts_nested_variable_width_rle() { + let rle = CompressiveEncoding { + compression: Some(Compression::Rle(Box::new(Rle { + values: Some(Box::new(flat(32))), + run_lengths: Some(Box::new(flat(16))), + }))), + }; + let dictionary = CompressiveEncoding { + compression: Some(Compression::Dictionary(Box::new(Dictionary { + indices: Some(Box::new(rle)), + items: Some(Box::new(flat(32))), + num_dictionary_items: 1, + }))), + }; + let layout = PageLayout { + layout: Some(Layout::FullZipLayout(FullZipLayout { + value_compression: Some(dictionary), + ..Default::default() + })), + }; + + validate_page_layout(&layout).unwrap(); + } +} diff --git a/rust/lance-file/src/versions/v2_3/writer.rs b/rust/lance-file/src/versions/v2_3/writer.rs new file mode 100644 index 00000000000..c18c7aa6e18 --- /dev/null +++ b/rust/lance-file/src/versions/v2_3/writer.rs @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use std::sync::Arc; + +use arrow_array::{ArrayRef, RecordBatch}; +use bytes::{BufMut, Bytes}; +use lance_core::{Result, datatypes::Schema}; +use lance_encoding::{ + compression_config::CompressionParams, + encoder::{BatchEncoder, EncodedBatch}, +}; +use lance_io::{object_store::ObjectStore, traits::Writer as ObjectWriter}; +use object_store::path::Path; +use tokio::io::AsyncWriteExt; + +use crate::{ + format::{MAGIC, pbfile}, + writer::{ + FileWriteSummary, FileWriterOptions, + structural::{EncodedBatchBody, EncodingPipeline, StructuralFileSink, encode_batch_body}, + }, +}; + +use super::encoding_strategy; + +/// A writer for the Lance v2.3 file grammar. +/// +/// The concrete writer owns the v2.3 encoding composition, finish ordering, +/// and exact footer identity. Shared components only execute the structural +/// encoding and I/O mechanisms selected here. +pub struct Writer { + sink: StructuralFileSink, + encoding: EncodingPipeline, + compression: CompressionParams, +} + +impl Writer { + /// Create a v2.3 writer with an explicit schema. + pub fn try_new( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + ) -> Result { + Self::try_new_with_compression(object_writer, schema, options, Default::default()) + } + + /// Create a v2.3 writer with explicit compression tuning. + pub fn try_new_with_compression( + object_writer: Box, + schema: Schema, + options: FileWriterOptions, + compression: CompressionParams, + ) -> Result { + let mut writer = Self::new_lazy_with_compression(object_writer, options, compression); + writer.initialize(schema)?; + Ok(writer) + } + + /// Create a v2.3 writer whose schema is inferred from the first batch. + pub fn new_lazy(object_writer: Box, options: FileWriterOptions) -> Self { + Self::new_lazy_with_compression(object_writer, options, Default::default()) + } + + /// Create a lazy v2.3 writer with explicit compression tuning. + pub fn new_lazy_with_compression( + object_writer: Box, + options: FileWriterOptions, + compression: CompressionParams, + ) -> Self { + Self { + sink: StructuralFileSink::new(object_writer), + encoding: EncodingPipeline::new(options), + compression, + } + } + + fn initialize(&mut self, schema: Schema) -> Result<()> { + let encoding_options = self.encoding.encoding_options(&schema); + schema.validate()?; + let strategy = encoding_strategy(self.compression.clone()); + let encoder = BatchEncoder::try_new(&schema, strategy.as_ref(), &encoding_options)?; + self.encoding.initialize(schema, encoder, &mut self.sink); + Ok(()) + } + + fn ensure_initialized(&mut self, batch: &RecordBatch) -> Result<()> { + if !self.encoding.is_initialized() { + self.initialize(Schema::try_from(batch.schema().as_ref())?)?; + } + Ok(()) + } + + /// Spill page metadata to a sidecar file instead of retaining it in memory. + pub fn with_page_metadata_spill(mut self, object_store: Arc, path: Path) -> Self { + self.sink.with_page_metadata_spill(object_store, path); + self + } + + /// Schedule batches to be written in iteration order. + pub async fn write_batches( + &mut self, + batches: impl Iterator, + ) -> Result<()> { + for batch in batches { + self.write_batch(batch).await?; + } + Ok(()) + } + + /// Schedule one record batch for writing. + pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> { + self.ensure_initialized(batch)?; + self.encoding.write_batch(batch, &mut self.sink).await + } + + /// Write one top-level field, advancing only that field's row count. + pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> { + self.encoding + .write_column(column_index, array, &mut self.sink) + .await + } + + /// Append a buffer whose page or column metadata is supplied externally. + pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> { + self.sink.write_external_buffer(bytes).await + } + + /// Add an entry to the schema metadata written in the file descriptor. + pub fn add_schema_metadata(&mut self, key: impl Into, value: impl Into) { + self.encoding.add_schema_metadata(key, value); + } + + /// Prepare the writer for encoded column data produced externally. + pub fn initialize_with_external_metadata( + &mut self, + schema: Schema, + column_metadata: Vec, + rows_written: u64, + ) { + self.encoding + .initialize_with_external_metadata(schema, rows_written); + self.sink.initialize_with_external_metadata(column_metadata); + } + + /// Add an arbitrary global buffer and return its one-based index. + pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result { + self.sink.add_global_buffer(buffer).await + } + + /// Finish the v2.3 file and close its object writer. + pub async fn finish(&mut self) -> Result { + // The order below is the v2.3 wire contract. + self.encoding.flush(&mut self.sink).await?; + self.encoding.finish_encoders(&mut self.sink).await?; + + let descriptor = self.encoding.make_file_descriptor()?; + let global_buffer_offsets = self.sink.write_global_buffers(descriptor).await?; + let num_global_buffers = global_buffer_offsets.len() as u32; + + let column_metadata_start = self.sink.tell().await?; + let column_metadata_offsets = self.sink.write_column_metadatas().await?; + let column_metadata_offsets_start = self + .sink + .write_offset_table(&column_metadata_offsets) + .await?; + let global_buffer_offsets_start = + self.sink.write_offset_table(&global_buffer_offsets).await?; + let num_columns = self.sink.num_columns(); + + let output = self.sink.output_mut(); + output.write_u64_le(column_metadata_start).await?; + output.write_u64_le(column_metadata_offsets_start).await?; + output.write_u64_le(global_buffer_offsets_start).await?; + output.write_u32_le(num_global_buffers).await?; + output.write_u32_le(num_columns).await?; + output.write_u16_le(2).await?; + output.write_u16_le(3).await?; + output.write_all(MAGIC).await?; + + Ok(FileWriteSummary { + num_rows: self.encoding.rows_written(), + size_bytes: self.sink.shutdown().await?, + }) + } + + /// Abandon this write. + pub async fn abort(&mut self) { + // Dropping a multipart ObjectWriter aborts the upload. + } + + /// Return the current object-writer position. + pub async fn tell(&mut self) -> Result { + self.sink.tell().await + } + + /// Return the field-id to physical-column mapping selected by v2.3. + pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] { + self.encoding.field_id_to_column_indices() + } +} + +/// Append a self-described or mini-lance v2.3 footer to an encoded batch. +pub fn concat_lance_footer(batch: &EncodedBatch, write_schema: bool) -> Result { + let EncodedBatchBody { + mut data, + column_metadata_start, + column_metadata_offsets_start, + global_buffer_offsets_start, + num_global_buffers, + num_columns, + } = encode_batch_body(batch, write_schema)?; + + data.put_u64_le(column_metadata_start); + data.put_u64_le(column_metadata_offsets_start); + data.put_u64_le(global_buffer_offsets_start); + data.put_u32_le(num_global_buffers); + data.put_u32_le(num_columns); + data.put_u16_le(2); + data.put_u16_le(3); + data.extend_from_slice(MAGIC); + Ok(data.freeze()) +} diff --git a/rust/lance-file/src/writer.rs b/rust/lance-file/src/writer.rs index f7042b6b99c..41b052c6091 100644 --- a/rust/lance-file/src/writer.rs +++ b/rust/lance-file/src/writer.rs @@ -1,53 +1,24 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The Lance Authors -use core::panic; -use std::collections::HashMap; use std::sync::Arc; -use std::sync::atomic::AtomicBool; use arrow_array::{ArrayRef, RecordBatch}; - -use arrow_data::ArrayData; -use bytes::{Buf, BufMut, Bytes, BytesMut}; -use futures::StreamExt; -use futures::stream::FuturesOrdered; -use lance_core::datatypes::{Field, Schema as LanceSchema}; -use lance_core::utils::bit::pad_bytes; -use lance_core::{Error, Result}; -use lance_encoding::decoder::PageEncoding; -use lance_encoding::encoder::{ - BatchEncoder, EncodeTask, EncodedBatch, EncodedPage, EncodingOptions, FieldEncoder, - FieldEncodingStrategy, OutOfLineBuffers, default_encoding_strategy, -}; -use lance_encoding::repdef::RepDefBuilder; -use lance_encoding::version::LanceFileVersion; +use bytes::Bytes; +use lance_core::{Result, datatypes::Schema}; +use lance_encoding::decoder::{ColumnInfo, PageEncoding}; use lance_io::object_store::ObjectStore; -use lance_io::traits::Writer; -use log::{debug, warn}; use object_store::path::Path; use prost::Message; use prost_types::Any; -use tokio::io::AsyncWrite; -use tokio::io::AsyncWriteExt; -use tracing::instrument; -use crate::datatypes::FieldsWithMeta; -use crate::format::MAGIC; -use crate::format::pb; -use crate::format::pbfile; -use crate::format::pbfile::DirectEncoding; +use crate::{format::pbfile, versions}; + +pub(crate) mod structural; -/// Pages buffers are aligned to 64 bytes +/// Page buffers in current Lance files are aligned to 64 bytes. pub(crate) const PAGE_BUFFER_ALIGNMENT: usize = 64; -const PAD_BUFFER: [u8; PAGE_BUFFER_ALIGNMENT] = [72; PAGE_BUFFER_ALIGNMENT]; -// In 2.1+, we split large pages on read instead of write to avoid empty pages -// and small pages issues. However, we keep the write-time limit at 32MB to avoid -// potential regressions in 2.0 format readers. -// -// This limit is not applied in the 2.1 writer -const MAX_PAGE_BYTES: usize = 32 * 1024 * 1024; -const ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES: &str = "LANCE_FILE_WRITER_MAX_PAGE_BYTES"; +pub(crate) const ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES: &str = "LANCE_FILE_WRITER_MAX_PAGE_BYTES"; /// Summary of a completed Lance file write. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -58,345 +29,120 @@ pub struct FileWriteSummary { pub size_bytes: u64, } +/// Runtime options shared by all current-format writers. +/// +/// These options control buffering and execution only. Select the file grammar +/// by constructing a writer under [`crate::versions`]. #[derive(Debug, Clone, Default)] pub struct FileWriterOptions { - /// How many bytes to use for buffering column data + /// How many bytes to use for buffering column data. /// - /// When data comes in small batches the writer will buffer column data so that - /// larger pages can be created. This value will be divided evenly across all of the - /// columns. Generally you want this to be at least large enough to match your - /// filesystem's ideal read size per column. - /// - /// In some cases you might want this value to be even larger if you have highly - /// compressible data. However, if this is too large, then the writer could require - /// a lot of memory and write performance may suffer if the CPU-expensive encoding - /// falls behind and can't be interleaved with the I/O expensive flushing. - /// - /// The default will use 8MiB per column which should be reasonable for most cases. - // TODO: Do we need to be able to set this on a per-column basis? + /// The budget is divided evenly across columns. The default is 8 MiB per + /// column. pub data_cache_bytes: Option, - /// A hint to indicate the max size of a page - /// - /// This hint can't always be respected. A single value could be larger than this value - /// and we never slice single values. In addition, there are some cases where it can be - /// difficult to know size up-front and so we might not be able to respect this value. + /// A best-effort maximum encoded page size. pub max_page_bytes: Option, - /// The file writer buffers columns until enough data has arrived to flush a page - /// to disk. + /// Keep input arrays instead of copying buffered slices. /// - /// Some columns with small data types may not flush very often. These arrays can - /// stick around for a long time. These arrays might also be keeping larger data - /// structures alive. By default, the writer will make a deep copy of this array - /// to avoid any potential memory leaks. However, this can be disabled for a - /// (probably minor) performance boost if you are sure that arrays are not keeping - /// any sibling structures alive (this typically means the array was allocated in - /// the same language / runtime as the writer) - /// - /// Do not enable this if your data is arriving from the C data interface. - /// Data typically arrives one "batch" at a time (encoded in the C data interface - /// as a struct array). Each array in that batch keeps the entire batch alive. - /// This means a small boolean array (which we will buffer in memory for quite a - /// while) might keep a much larger record batch around in memory (even though most - /// of that batch's data has been written to disk) + /// Do not enable this for arrays arriving through the Arrow C data + /// interface because a small child array can keep an entire batch alive. pub keep_original_array: Option, - pub encoding_strategy: Option>, - /// The format version to use when writing the file - /// - /// This controls which encodings will be used when encoding the data. Newer - /// versions may have more efficient encodings. However, newer format versions will - /// require more up-to-date readers to read the data. - pub format_version: Option, } -// Total in-memory budget for buffering serialized page metadata before flushing -// to the spill file. Divided evenly across columns (with a floor of 64 bytes). -const DEFAULT_SPILL_BUFFER_LIMIT: usize = 256 * 1024; - -/// Spills serialized page metadata to a temporary file to bound memory usage. +/// A type-erased current-format file writer. /// -/// The spill file is an unstructured sequence of "chunks". Each chunk is a -/// contiguous run of length-delimited protobuf `Page` messages belonging to a -/// single column. Chunks from different columns are interleaved in the order -/// they are flushed (i.e. whenever a column's in-memory buffer exceeds -/// `per_column_limit`). The `column_chunks` index records the (offset, length) -/// of every chunk so each column's pages can be read back and reassembled in -/// order. -struct PageMetadataSpill { - writer: Box, - object_store: Arc, - path: Path, - /// Current write position in the spill file. - position: u64, - /// Per-column buffer of serialized (length-delimited protobuf) page metadata - /// that has not yet been flushed to the spill file. - column_buffers: Vec>, - /// Per-column list of chunks that have been flushed to the spill file. - /// Each entry is (offset, length) pointing into the spill file. - column_chunks: Vec>, - /// Maximum bytes to buffer per column before flushing to the spill file. - per_column_limit: usize, +/// This enum exists for callers that select a concrete file version at +/// runtime. Each variant owns the complete implementation for exactly one file +/// grammar; this type only forwards operations without adding format policy. +pub enum FileWriter { + V2_0(Box), + V2_1(Box), + V2_2(Box), + V2_3(Box), } -impl PageMetadataSpill { - async fn new(object_store: Arc, path: Path, num_columns: usize) -> Result { - let writer = object_store.create(&path).await?; - let per_column_limit = (DEFAULT_SPILL_BUFFER_LIMIT / num_columns.max(1)).max(64); - Ok(Self { - writer, - object_store, - path, - position: 0, - column_buffers: vec![Vec::new(); num_columns], - column_chunks: vec![Vec::new(); num_columns], - per_column_limit, +fn column_info_to_metadata(column: &ColumnInfo) -> Result { + let pages = column + .page_infos + .iter() + .map(|page| { + let encoding = match &page.encoding { + PageEncoding::Array(encoding) => Any::from_msg(encoding)?.encode_to_vec(), + PageEncoding::Structural(encoding) => Any::from_msg(encoding)?.encode_to_vec(), + }; + let (buffer_offsets, buffer_sizes) = + page.buffer_offsets_and_sizes.iter().copied().unzip(); + Ok(pbfile::column_metadata::Page { + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { + encoding, + })), + }), + length: page.num_rows, + priority: page.priority, + }) }) - } - - async fn append_page( - &mut self, - column_idx: usize, - page: &pbfile::column_metadata::Page, - ) -> Result<()> { - page.encode_length_delimited(&mut self.column_buffers[column_idx]) - .map_err(|e| { - Error::io_source(Box::new(std::io::Error::new( - std::io::ErrorKind::InvalidData, - e, - ))) - })?; - if self.column_buffers[column_idx].len() >= self.per_column_limit { - self.flush_column(column_idx).await?; - } - Ok(()) - } - - async fn flush_column(&mut self, column_idx: usize) -> Result<()> { - let buf = &self.column_buffers[column_idx]; - if buf.is_empty() { - return Ok(()); - } - let len = buf.len(); - self.writer.write_all(buf).await?; - self.column_chunks[column_idx].push((self.position, len as u32)); - self.position += len as u64; - self.column_buffers[column_idx].clear(); - Ok(()) - } - - async fn shutdown_writer(&mut self) -> Result<()> { - for col_idx in 0..self.column_buffers.len() { - self.flush_column(col_idx).await?; - } - Writer::shutdown(self.writer.as_mut()).await?; - Ok(()) - } + .collect::>>()?; + let (buffer_offsets, buffer_sizes) = column.buffer_offsets_and_sizes.iter().copied().unzip(); + let encoding = Any::from_msg(&column.encoding)?.encode_to_vec(); + Ok(pbfile::ColumnMetadata { + pages, + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { + encoding, + })), + }), + }) } -fn decode_spilled_chunk(data: &Bytes) -> Result> { - let mut pages = Vec::new(); - let mut cursor = data.clone(); - while cursor.has_remaining() { - let page = - pbfile::column_metadata::Page::decode_length_delimited(&mut cursor).map_err(|e| { - Error::io_source(Box::new(std::io::Error::new( - std::io::ErrorKind::InvalidData, - e, - ))) - })?; - pages.push(page); +impl From for FileWriter { + fn from(writer: versions::v2_0::Writer) -> Self { + Self::V2_0(Box::new(writer)) } - Ok(pages) } -enum PageSpillState { - Pending(Arc, Path), - Active(PageMetadataSpill), +impl From for FileWriter { + fn from(writer: versions::v2_1::Writer) -> Self { + Self::V2_1(Box::new(writer)) + } } -pub struct FileWriter { - writer: Box, - schema: Option, - column_writers: Vec>, - column_metadata: Vec, - field_id_to_column_indices: Vec<(u32, u32)>, - num_columns: u32, - rows_written: u64, - // The number of rows written for each top-level field (i.e. each entry in - // `column_writers`). With `write_batch` every field advances together and - // these are all equal, but `write_column` advances one field at a time, so - // a single file may end up with columns of differing item counts. - field_rows_written: Vec, - global_buffers: Vec<(u64, u64)>, - schema_metadata: HashMap, - options: FileWriterOptions, - page_spill: Option, +impl From for FileWriter { + fn from(writer: versions::v2_2::Writer) -> Self { + Self::V2_2(Box::new(writer)) + } } -fn initial_column_metadata() -> pbfile::ColumnMetadata { - pbfile::ColumnMetadata { - pages: Vec::new(), - buffer_offsets: Vec::new(), - buffer_sizes: Vec::new(), - encoding: None, +impl From for FileWriter { + fn from(writer: versions::v2_3::Writer) -> Self { + Self::V2_3(Box::new(writer)) } } -static WARNED_ON_UNSTABLE_API: AtomicBool = AtomicBool::new(false); - impl FileWriter { - /// Create a new FileWriter with a desired output schema - pub fn try_new( - object_writer: Box, - schema: LanceSchema, - options: FileWriterOptions, - ) -> Result { - let mut writer = Self::new_lazy(object_writer, options); - writer.initialize(schema)?; - Ok(writer) - } - - /// Create a new FileWriter without a desired output schema - /// - /// The output schema will be set based on the first batch of data to arrive. - /// If no data arrives and the writer is finished then the write will fail. - pub fn new_lazy(object_writer: Box, options: FileWriterOptions) -> Self { - if let Some(format_version) = options.format_version - && format_version.is_unstable() - && WARNED_ON_UNSTABLE_API - .compare_exchange( - false, - true, - std::sync::atomic::Ordering::Relaxed, - std::sync::atomic::Ordering::Relaxed, - ) - .is_ok() - { - warn!( - "You have requested an unstable format version. Files written with this format version may not be readable in the future! This is a development feature and should only be used for experimentation and never for production data." - ); - } - Self { - writer: object_writer, - schema: None, - column_writers: Vec::new(), - column_metadata: Vec::new(), - num_columns: 0, - rows_written: 0, - field_rows_written: Vec::new(), - field_id_to_column_indices: Vec::new(), - global_buffers: Vec::new(), - schema_metadata: HashMap::new(), - page_spill: None, - options, - } - } - - /// Spill page metadata to a sidecar file instead of accumulating in memory. - /// - /// This can dramatically reduce memory usage when many writers are open - /// concurrently (e.g. IVF shuffle with thousands of partition writers). - /// The sidecar file is created lazily on the first page write. The caller - /// is responsible for cleaning up `path` (e.g. by placing it in a temp - /// directory that is removed via RAII). - pub fn with_page_metadata_spill(mut self, object_store: Arc, path: Path) -> Self { - self.page_spill = Some(PageSpillState::Pending(object_store, path)); - self - } - - /// Write a series of record batches to a new file - /// - /// Returns the number of rows written - pub async fn create_file_with_batches( - store: &ObjectStore, - path: &Path, - schema: lance_core::datatypes::Schema, - batches: impl Iterator + Send, - options: FileWriterOptions, - ) -> Result { - let writer = store.create(path).await?; - let mut writer = Self::try_new(writer, schema, options)?; - for batch in batches { - writer.write_batch(&batch).await?; - } - Ok(writer.finish().await?.num_rows as usize) - } - - async fn do_write_buffer(writer: &mut (impl AsyncWrite + Unpin), buf: &[u8]) -> Result<()> { - writer.write_all(buf).await?; - let pad_bytes = pad_bytes::(buf.len()); - writer.write_all(&PAD_BUFFER[..pad_bytes]).await?; - Ok(()) - } - - /// Returns the format version that will be used when writing the file - pub fn version(&self) -> LanceFileVersion { - self.options.format_version.unwrap_or_default() - } - - async fn write_page(&mut self, encoded_page: EncodedPage) -> Result<()> { - let buffers = encoded_page.data; - let mut buffer_offsets = Vec::with_capacity(buffers.len()); - let mut buffer_sizes = Vec::with_capacity(buffers.len()); - for buffer in buffers { - buffer_offsets.push(self.writer.tell().await? as u64); - buffer_sizes.push(buffer.len() as u64); - Self::do_write_buffer(&mut self.writer, &buffer).await?; - } - let encoded_encoding = match encoded_page.description { - PageEncoding::Legacy(array_encoding) => Any::from_msg(&array_encoding)?.encode_to_vec(), - PageEncoding::Structural(page_layout) => Any::from_msg(&page_layout)?.encode_to_vec(), - }; - let page = pbfile::column_metadata::Page { - buffer_offsets, - buffer_sizes, - encoding: Some(pbfile::Encoding { - location: Some(pbfile::encoding::Location::Direct(DirectEncoding { - encoding: encoded_encoding, - })), - }), - length: encoded_page.num_rows, - priority: encoded_page.row_number, - }; - let col_idx = encoded_page.column_idx as usize; - if matches!(&self.page_spill, Some(PageSpillState::Pending(..))) { - let Some(PageSpillState::Pending(store, path)) = self.page_spill.take() else { - unreachable!() - }; - self.page_spill = Some(PageSpillState::Active( - PageMetadataSpill::new(store, path, self.num_columns as usize).await?, - )); - } - match &mut self.page_spill { - Some(PageSpillState::Active(spill)) => spill.append_page(col_idx, &page).await?, - None => self.column_metadata[col_idx].pages.push(page), - Some(PageSpillState::Pending(..)) => unreachable!(), - } - Ok(()) - } - - #[instrument(skip_all, level = "debug")] - async fn write_pages(&mut self, mut encoding_tasks: FuturesOrdered) -> Result<()> { - // As soon as an encoding task is done we write it. There is no parallelism - // needed here because "writing" is really just submitting the buffer to the - // underlying write scheduler (either the OS or object_store's scheduler for - // cloud writes). The only time we might truly await on write_page is if the - // scheduler's write queue is full. - // - // Also, there is no point in trying to make write_page parallel anyways - // because we wouldn't want buffers getting mixed up across pages. - while let Some(encoding_task) = encoding_tasks.next().await { - let encoded_page = encoding_task?; - self.write_page(encoded_page).await?; - } - // It's important to flush here, we don't know when the next batch will arrive - // and the underlying cloud store could have writes in progress that won't advance - // until we interact with the writer again. These in-progress writes will time out - // if we don't flush. - self.writer.flush().await?; - Ok(()) - } - - /// Schedule batches of data to be written to the file + /// Spill page metadata to a sidecar file. + pub fn with_page_metadata_spill(self, object_store: Arc, path: Path) -> Self { + match self { + Self::V2_0(writer) => Self::V2_0(Box::new( + (*writer).with_page_metadata_spill(object_store, path), + )), + Self::V2_1(writer) => Self::V2_1(Box::new( + (*writer).with_page_metadata_spill(object_store, path), + )), + Self::V2_2(writer) => Self::V2_2(Box::new( + (*writer).with_page_metadata_spill(object_store, path), + )), + Self::V2_3(writer) => Self::V2_3(Box::new( + (*writer).with_page_metadata_spill(object_store, path), + )), + } + } + + /// Schedule batches of data to be written to the file. pub async fn write_batches( &mut self, batches: impl Iterator, @@ -407,2056 +153,127 @@ impl FileWriter { Ok(()) } - fn verify_field_nullability(arr: &ArrayData, field: &Field) -> Result<()> { - if !field.nullable && arr.null_count() > 0 { - return Err(Error::invalid_input(format!( - "The field `{}` contained null values even though the field is marked non-null in the schema", - field.name - ))); - } - - for (child_field, child_arr) in field.children.iter().zip(arr.child_data()) { - Self::verify_field_nullability(child_arr, child_field)?; - } - - Ok(()) - } - - fn verify_nullability_constraints(&self, batch: &RecordBatch) -> Result<()> { - for (col, field) in batch - .columns() - .iter() - .zip(self.schema.as_ref().unwrap().fields.iter()) - { - Self::verify_field_nullability(&col.to_data(), field)?; - } - Ok(()) - } - - fn initialize(&mut self, mut schema: LanceSchema) -> Result<()> { - let cache_bytes_per_column = if let Some(data_cache_bytes) = self.options.data_cache_bytes { - data_cache_bytes / schema.fields.len() as u64 - } else { - 8 * 1024 * 1024 - }; - - let max_page_bytes = self.options.max_page_bytes.unwrap_or_else(|| { - std::env::var(ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES) - .map(|s| { - s.parse::().unwrap_or_else(|e| { - warn!( - "Failed to parse {}: {}, using default", - ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, e - ); - MAX_PAGE_BYTES as u64 - }) - }) - .unwrap_or(MAX_PAGE_BYTES as u64) - }); - - schema.validate()?; - - let keep_original_array = self.options.keep_original_array.unwrap_or(false); - let encoding_strategy = self.options.encoding_strategy.clone().unwrap_or_else(|| { - let version = self.version(); - default_encoding_strategy(version).into() - }); - - let encoding_options = EncodingOptions { - cache_bytes_per_column, - max_page_bytes, - keep_original_array, - buffer_alignment: PAGE_BUFFER_ALIGNMENT as u64, - version: self.version(), - }; - let encoder = - BatchEncoder::try_new(&schema, encoding_strategy.as_ref(), &encoding_options)?; - self.num_columns = encoder.num_columns(); - - self.field_rows_written = vec![0; encoder.field_encoders.len()]; - self.column_writers = encoder.field_encoders; - self.column_metadata = vec![initial_column_metadata(); self.num_columns as usize]; - self.field_id_to_column_indices = encoder.field_id_to_column_index; - self.schema_metadata - .extend(std::mem::take(&mut schema.metadata)); - self.schema = Some(schema); - Ok(()) - } - - fn ensure_initialized(&mut self, batch: &RecordBatch) -> Result<&LanceSchema> { - if self.schema.is_none() { - let schema = LanceSchema::try_from(batch.schema().as_ref())?; - self.initialize(schema)?; - } - Ok(self.schema.as_ref().unwrap()) - } - - #[instrument(skip_all, level = "debug")] - fn encode_batch( - &mut self, - batch: &RecordBatch, - external_buffers: &mut OutOfLineBuffers, - ) -> Result>> { - let field_arrays = self - .schema - .as_ref() - .unwrap() - .fields - .iter() - .enumerate() - .map(|(field_idx, field)| { - let array = - batch - .column_by_name(&field.name) - .ok_or(Error::invalid_input_source( - format!( - "Cannot write batch. The batch was missing the column `{}`", - field.name - ) - .into(), - ))?; - Ok((field_idx, array.clone())) - }) - .collect::>>()?; - self.encode_columns(&field_arrays, external_buffers) - } - - // Encode a set of `(field index, array)` pairs, each advancing only its own - // column. Each task captures its field's current row offset at encode time, - // so `advance_columns` must run after this call (never before); the order of - // the returned tasks relative to `write_pages` does not matter. - fn encode_columns( - &mut self, - field_arrays: &[(usize, ArrayRef)], - external_buffers: &mut OutOfLineBuffers, - ) -> Result>> { - // Snapshot the starting row number of each field before borrowing the - // column writers mutably below. - let row_numbers = field_arrays - .iter() - .map(|(field_idx, _)| self.field_rows_written[*field_idx]) - .collect::>(); - field_arrays - .iter() - .zip(row_numbers) - .map(|((field_idx, array), row_number)| { - let repdef = RepDefBuilder::default(); - let num_rows = array.len() as u64; - self.column_writers[*field_idx].maybe_encode( - array.clone(), - external_buffers, - repdef, - row_number, - num_rows, - ) - }) - .collect::>>() - } - - // Advance the per-field row counters after a set of columns has been - // written, keeping `rows_written` (the file's logical length) in sync as the - // longest column. Only the written fields move, so their new totals fold into - // `rows_written` directly without rescanning every field. (`write_batch` - // advances every field uniformly and tracks this inline instead.) - fn advance_columns(&mut self, field_arrays: &[(usize, ArrayRef)]) { - for (field_idx, array) in field_arrays { - let new_total = self.field_rows_written[*field_idx] + array.len() as u64; - self.field_rows_written[*field_idx] = new_total; - self.rows_written = self.rows_written.max(new_total); - } - } - - /// Schedule a batch of data to be written to the file - /// - /// Note: the future returned by this method may complete before the data has been fully - /// flushed to the file (some data may be in the data cache or the I/O cache) + /// Schedule a batch of data to be written to the file. pub async fn write_batch(&mut self, batch: &RecordBatch) -> Result<()> { - debug!( - "write_batch called with {} rows, {} columns, and {} bytes of data", - batch.num_rows(), - batch.num_columns(), - batch.get_array_memory_size() - ); - self.ensure_initialized(batch)?; - self.verify_nullability_constraints(batch)?; - let num_rows = batch.num_rows() as u64; - if num_rows == 0 { - return Ok(()); - } - if num_rows > u32::MAX as u64 { - return Err(Error::invalid_input_source( - "cannot write Lance files with more than 2^32 rows".into(), - )); - } - // First we push each array into its column writer. This may or may not generate enough - // data to trigger an encoding task. We collect any encoding tasks into a queue. - let mut external_buffers = - OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); - let encoding_tasks = self.encode_batch(batch, &mut external_buffers)?; - // Next, write external buffers - for external_buffer in external_buffers.take_buffers() { - Self::do_write_buffer(&mut self.writer, &external_buffer).await?; - } - - let encoding_tasks = encoding_tasks - .into_iter() - .flatten() - .collect::>(); - - // `write_batch` advances every field by the same amount, so the longest - // column simply grows by `num_rows`. Guard against overflowing the row - // counter. - if self.rows_written.checked_add(num_rows).is_none() { - return Err(Error::invalid_input_source(format!("cannot write batch with {} rows because {} rows have already been written and Lance files cannot contain more than 2^64 rows", num_rows, self.rows_written).into())); - } - for field_rows in self.field_rows_written.iter_mut() { - *field_rows += num_rows; + match self { + Self::V2_0(writer) => writer.write_batch(batch).await, + Self::V2_1(writer) => writer.write_batch(batch).await, + Self::V2_2(writer) => writer.write_batch(batch).await, + Self::V2_3(writer) => writer.write_batch(batch).await, } - self.rows_written += num_rows; - - self.write_pages(encoding_tasks).await?; - - Ok(()) } - /// Write a single column, advancing only that column's row counter. - /// - /// Unlike [`write_batch`](Self::write_batch), which advances every column - /// from a single shared row counter, this method advances one column - /// independently. Used across calls it produces a single file whose columns - /// may have different item counts. - /// - /// `column_index` refers to a top-level field in the writer's schema (the - /// same order as the schema's fields); a nested child cannot be targeted on - /// its own. Because each call writes the whole field from a single array, the - /// children of a struct field always advance together and stay equal-length; - /// only different top-level fields can diverge in length. A column may be - /// written across multiple calls; its values are appended. A field that is - /// never written ends up as a zero-length column. The writer must have been - /// created with an explicit schema (via [`try_new`](Self::try_new)); a lazy - /// schema cannot be inferred here because individual calls need not cover - /// every field. - /// - /// ``` - /// # use arrow_array::{ArrayRef, Int32Array}; - /// # use std::sync::Arc; - /// # use lance_file::writer::FileWriter; - /// # async fn example(writer: &mut FileWriter) -> lance_core::Result<()> { - /// // Field 0 gets three values, field 1 gets one — a non-rectangular file. - /// writer.write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3]))).await?; - /// writer.write_column(1, Arc::new(Int32Array::from(vec![10]))).await?; - /// # Ok(()) - /// # } - /// ``` + /// Write one top-level column. pub async fn write_column(&mut self, column_index: usize, array: ArrayRef) -> Result<()> { - let schema = self.schema.as_ref().ok_or_else(|| { - Error::invalid_input_source( - "write_column requires the writer to be created with an explicit schema".into(), - ) - })?; - let field = schema.fields.get(column_index).ok_or_else(|| { - Error::invalid_input_source( - format!( - "write_column: field index {} is out of bounds (schema has {} fields)", - column_index, - schema.fields.len() - ) - .into(), - ) - })?; - if array.len() as u64 > u32::MAX as u64 { - return Err(Error::invalid_input_source( - "cannot write Lance files with more than 2^32 rows".into(), - )); - } - Self::verify_field_nullability(&array.to_data(), field)?; - - // A never-advanced field simply remains a zero-length column, which the - // encoders handle at `finish` time. - if array.is_empty() { - return Ok(()); - } - - let columns = [(column_index, array)]; - let mut external_buffers = - OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); - let encoding_tasks = self.encode_columns(&columns, &mut external_buffers)?; - for external_buffer in external_buffers.take_buffers() { - Self::do_write_buffer(&mut self.writer, &external_buffer).await?; + match self { + Self::V2_0(writer) => writer.write_column(column_index, array).await, + Self::V2_1(writer) => writer.write_column(column_index, array).await, + Self::V2_2(writer) => writer.write_column(column_index, array).await, + Self::V2_3(writer) => writer.write_column(column_index, array).await, } - let encoding_tasks = encoding_tasks - .into_iter() - .flatten() - .collect::>(); - - self.advance_columns(&columns); - self.write_pages(encoding_tasks).await?; - Ok(()) - } - - async fn write_column_metadata( - &mut self, - metadata: pbfile::ColumnMetadata, - ) -> Result<(u64, u64)> { - let metadata_bytes = metadata.encode_to_vec(); - let position = self.writer.tell().await? as u64; - let len = metadata_bytes.len() as u64; - self.writer.write_all(&metadata_bytes).await?; - Ok((position, len)) } - async fn write_column_metadatas(&mut self) -> Result> { - let metadatas = std::mem::take(&mut self.column_metadata); - - // If spilling, finalize the spill writer and reopen for reading. - // The spill file itself is cleaned up by the caller (it lives in a - // temp directory managed by the caller's RAII guard). - let spill_state = self.page_spill.take(); - let (spill_chunks, spill_reader) = - if let Some(PageSpillState::Active(mut spill)) = spill_state { - spill.shutdown_writer().await?; - let reader = spill.object_store.open(&spill.path).await?; - let chunks = std::mem::take(&mut spill.column_chunks); - (chunks, Some(reader)) - } else { - (Vec::new(), None) - }; - - let mut metadata_positions = Vec::with_capacity(metadatas.len()); - for (col_idx, mut metadata) in metadatas.into_iter().enumerate() { - if let Some(reader) = &spill_reader { - let mut pages = Vec::new(); - for &(offset, len) in &spill_chunks[col_idx] { - let data = reader - .get_range(offset as usize..(offset as usize + len as usize)) - .await - .map_err(|e| Error::io_source(Box::new(e)))?; - pages.extend(decode_spilled_chunk(&data)?); - } - metadata.pages = pages; - } - metadata_positions.push(self.write_column_metadata(metadata).await?); + /// Append a buffer whose page or column metadata is supplied externally. + pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> { + match self { + Self::V2_0(writer) => writer.write_external_buffer(bytes).await, + Self::V2_1(writer) => writer.write_external_buffer(bytes).await, + Self::V2_2(writer) => writer.write_external_buffer(bytes).await, + Self::V2_3(writer) => writer.write_external_buffer(bytes).await, } - - Ok(metadata_positions) } - fn make_file_descriptor( - schema: &lance_core::datatypes::Schema, - num_rows: u64, - ) -> Result { - let fields_with_meta = FieldsWithMeta::from(schema); - Ok(pb::FileDescriptor { - schema: Some(pb::Schema { - fields: fields_with_meta.fields.0, - metadata: fields_with_meta.metadata, - }), - length: num_rows, - }) - } - - async fn write_global_buffers(&mut self) -> Result> { - let schema = self.schema.as_mut().ok_or(Error::invalid_input("No schema provided on writer open and no data provided. Schema is unknown and file cannot be created"))?; - schema.metadata = std::mem::take(&mut self.schema_metadata); - // Use descriptor layout for blob v2 fields in the footer to avoid exposing logical child fields. - schema - .fields - .iter_mut() - .for_each(|f| f.unload_blobs_recursive()); - - let file_descriptor = Self::make_file_descriptor(schema, self.rows_written)?; - let file_descriptor_bytes = file_descriptor.encode_to_vec(); - let file_descriptor_len = file_descriptor_bytes.len() as u64; - let file_descriptor_position = self.writer.tell().await? as u64; - self.writer.write_all(&file_descriptor_bytes).await?; - let mut gbo_table = Vec::with_capacity(1 + self.global_buffers.len()); - gbo_table.push((file_descriptor_position, file_descriptor_len)); - gbo_table.append(&mut self.global_buffers); - Ok(gbo_table) - } - - /// Add a metadata entry to the schema - /// - /// This method is useful because sometimes the metadata is not known until after the - /// data has been written. This method allows you to alter the schema metadata. It - /// must be called before `finish` is called. + /// Add a metadata entry to the schema. pub fn add_schema_metadata(&mut self, key: impl Into, value: impl Into) { - self.schema_metadata.insert(key.into(), value.into()); + let key = key.into(); + let value = value.into(); + match self { + Self::V2_0(writer) => writer.add_schema_metadata(key, value), + Self::V2_1(writer) => writer.add_schema_metadata(key, value), + Self::V2_2(writer) => writer.add_schema_metadata(key, value), + Self::V2_3(writer) => writer.add_schema_metadata(key, value), + } } - /// Prepare the writer when column data and metadata were produced externally. - /// - /// This is useful for flows that copy already-encoded pages (e.g., binary copy - /// during compaction) where the column buffers have been written directly and we - /// only need to write the footer and schema metadata. The provided - /// `column_metadata` must describe the buffers already persisted by the - /// underlying `ObjectWriter`, and `rows_written` should reflect the total number - /// of rows in those buffers. - pub fn initialize_with_external_metadata( + /// Prepare a writer from encoded columns whose buffers were produced externally. + pub fn initialize_with_external_columns( &mut self, - schema: lance_core::datatypes::Schema, - column_metadata: Vec, + schema: Schema, + columns: &[Arc], rows_written: u64, - ) { - self.schema = Some(schema); - self.num_columns = column_metadata.len() as u32; - self.column_metadata = column_metadata; - self.rows_written = rows_written; - } - - /// Adds a global buffer to the file - /// - /// The global buffer can contain any arbitrary bytes. It will be written to the disk - /// immediately. This method returns the index of the global buffer (this will always - /// start at 1 and increment by 1 each time this method is called) - pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result { - let position = self.writer.tell().await? as u64; - let len = buffer.len() as u64; - Self::do_write_buffer(&mut self.writer, &buffer).await?; - self.global_buffers.push((position, len)); - Ok(self.global_buffers.len() as u32) - } - - async fn finish_writers(&mut self) -> Result<()> { - let mut col_idx = 0; - for mut writer in std::mem::take(&mut self.column_writers) { - let mut external_buffers = - OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); - let columns = writer.finish(&mut external_buffers).await?; - for buffer in external_buffers.take_buffers() { - self.writer.write_all(&buffer).await?; + ) -> Result<()> { + let column_metadata = columns + .iter() + .map(|column| column_info_to_metadata(column)) + .collect::>>()?; + match self { + Self::V2_0(writer) => { + writer.initialize_with_external_metadata(schema, column_metadata, rows_written) } - debug_assert_eq!( - columns.len(), - writer.num_columns() as usize, - "Expected {} columns from column at index {} and got {}", - writer.num_columns(), - col_idx, - columns.len() - ); - for column in columns { - for page in column.final_pages { - self.write_page(page).await?; - } - let column_metadata = &mut self.column_metadata[col_idx]; - let mut buffer_pos = self.writer.tell().await? as u64; - for buffer in column.column_buffers { - column_metadata.buffer_offsets.push(buffer_pos); - let mut size = 0; - Self::do_write_buffer(&mut self.writer, &buffer).await?; - size += buffer.len() as u64; - buffer_pos += size; - column_metadata.buffer_sizes.push(size); - } - let encoded_encoding = Any::from_msg(&column.encoding)?.encode_to_vec(); - column_metadata.encoding = Some(pbfile::Encoding { - location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { - encoding: encoded_encoding, - })), - }); - col_idx += 1; + Self::V2_1(writer) => { + writer.initialize_with_external_metadata(schema, column_metadata, rows_written) + } + Self::V2_2(writer) => { + writer.initialize_with_external_metadata(schema, column_metadata, rows_written) + } + Self::V2_3(writer) => { + writer.initialize_with_external_metadata(schema, column_metadata, rows_written) } - } - if col_idx != self.column_metadata.len() { - panic!( - "Column writers finished with {} columns but we expected {}", - col_idx, - self.column_metadata.len() - ); } Ok(()) } - /// Converts self.version (which is a mix of "software version" and - /// "format version" into a format version) - fn version_to_numbers(&self) -> (u16, u16) { - let version = self.options.format_version.unwrap_or_default(); - match version.resolve() { - LanceFileVersion::V2_0 => (0, 3), - LanceFileVersion::V2_1 => (2, 1), - LanceFileVersion::V2_2 => (2, 2), - LanceFileVersion::V2_3 => (2, 3), - _ => panic!("Unsupported version: {}", version), + /// Add an arbitrary global buffer and return its one-based index. + pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result { + match self { + Self::V2_0(writer) => writer.add_global_buffer(buffer).await, + Self::V2_1(writer) => writer.add_global_buffer(buffer).await, + Self::V2_2(writer) => writer.add_global_buffer(buffer).await, + Self::V2_3(writer) => writer.add_global_buffer(buffer).await, } } - /// Finishes writing the file - /// - /// This method will wait until all data has been flushed to the file. Then it - /// will write the file metadata and the footer. It will not return until all - /// data has been flushed and the file has been closed. - /// - /// Returns a summary of the completed file write. + /// Finish the file and close its object writer. pub async fn finish(&mut self) -> Result { - // 1. flush any remaining data and write out those pages - let mut external_buffers = - OutOfLineBuffers::new(self.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); - let encoding_tasks = self - .column_writers - .iter_mut() - .map(|writer| writer.flush(&mut external_buffers)) - .collect::>>()?; - for external_buffer in external_buffers.take_buffers() { - Self::do_write_buffer(&mut self.writer, &external_buffer).await?; - } - let encoding_tasks = encoding_tasks - .into_iter() - .flatten() - .collect::>(); - self.write_pages(encoding_tasks).await?; - - if !self.column_writers.is_empty() { - self.finish_writers().await?; + match self { + Self::V2_0(writer) => writer.finish().await, + Self::V2_1(writer) => writer.finish().await, + Self::V2_2(writer) => writer.finish().await, + Self::V2_3(writer) => writer.finish().await, } - - // 3. write global buffers (we write the schema here) - let global_buffer_offsets = self.write_global_buffers().await?; - let num_global_buffers = global_buffer_offsets.len() as u32; - - // 4. write the column metadatas - let column_metadata_start = self.writer.tell().await? as u64; - let metadata_positions = self.write_column_metadatas().await?; - - // 5. write the column metadata offset table - let cmo_table_start = self.writer.tell().await? as u64; - for (meta_pos, meta_len) in metadata_positions { - self.writer.write_u64_le(meta_pos).await?; - self.writer.write_u64_le(meta_len).await?; - } - - // 6. write global buffers offset table - let gbo_table_start = self.writer.tell().await? as u64; - for (gbo_pos, gbo_len) in global_buffer_offsets { - self.writer.write_u64_le(gbo_pos).await?; - self.writer.write_u64_le(gbo_len).await?; - } - - let (major, minor) = self.version_to_numbers(); - // 7. write the footer - self.writer.write_u64_le(column_metadata_start).await?; - self.writer.write_u64_le(cmo_table_start).await?; - self.writer.write_u64_le(gbo_table_start).await?; - self.writer.write_u32_le(num_global_buffers).await?; - self.writer.write_u32_le(self.num_columns).await?; - self.writer.write_u16_le(major).await?; - self.writer.write_u16_le(minor).await?; - self.writer.write_all(MAGIC).await?; - - // 7. close the writer - let write_result = Writer::shutdown(self.writer.as_mut()).await?; - - Ok(FileWriteSummary { - num_rows: self.rows_written, - size_bytes: write_result.size as u64, - }) } + /// Abandon the file write. pub async fn abort(&mut self) { - // For multipart uploads, ObjectWriter's Drop impl will abort - // the upload when the writer is dropped. + match self { + Self::V2_0(writer) => writer.abort().await, + Self::V2_1(writer) => writer.abort().await, + Self::V2_2(writer) => writer.abort().await, + Self::V2_3(writer) => writer.abort().await, + } } + /// Return the current object-writer position. pub async fn tell(&mut self) -> Result { - Ok(self.writer.tell().await? as u64) + match self { + Self::V2_0(writer) => writer.tell().await, + Self::V2_1(writer) => writer.tell().await, + Self::V2_2(writer) => writer.tell().await, + Self::V2_3(writer) => writer.tell().await, + } } + /// Return the field-id to physical-column mapping. pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] { - &self.field_id_to_column_indices - } -} - -/// Utility trait for converting EncodedBatch to Bytes using the -/// lance file format -pub trait EncodedBatchWriteExt { - /// Serializes into a lance file, including the schema - fn try_to_self_described_lance(&self, version: LanceFileVersion) -> Result; - /// Serializes into a lance file, without the schema. - /// - /// The schema must be provided to deserialize the buffer - fn try_to_mini_lance(&self, version: LanceFileVersion) -> Result; -} - -// Creates a lance footer and appends it to the encoded data -// -// The logic here is very similar to logic in the FileWriter except we -// are using BufMut (put_xyz) instead of AsyncWrite (write_xyz). -fn concat_lance_footer( - batch: &EncodedBatch, - write_schema: bool, - version: LanceFileVersion, -) -> Result { - // Estimating 1MiB for file footer - let mut data = BytesMut::with_capacity(batch.data.len() + 1024 * 1024); - data.put(batch.data.clone()); - // write global buffers (we write the schema here) - let global_buffers = if write_schema { - let schema_start = data.len() as u64; - let lance_schema = lance_core::datatypes::Schema::try_from(batch.schema.as_ref())?; - let descriptor = FileWriter::make_file_descriptor(&lance_schema, batch.num_rows)?; - let descriptor_bytes = descriptor.encode_to_vec(); - let descriptor_len = descriptor_bytes.len() as u64; - data.put(descriptor_bytes.as_slice()); - - vec![(schema_start, descriptor_len)] - } else { - vec![] - }; - let col_metadata_start = data.len() as u64; - - let mut col_metadata_positions = Vec::new(); - // Write column metadata - for col in &batch.page_table { - let position = data.len() as u64; - let pages = col - .page_infos - .iter() - .map(|page_info| { - let encoded_encoding = match &page_info.encoding { - PageEncoding::Legacy(array_encoding) => { - Any::from_msg(array_encoding)?.encode_to_vec() - } - PageEncoding::Structural(page_layout) => { - Any::from_msg(page_layout)?.encode_to_vec() - } - }; - let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = page_info - .buffer_offsets_and_sizes - .as_ref() - .iter() - .cloned() - .unzip(); - Ok(pbfile::column_metadata::Page { - buffer_offsets, - buffer_sizes, - encoding: Some(pbfile::Encoding { - location: Some(pbfile::encoding::Location::Direct(DirectEncoding { - encoding: encoded_encoding, - })), - }), - length: page_info.num_rows, - priority: page_info.priority, - }) - }) - .collect::>>()?; - let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = - col.buffer_offsets_and_sizes.iter().cloned().unzip(); - let encoded_col_encoding = Any::from_msg(&col.encoding)?.encode_to_vec(); - let column = pbfile::ColumnMetadata { - pages, - buffer_offsets, - buffer_sizes, - encoding: Some(pbfile::Encoding { - location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { - encoding: encoded_col_encoding, - })), - }), - }; - let column_bytes = column.encode_to_vec(); - col_metadata_positions.push((position, column_bytes.len() as u64)); - data.put(column_bytes.as_slice()); - } - // Write column metadata offsets table - let cmo_table_start = data.len() as u64; - for (meta_pos, meta_len) in col_metadata_positions { - data.put_u64_le(meta_pos); - data.put_u64_le(meta_len); - } - // Write global buffers offsets table - let gbo_table_start = data.len() as u64; - let num_global_buffers = global_buffers.len() as u32; - for (gbo_pos, gbo_len) in global_buffers { - data.put_u64_le(gbo_pos); - data.put_u64_le(gbo_len); - } - - let (major, minor) = version.to_numbers(); - - // write the footer - data.put_u64_le(col_metadata_start); - data.put_u64_le(cmo_table_start); - data.put_u64_le(gbo_table_start); - data.put_u32_le(num_global_buffers); - data.put_u32_le(batch.page_table.len() as u32); - data.put_u16_le(major as u16); - data.put_u16_le(minor as u16); - data.put(MAGIC.as_slice()); - - Ok(data.freeze()) -} - -impl EncodedBatchWriteExt for EncodedBatch { - fn try_to_self_described_lance(&self, version: LanceFileVersion) -> Result { - concat_lance_footer(self, true, version) - } - - fn try_to_mini_lance(&self, version: LanceFileVersion) -> Result { - concat_lance_footer(self, false, version) + match self { + Self::V2_0(writer) => writer.field_id_to_column_indices(), + Self::V2_1(writer) => writer.field_id_to_column_indices(), + Self::V2_2(writer) => writer.field_id_to_column_indices(), + Self::V2_3(writer) => writer.field_id_to_column_indices(), + } } } #[cfg(test)] -mod tests { - use std::collections::HashMap; - use std::sync::Arc; - - use crate::reader::{FileReader, FileReaderOptions, ReaderProjection, describe_encoding}; - use crate::testing::FsFixture; - use crate::writer::{ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, FileWriter, FileWriterOptions}; - use arrow_array::builder::{Float32Builder, Int32Builder}; - use arrow_array::{ArrayRef, Int32Array, RecordBatch, UInt64Array}; - use arrow_array::{RecordBatchReader, StringArray, types::Float64Type}; - use arrow_schema::{DataType, Field, Field as ArrowField, Schema, Schema as ArrowSchema}; - use lance_core::cache::LanceCache; - use lance_core::datatypes::Schema as LanceSchema; - use lance_core::utils::tempfile::TempObjFile; - use lance_datagen::{BatchCount, RowCount, array, gen_batch}; - use lance_encoding::compression_config::{CompressionFieldParams, CompressionParams}; - use lance_encoding::decoder::DecoderPlugins; - use lance_encoding::version::LanceFileVersion; - use lance_io::object_store::ObjectStore; - use lance_io::utils::CachedFileSize; - use rstest::rstest; - - #[tokio::test] - async fn test_basic_write() { - let tmp_path = TempObjFile::default(); - let obj_store = Arc::new(ObjectStore::local()); - - let reader = gen_batch() - .col("score", array::rand::()) - .into_reader_rows(RowCount::from(1000), BatchCount::from(10)); - - let writer = obj_store.create(&tmp_path).await.unwrap(); - - let lance_schema = - lance_core::datatypes::Schema::try_from(reader.schema().as_ref()).unwrap(); - - let mut file_writer = - FileWriter::try_new(writer, lance_schema, FileWriterOptions::default()).unwrap(); - - for batch in reader { - file_writer.write_batch(&batch.unwrap()).await.unwrap(); - } - file_writer.add_schema_metadata("foo", "bar"); - file_writer.finish().await.unwrap(); - // Tests asserting the contents of the written file are in reader.rs - } - - #[tokio::test] - async fn test_write_empty() { - let tmp_path = TempObjFile::default(); - let obj_store = Arc::new(ObjectStore::local()); - - let reader = gen_batch() - .col("score", array::rand::()) - .into_reader_rows(RowCount::from(0), BatchCount::from(0)); - - let writer = obj_store.create(&tmp_path).await.unwrap(); - - let lance_schema = - lance_core::datatypes::Schema::try_from(reader.schema().as_ref()).unwrap(); - - let mut file_writer = - FileWriter::try_new(writer, lance_schema, FileWriterOptions::default()).unwrap(); - - for batch in reader { - file_writer.write_batch(&batch.unwrap()).await.unwrap(); - } - file_writer.add_schema_metadata("foo", "bar"); - file_writer.finish().await.unwrap(); - } - - // Read a single column back at an explicit range/index set, returning its - // `Int32` values. Reading one column (or an equal-length group) at a time is - // how unequal-length files are consumed: a full scan across columns of - // differing lengths cannot form a single rectangular batch. - async fn read_int32_column( - reader: &FileReader, - schema: &LanceSchema, - version: LanceFileVersion, - name: &str, - params: lance_io::ReadBatchParams, - ) -> Vec> { - use futures::TryStreamExt; - use lance_encoding::decoder::FilterExpression; - - let projection = ReaderProjection::from_column_names(version, schema, &[name]).unwrap(); - let batches: Vec = reader - .read_stream_projected(params, 1024, 16, projection, FilterExpression::no_filter()) - .await - .unwrap() - .try_collect() - .await - .unwrap(); - batches - .iter() - .flat_map(|b| { - b.column(0) - .as_any() - .downcast_ref::() - .unwrap() - .iter() - .collect::>() - }) - .collect() - } - - /// A single file may hold columns of differing item counts, written by - /// advancing each column's row counter independently (no shared global - /// counter). - #[rstest] - #[tokio::test] - async fn test_write_columns_unequal_lengths( - #[values(LanceFileVersion::V2_0, LanceFileVersion::V2_1)] version: LanceFileVersion, - ) { - use lance_io::ReadBatchParams; - - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("a", DataType::Int32, true), - ArrowField::new("b", DataType::Int32, true), - ArrowField::new("c", DataType::Int32, true), - ])); - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - - let fs = FsFixture::default(); - let options = FileWriterOptions { - format_version: Some(version), - ..Default::default() - }; - let mut writer = FileWriter::try_new( - fs.object_store.create(&fs.tmp_path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - - // Field "a" gets 5 values across two calls (appending), field "b" gets a - // single value, and field "c" is never written (a zero-length column). - let a1: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); - let b: ArrayRef = Arc::new(Int32Array::from(vec![10])); - writer.write_column(0, a1).await.unwrap(); - writer.write_column(1, b).await.unwrap(); - let a2: ArrayRef = Arc::new(Int32Array::from(vec![4, 5])); - writer.write_column(0, a2).await.unwrap(); - // An empty array is a no-op whether or not the field already has rows: - // field "a" keeps its 5 rows, field "c" stays a zero-length column. - let empty: ArrayRef = Arc::new(Int32Array::from(Vec::::new())); - writer.write_column(0, empty.clone()).await.unwrap(); - writer.write_column(2, empty).await.unwrap(); - - let summary = writer.finish().await.unwrap(); - // The file's logical length is the longest column. - assert_eq!(summary.num_rows, 5); - - let file_scheduler = fs - .scheduler - .open_file(&fs.tmp_path, &CachedFileSize::unknown()) - .await - .unwrap(); - let reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - // Per-column row counts are recorded in / derivable from file metadata. - assert_eq!(reader.num_rows(), 5); - assert_eq!(reader.column_num_rows(0).unwrap(), 5); - assert_eq!(reader.column_num_rows(1).unwrap(), 1); - assert_eq!(reader.column_num_rows(2).unwrap(), 0); - assert!(reader.column_num_rows(3).is_err()); - - // Each column reads back independently at its own length. - assert_eq!( - read_int32_column( - &reader, - &lance_schema, - version, - "a", - ReadBatchParams::Range(0..5) - ) - .await, - vec![Some(1), Some(2), Some(3), Some(4), Some(5)], - ); - assert_eq!( - read_int32_column( - &reader, - &lance_schema, - version, - "b", - ReadBatchParams::Range(0..1) - ) - .await, - vec![Some(10)], - ); - - // Random access by position within the longer column returns the right - // value even though other columns are shorter. (The take path requires - // strictly increasing indices.) - assert_eq!( - read_int32_column( - &reader, - &lance_schema, - version, - "a", - ReadBatchParams::Indices(arrow_array::UInt32Array::from(vec![0, 2, 4])), - ) - .await, - vec![Some(1), Some(3), Some(5)], - ); - } - - /// Reading an unequal-length file: - /// - a projection whose columns are equal length full-scans normally; - /// - a full scan across columns of differing length is rejected up front, - /// before any batch is produced (even though a prefix would be rectangular); - /// - a bounded read is valid as long as every projected column covers it; - /// - a single-column `RangeFull` resolves to that column's own length, not - /// the file's (maximum) length. - #[rstest] - #[tokio::test] - async fn test_read_unequal_length_projection( - #[values(LanceFileVersion::V2_0, LanceFileVersion::V2_1)] version: LanceFileVersion, - ) { - use futures::TryStreamExt; - use lance_encoding::decoder::FilterExpression; - use lance_io::ReadBatchParams; - - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("a", DataType::Int32, true), - ArrowField::new("b", DataType::Int32, true), - ArrowField::new("c", DataType::Int32, true), - ])); - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - let fs = FsFixture::default(); - let options = FileWriterOptions { - format_version: Some(version), - ..Default::default() - }; - let mut writer = FileWriter::try_new( - fs.object_store.create(&fs.tmp_path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - // "a" and "b" are equal length (5); "c" is shorter (1). - writer - .write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5]))) - .await - .unwrap(); - writer - .write_column(1, Arc::new(Int32Array::from(vec![6, 7, 8, 9, 10]))) - .await - .unwrap(); - writer - .write_column(2, Arc::new(Int32Array::from(vec![100]))) - .await - .unwrap(); - writer.finish().await.unwrap(); - - let file_scheduler = fs - .scheduler - .open_file(&fs.tmp_path, &CachedFileSize::unknown()) - .await - .unwrap(); - let reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - let read = |names: &'static [&'static str], params: ReadBatchParams| { - let projection = - ReaderProjection::from_column_names(version, &lance_schema, names).unwrap(); - async { - match reader - .read_stream_projected( - params, - 1024, - 16, - projection, - FilterExpression::no_filter(), - ) - .await - { - Ok(stream) => stream.try_collect::>().await, - Err(e) => Err(e), - } - } - }; - let col_values = |batches: &[RecordBatch], idx: usize| -> Vec> { - batches - .iter() - .flat_map(|b| { - b.column(idx) - .as_any() - .downcast_ref::() - .unwrap() - .iter() - .collect::>() - }) - .collect() - }; - - // Equal-length projection [a, b] full-scans into rectangular batches. - let batches = read(&["a", "b"], ReadBatchParams::RangeFull).await.unwrap(); - assert_eq!( - col_values(&batches, 0), - vec![Some(1), Some(2), Some(3), Some(4), Some(5)] - ); - assert_eq!( - col_values(&batches, 1), - vec![Some(6), Some(7), Some(8), Some(9), Some(10)] - ); - - // A mismatched-length projection [a, c] (5 vs 1) is rejected before any - // batch is yielded, regardless of the read params — its columns cannot - // be combined into rectangular batches. The error names each column's - // length so the caller can see which column is the odd one out. - let err = read(&["a", "c"], ReadBatchParams::RangeFull) - .await - .unwrap_err() - .to_string(); - assert!( - err.contains("a=5") && err.contains("c=1"), - "error should name each column's length, got: {err}" - ); - assert!( - read(&["a", "c"], ReadBatchParams::Range(0..1)) - .await - .is_err(), - "even a common-prefix read of unequal-length columns must error" - ); - - // A single-column RangeFull resolves to that column's own length. - let batches = read(&["c"], ReadBatchParams::RangeFull).await.unwrap(); - assert_eq!(col_values(&batches, 0), vec![Some(100)]); - let batches = read(&["a"], ReadBatchParams::RangeFull).await.unwrap(); - assert_eq!( - col_values(&batches, 0), - vec![Some(1), Some(2), Some(3), Some(4), Some(5)] - ); - - // RangeFrom/RangeTo likewise resolve against the projected column's own - // length rather than the file's longest column. - let batches = read(&["a"], ReadBatchParams::RangeFrom(2..)).await.unwrap(); - assert_eq!(col_values(&batches, 0), vec![Some(3), Some(4), Some(5)]); - // RangeFrom on the short column "c" resolves to length 1, not 5. - let batches = read(&["c"], ReadBatchParams::RangeFrom(0..)).await.unwrap(); - assert_eq!(col_values(&batches, 0), vec![Some(100)]); - let batches = read(&["a"], ReadBatchParams::RangeTo(..3)).await.unwrap(); - assert_eq!(col_values(&batches, 0), vec![Some(1), Some(2), Some(3)]); - // A bound past the projected column's length errors. - assert!( - read(&["a"], ReadBatchParams::RangeTo(..6)).await.is_err(), - "RangeTo past the column length must error" - ); - assert!( - read(&["c"], ReadBatchParams::RangeFrom(2..)).await.is_err(), - "RangeFrom past the column length must error" - ); - } - - /// A struct and a list column each map to multiple physical columns, and a - /// list's item column is longer than its top-level row count. The - /// projection-length check must partition `column_indices` by top-level - /// field and use each field's root column, so an ordinary (rectangular) file - /// with nested columns still reads under the new validation path. - #[rstest] - #[tokio::test] - async fn test_read_nested_columns_under_validation( - #[values(LanceFileVersion::V2_0, LanceFileVersion::V2_1)] version: LanceFileVersion, - ) { - use arrow_array::types::Int32Type; - use arrow_array::{ListArray, StructArray}; - use futures::TryStreamExt; - use lance_encoding::decoder::FilterExpression; - use lance_io::ReadBatchParams; - - let struct_type = DataType::Struct( - vec![ - ArrowField::new("x", DataType::Int32, true), - ArrowField::new("y", DataType::Int32, true), - ] - .into(), - ); - let list_type = DataType::List(Arc::new(ArrowField::new("item", DataType::Int32, true))); - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("a", DataType::Int32, true), - ArrowField::new("s", struct_type, true), - ArrowField::new("lst", list_type, true), - ])); - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - - let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); - let s: ArrayRef = Arc::new(StructArray::from(vec![ - ( - Arc::new(ArrowField::new("x", DataType::Int32, true)), - Arc::new(Int32Array::from(vec![10, 20, 30])) as ArrayRef, - ), - ( - Arc::new(ArrowField::new("y", DataType::Int32, true)), - Arc::new(Int32Array::from(vec![11, 21, 31])) as ArrayRef, - ), - ])); - // 3 lists, 6 items: the item column is longer than the top-level rows. - let lst: ArrayRef = Arc::new(ListArray::from_iter_primitive::(vec![ - Some(vec![Some(1), Some(2)]), - Some(vec![Some(3)]), - Some(vec![Some(4), Some(5), Some(6)]), - ])); - let batch = RecordBatch::try_new(arrow_schema.clone(), vec![a, s, lst]).unwrap(); - - let fs = FsFixture::default(); - let options = FileWriterOptions { - format_version: Some(version), - ..Default::default() - }; - let mut writer = FileWriter::try_new( - fs.object_store.create(&fs.tmp_path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - writer.write_batch(&batch).await.unwrap(); - writer.finish().await.unwrap(); - - let file_scheduler = fs - .scheduler - .open_file(&fs.tmp_path, &CachedFileSize::unknown()) - .await - .unwrap(); - let reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - // If `validate_field_length` mispartitioned the physical columns, the - // length check would read the wrong root column (e.g. the list's item - // column, length 6) and spuriously reject this rectangular file. - for names in [&["a", "s", "lst"][..], &["a", "lst"][..], &["a", "s"][..]] { - let projection = - ReaderProjection::from_column_names(version, &lance_schema, names).unwrap(); - let batches: Vec = reader - .read_stream_projected( - ReadBatchParams::RangeFull, - 1024, - 16, - projection, - FilterExpression::no_filter(), - ) - .await - .unwrap() - .try_collect() - .await - .unwrap(); - let total_rows: usize = batches.iter().map(|b| b.num_rows()).sum(); - assert_eq!( - total_rows, 3, - "projection {names:?} should read 3 top-level rows" - ); - } - } - - /// `write_column` rejects invalid inputs at the API boundary with - /// descriptive errors: a writer without an explicit schema, an - /// out-of-bounds field index, and a null written into a non-nullable field. - #[tokio::test] - async fn test_write_column_validation_errors() { - // A lazy-schema writer cannot infer the schema from a single column. - let fs = FsFixture::default(); - let mut lazy_writer = FileWriter::new_lazy( - fs.object_store.create(&fs.tmp_path).await.unwrap(), - FileWriterOptions::default(), - ); - let err = lazy_writer - .write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3]))) - .await - .unwrap_err() - .to_string(); - assert!( - err.contains("explicit schema"), - "expected explicit-schema error, got: {err}" - ); - - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("a", DataType::Int32, false), - ArrowField::new("b", DataType::Int32, true), - ])); - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - - // An out-of-bounds field index is rejected, naming the index and count. - let fs = FsFixture::default(); - let mut writer = FileWriter::try_new( - fs.object_store.create(&fs.tmp_path).await.unwrap(), - lance_schema.clone(), - FileWriterOptions::default(), - ) - .unwrap(); - let err = writer - .write_column(5, Arc::new(Int32Array::from(vec![1]))) - .await - .unwrap_err() - .to_string(); - assert!( - err.contains('5') && err.contains('2'), - "expected out-of-bounds error naming index 5 and 2 fields, got: {err}" - ); - - // A null in a non-nullable field ("a") is rejected. - let err = writer - .write_column(0, Arc::new(Int32Array::from(vec![Some(1), None, Some(3)]))) - .await - .unwrap_err() - .to_string(); - assert!( - err.contains("non-null"), - "expected nullability error, got: {err}" - ); - } - - /// The blocking read path applies the same projection-length validation as - /// the async path: a short single column resolves to its own length, and a - /// mismatched-length projection errors up front. - #[rstest] - #[tokio::test] - async fn test_blocking_read_unequal_length( - #[values(LanceFileVersion::V2_0, LanceFileVersion::V2_1)] version: LanceFileVersion, - ) { - use lance_encoding::decoder::FilterExpression; - use lance_io::ReadBatchParams; - - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("a", DataType::Int32, true), - ArrowField::new("c", DataType::Int32, true), - ])); - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - let fs = FsFixture::default(); - let options = FileWriterOptions { - format_version: Some(version), - ..Default::default() - }; - let mut writer = FileWriter::try_new( - fs.object_store.create(&fs.tmp_path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - writer - .write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5]))) - .await - .unwrap(); - writer - .write_column(1, Arc::new(Int32Array::from(vec![100]))) - .await - .unwrap(); - writer.finish().await.unwrap(); - - let file_scheduler = fs - .scheduler - .open_file(&fs.tmp_path, &CachedFileSize::unknown()) - .await - .unwrap(); - let reader = Arc::new( - FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(), - ); - - // Single short column: RangeFull resolves to its own length (1). - let proj_c = ReaderProjection::from_column_names(version, &lance_schema, &["c"]).unwrap(); - let reader_c = reader.clone(); - let batches = tokio::task::spawn_blocking(move || { - reader_c - .read_stream_projected_blocking( - ReadBatchParams::RangeFull, - 1024, - Some(proj_c), - FilterExpression::no_filter(), - ) - .unwrap() - .collect::, _>>() - .unwrap() - }) - .await - .unwrap(); - let total_rows: usize = batches.iter().map(|b| b.num_rows()).sum(); - assert_eq!(total_rows, 1); - - // A mismatched projection [a, c] errors on the blocking path too. - let proj_ac = - ReaderProjection::from_column_names(version, &lance_schema, &["a", "c"]).unwrap(); - let reader_ac = reader.clone(); - let is_err = tokio::task::spawn_blocking(move || { - reader_ac - .read_stream_projected_blocking( - ReadBatchParams::RangeFull, - 1024, - Some(proj_ac), - FilterExpression::no_filter(), - ) - .is_err() - }) - .await - .unwrap(); - assert!( - is_err, - "blocking full scan across unequal-length columns must error" - ); - } - - /// Files written the ordinary (rectangular) way keep equal column lengths, - /// so the unequal-length support is backwards compatible. - #[tokio::test] - async fn test_write_batch_keeps_equal_lengths() { - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("a", DataType::Int32, true), - ArrowField::new("b", DataType::Int32, true), - ])); - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - - let fs = FsFixture::default(); - let mut writer = FileWriter::try_new( - fs.object_store.create(&fs.tmp_path).await.unwrap(), - lance_schema, - FileWriterOptions::default(), - ) - .unwrap(); - let batch = RecordBatch::try_new( - arrow_schema.clone(), - vec![ - Arc::new(Int32Array::from(vec![1, 2, 3])), - Arc::new(Int32Array::from(vec![4, 5, 6])), - ], - ) - .unwrap(); - writer.write_batch(&batch).await.unwrap(); - let summary = writer.finish().await.unwrap(); - assert_eq!(summary.num_rows, 3); - - let file_scheduler = fs - .scheduler - .open_file(&fs.tmp_path, &CachedFileSize::unknown()) - .await - .unwrap(); - let reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - assert_eq!(reader.column_num_rows(0).unwrap(), 3); - assert_eq!(reader.column_num_rows(1).unwrap(), 3); - } - - #[tokio::test] - async fn test_max_page_bytes_enforced() { - let arrow_field = Field::new("data", DataType::UInt64, false); - let arrow_schema = Schema::new(vec![arrow_field]); - let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); - - // 8MiB - let data: Vec = (0..1_000_000).collect(); - let array = UInt64Array::from(data); - let batch = - RecordBatch::try_new(arrow_schema.clone().into(), vec![Arc::new(array)]).unwrap(); - - let options = FileWriterOptions { - max_page_bytes: Some(1024 * 1024), // 1MB - // This is a 2.0 only test because 2.1+ splits large pages on read instead of write - format_version: Some(LanceFileVersion::V2_0), - ..Default::default() - }; - - let path = TempObjFile::default(); - let object_store = ObjectStore::local(); - let mut writer = FileWriter::try_new( - object_store.create(&path).await.unwrap(), - lance_schema, - options, - ) - .unwrap(); - - writer.write_batch(&batch).await.unwrap(); - writer.finish().await.unwrap(); - - let fs = FsFixture::default(); - let file_scheduler = fs - .scheduler - .open_file(&path, &CachedFileSize::unknown()) - .await - .unwrap(); - let file_reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - let column_meta = file_reader.metadata(); - - let mut total_page_num: u32 = 0; - for (col_idx, col_metadata) in column_meta.column_metadatas.iter().enumerate() { - assert!( - !col_metadata.pages.is_empty(), - "Column {} has no pages", - col_idx - ); - - for (page_idx, page) in col_metadata.pages.iter().enumerate() { - total_page_num += 1; - let total_size: u64 = page.buffer_sizes.iter().sum(); - assert!( - total_size <= 1024 * 1024, - "Column {} Page {} size {} exceeds 1MB limit", - col_idx, - page_idx, - total_size - ); - } - } - - assert_eq!(total_page_num, 8) - } - - #[tokio::test(flavor = "current_thread")] - async fn test_max_page_bytes_env_var() { - let arrow_field = Field::new("data", DataType::UInt64, false); - let arrow_schema = Schema::new(vec![arrow_field]); - let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); - // 4MiB - let data: Vec = (0..500_000).collect(); - let array = UInt64Array::from(data); - let batch = - RecordBatch::try_new(arrow_schema.clone().into(), vec![Arc::new(array)]).unwrap(); - - // 2MiB - unsafe { - std::env::set_var(ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, "2097152"); - } - - let options = FileWriterOptions { - max_page_bytes: None, // enforce env - ..Default::default() - }; - - let path = TempObjFile::default(); - let object_store = ObjectStore::local(); - let mut writer = FileWriter::try_new( - object_store.create(&path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - - writer.write_batch(&batch).await.unwrap(); - writer.finish().await.unwrap(); - - let fs = FsFixture::default(); - let file_scheduler = fs - .scheduler - .open_file(&path, &CachedFileSize::unknown()) - .await - .unwrap(); - let file_reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - for col_metadata in file_reader.metadata().column_metadatas.iter() { - for page in col_metadata.pages.iter() { - let total_size: u64 = page.buffer_sizes.iter().sum(); - assert!( - total_size <= 2 * 1024 * 1024, - "Page size {} exceeds 2MB limit", - total_size - ); - } - } - - unsafe { - std::env::set_var(ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, ""); - } - } - - #[tokio::test] - async fn test_compression_overrides_end_to_end() { - // Create test schema with different column types - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("customer_id", DataType::Int32, false), - ArrowField::new("product_id", DataType::Int32, false), - ArrowField::new("quantity", DataType::Int32, false), - ArrowField::new("price", DataType::Float32, false), - ArrowField::new("description", DataType::Utf8, false), - ])); - - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - - // Create test data with patterns suitable for different compression - let mut customer_ids = Int32Builder::new(); - let mut product_ids = Int32Builder::new(); - let mut quantities = Int32Builder::new(); - let mut prices = Float32Builder::new(); - let mut descriptions = Vec::new(); - - // Generate data with specific patterns: - // - customer_id: highly repetitive (good for RLE) - // - product_id: moderately repetitive (good for RLE) - // - quantity: random values (not good for RLE) - // - price: some repetition - // - description: long strings (good for Zstd) - for i in 0..10000 { - // Customer ID repeats every 100 rows (100 unique customers) - // This creates runs of 100 identical values - customer_ids.append_value(i / 100); - - // Product ID has only 5 unique values with long runs - product_ids.append_value(i / 2000); - - // Quantity is mostly 1 with occasional other values - quantities.append_value(if i % 10 == 0 { 5 } else { 1 }); - - // Price has only 3 unique values - prices.append_value(match i % 3 { - 0 => 9.99, - 1 => 19.99, - _ => 29.99, - }); - - // Descriptions are repetitive but we'll keep them simple - descriptions.push(format!("Product {}", i / 2000)); - } - - let batch = RecordBatch::try_new( - arrow_schema.clone(), - vec![ - Arc::new(customer_ids.finish()), - Arc::new(product_ids.finish()), - Arc::new(quantities.finish()), - Arc::new(prices.finish()), - Arc::new(StringArray::from(descriptions)), - ], - ) - .unwrap(); - - // Configure compression parameters - let mut params = CompressionParams::new(); - - // RLE for ID columns (ends with _id) - params.columns.insert( - "*_id".to_string(), - CompressionFieldParams { - rle_threshold: Some(0.5), // Lower threshold to trigger RLE more easily - compression: None, // Will use default compression if any - compression_level: None, - bss: Some(lance_encoding::compression_config::BssMode::Off), // Explicitly disable BSS to ensure RLE is used - minichunk_size: None, - }, - ); - - // For now, we'll skip Zstd compression since it's not imported - // In a real implementation, you could add other compression types here - - // Build encoding strategy with compression parameters - let encoding_strategy = lance_encoding::encoder::default_encoding_strategy_with_params( - LanceFileVersion::V2_1, - params, - ) - .unwrap(); - - // Configure file writer options - let options = FileWriterOptions { - encoding_strategy: Some(Arc::from(encoding_strategy)), - format_version: Some(LanceFileVersion::V2_1), - max_page_bytes: Some(64 * 1024), // 64KB pages - ..Default::default() - }; - - // Write the file - let path = TempObjFile::default(); - let object_store = ObjectStore::local(); - - let mut writer = FileWriter::try_new( - object_store.create(&path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - - writer.write_batch(&batch).await.unwrap(); - writer.add_schema_metadata("compression_test", "configured_compression"); - writer.finish().await.unwrap(); - - // Now write the same data without compression overrides for comparison - let path_no_compression = TempObjFile::default(); - let default_options = FileWriterOptions { - format_version: Some(LanceFileVersion::V2_1), - max_page_bytes: Some(64 * 1024), - ..Default::default() - }; - - let mut writer_no_compression = FileWriter::try_new( - object_store.create(&path_no_compression).await.unwrap(), - lance_schema.clone(), - default_options, - ) - .unwrap(); - - writer_no_compression.write_batch(&batch).await.unwrap(); - writer_no_compression.finish().await.unwrap(); - - // Note: With our current data patterns and RLE compression, the compressed file - // might actually be slightly larger due to compression metadata overhead. - // This is expected and the test is mainly to verify the system works end-to-end. - - // Read back the compressed file and verify data integrity - let fs = FsFixture::default(); - let file_scheduler = fs - .scheduler - .open_file(&path, &CachedFileSize::unknown()) - .await - .unwrap(); - - let file_reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - // Verify metadata - let metadata = file_reader.metadata(); - assert_eq!(metadata.major_version, 2); - assert_eq!(metadata.minor_version, 1); - - let schema = file_reader.schema(); - assert_eq!( - schema.metadata.get("compression_test"), - Some(&"configured_compression".to_string()) - ); - - // Verify the actual encodings used - let column_metadatas = &metadata.column_metadatas; - - // Check customer_id column (index 0) - should use RLE due to our configuration - assert!(!column_metadatas[0].pages.is_empty()); - let customer_id_encoding = describe_encoding(&column_metadatas[0].pages[0]); - assert!( - customer_id_encoding.contains("RLE") || customer_id_encoding.contains("Rle"), - "customer_id column should use RLE encoding due to '*_id' pattern match, but got: {}", - customer_id_encoding - ); - - // Check product_id column (index 1) - should use RLE due to our configuration - assert!(!column_metadatas[1].pages.is_empty()); - let product_id_encoding = describe_encoding(&column_metadatas[1].pages[0]); - assert!( - product_id_encoding.contains("RLE") || product_id_encoding.contains("Rle"), - "product_id column should use RLE encoding due to '*_id' pattern match, but got: {}", - product_id_encoding - ); - } - - #[tokio::test] - async fn test_field_metadata_compression() { - // Test that field metadata compression settings are respected - let mut metadata = HashMap::new(); - metadata.insert( - lance_encoding::constants::COMPRESSION_META_KEY.to_string(), - "zstd".to_string(), - ); - metadata.insert( - lance_encoding::constants::COMPRESSION_LEVEL_META_KEY.to_string(), - "6".to_string(), - ); - - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("id", DataType::Int32, false), - ArrowField::new("text", DataType::Utf8, false).with_metadata(metadata.clone()), - ArrowField::new("data", DataType::Int32, false).with_metadata(HashMap::from([( - lance_encoding::constants::COMPRESSION_META_KEY.to_string(), - "none".to_string(), - )])), - ])); - - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - - // Create test data - let id_array = Int32Array::from_iter_values(0..1000); - let text_array = StringArray::from_iter_values( - (0..1000).map(|i| format!("test string {} repeated text", i)), - ); - let data_array = Int32Array::from_iter_values((0..1000).map(|i| i * 2)); - - let batch = RecordBatch::try_new( - arrow_schema.clone(), - vec![ - Arc::new(id_array), - Arc::new(text_array), - Arc::new(data_array), - ], - ) - .unwrap(); - - let path = TempObjFile::default(); - let object_store = ObjectStore::local(); - - // Create encoding strategy that will read from field metadata - let params = CompressionParams::new(); - let encoding_strategy = lance_encoding::encoder::default_encoding_strategy_with_params( - LanceFileVersion::V2_1, - params, - ) - .unwrap(); - - let options = FileWriterOptions { - encoding_strategy: Some(Arc::from(encoding_strategy)), - format_version: Some(LanceFileVersion::V2_1), - ..Default::default() - }; - let mut writer = FileWriter::try_new( - object_store.create(&path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - - writer.write_batch(&batch).await.unwrap(); - writer.finish().await.unwrap(); - - // Read back metadata - let fs = FsFixture::default(); - let file_scheduler = fs - .scheduler - .open_file(&path, &CachedFileSize::unknown()) - .await - .unwrap(); - let file_reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - let column_metadatas = &file_reader.metadata().column_metadatas; - - // The text column (index 1) should use zstd compression based on metadata - let text_encoding = describe_encoding(&column_metadatas[1].pages[0]); - // For string columns, we expect Binary encoding with zstd compression - assert!( - text_encoding.contains("Zstd"), - "text column should use zstd compression from field metadata, but got: {}", - text_encoding - ); - - // The data column (index 2) should use no compression based on metadata - let data_encoding = describe_encoding(&column_metadatas[2].pages[0]); - // For Int32 columns with "none" compression, we expect Flat encoding without compression - assert!( - data_encoding.contains("Flat") && data_encoding.contains("compression: None"), - "data column should use no compression from field metadata, but got: {}", - data_encoding - ); - } - - #[tokio::test] - async fn test_field_metadata_rle_threshold() { - // Test that RLE threshold from field metadata is respected - let mut metadata = HashMap::new(); - metadata.insert( - lance_encoding::constants::RLE_THRESHOLD_META_KEY.to_string(), - "0.9".to_string(), - ); - // Also set compression to ensure RLE is used - metadata.insert( - lance_encoding::constants::COMPRESSION_META_KEY.to_string(), - "lz4".to_string(), - ); - // Explicitly disable BSS to ensure RLE is tested - metadata.insert( - lance_encoding::constants::BSS_META_KEY.to_string(), - "off".to_string(), - ); - - let arrow_schema = Arc::new(ArrowSchema::new(vec![ - ArrowField::new("status", DataType::Int32, false).with_metadata(metadata), - ])); - - let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); - - // Create data with very high repetition (3 runs for 10000 values = 0.0003 ratio) - let status_array = Int32Array::from_iter_values( - std::iter::repeat_n(200, 8000) - .chain(std::iter::repeat_n(404, 1500)) - .chain(std::iter::repeat_n(500, 500)), - ); - - let batch = - RecordBatch::try_new(arrow_schema.clone(), vec![Arc::new(status_array)]).unwrap(); - - let path = TempObjFile::default(); - let object_store = ObjectStore::local(); - - // Create encoding strategy that will read from field metadata - let params = CompressionParams::new(); - let encoding_strategy = lance_encoding::encoder::default_encoding_strategy_with_params( - LanceFileVersion::V2_1, - params, - ) - .unwrap(); - - let options = FileWriterOptions { - encoding_strategy: Some(Arc::from(encoding_strategy)), - format_version: Some(LanceFileVersion::V2_1), - ..Default::default() - }; - let mut writer = FileWriter::try_new( - object_store.create(&path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - - writer.write_batch(&batch).await.unwrap(); - writer.finish().await.unwrap(); - - // Read back and check encoding - let fs = FsFixture::default(); - let file_scheduler = fs - .scheduler - .open_file(&path, &CachedFileSize::unknown()) - .await - .unwrap(); - let file_reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - FileReaderOptions::default(), - ) - .await - .unwrap(); - - let column_metadatas = &file_reader.metadata().column_metadatas; - let status_encoding = describe_encoding(&column_metadatas[0].pages[0]); - assert!( - status_encoding.contains("RLE") || status_encoding.contains("Rle"), - "status column should use RLE encoding due to metadata threshold, but got: {}", - status_encoding - ); - } - - #[tokio::test] - async fn test_large_page_split_on_read() { - use arrow_array::Array; - use futures::TryStreamExt; - use lance_encoding::decoder::FilterExpression; - use lance_io::ReadBatchParams; - - // Test that large pages written with relaxed limits can be split during read - - let arrow_field = ArrowField::new("data", DataType::Binary, false); - let arrow_schema = ArrowSchema::new(vec![arrow_field]); - let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); - - // Create a large binary value (40MB) to trigger large page creation - let large_value = vec![42u8; 40 * 1024 * 1024]; - let array = arrow_array::BinaryArray::from(vec![ - Some(large_value.as_slice()), - Some(b"small value"), - ]); - let batch = RecordBatch::try_new(Arc::new(arrow_schema), vec![Arc::new(array)]).unwrap(); - - // Write with relaxed page size limit (128MB) - let options = FileWriterOptions { - max_page_bytes: Some(128 * 1024 * 1024), - format_version: Some(LanceFileVersion::V2_1), - ..Default::default() - }; - - let fs = FsFixture::default(); - let path = fs.tmp_path; - - let mut writer = FileWriter::try_new( - fs.object_store.create(&path).await.unwrap(), - lance_schema.clone(), - options, - ) - .unwrap(); - - writer.write_batch(&batch).await.unwrap(); - let write_summary = writer.finish().await.unwrap(); - assert_eq!(write_summary.num_rows, 2); - assert_eq!( - write_summary.size_bytes, - fs.object_store.size(&path).await.unwrap() - ); - - // Read back with split configuration - let file_scheduler = fs - .scheduler - .open_file(&path, &CachedFileSize::unknown()) - .await - .unwrap(); - - // Configure reader to split pages larger than 10MB into chunks - let reader_options = FileReaderOptions { - read_chunk_size: 10 * 1024 * 1024, // 10MB chunks - ..Default::default() - }; - - let file_reader = FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &LanceCache::no_cache(), - reader_options, - ) - .await - .unwrap(); - - // Read the data back - let stream = file_reader - .read_stream( - ReadBatchParams::RangeFull, - 1024, - 10, // batch_readahead - FilterExpression::no_filter(), - ) - .await - .unwrap(); - - let batches: Vec = stream.try_collect().await.unwrap(); - assert_eq!(batches.len(), 1); - - // Verify the data is correctly read despite splitting - let read_array = batches[0].column(0); - let read_binary = read_array - .as_any() - .downcast_ref::() - .unwrap(); - - assert_eq!(read_binary.len(), 2); - assert_eq!(read_binary.value(0).len(), 40 * 1024 * 1024); - assert_eq!(read_binary.value(1), b"small value"); - - // Verify first value matches what we wrote - assert!(read_binary.value(0).iter().all(|&b| b == 42u8)); - } - - fn spill_config() -> (TempObjFile, Arc) { - let spill_path = TempObjFile::default(); - (spill_path, Arc::new(ObjectStore::local())) - } - - fn make_batches(num_batches: i32, num_cols: usize, rows_per_batch: i32) -> Vec { - let fields: Vec<_> = (0..num_cols) - .map(|c| ArrowField::new(format!("c{c}"), DataType::Int32, false)) - .collect(); - let schema = Arc::new(ArrowSchema::new(fields)); - (0..num_batches) - .map(|i| { - let cols: Vec> = (0..num_cols) - .map(|c| { - let start = (i * rows_per_batch + c as i32) * 100; - Arc::new(Int32Array::from_iter_values(start..start + rows_per_batch)) - as Arc - }) - .collect(); - RecordBatch::try_new(schema.clone(), cols).unwrap() - }) - .collect() - } - - async fn write_and_read_batches( - batches: &[RecordBatch], - spill: Option<(Arc, object_store::path::Path)>, - ) -> Vec { - let fs = FsFixture::default(); - let lance_schema = LanceSchema::try_from(batches[0].schema().as_ref()).unwrap(); - let writer = fs.object_store.create(&fs.tmp_path).await.unwrap(); - let mut file_writer = - FileWriter::try_new(writer, lance_schema, FileWriterOptions::default()).unwrap(); - if let Some((store, path)) = spill { - file_writer = file_writer.with_page_metadata_spill(store, path); - } - for batch in batches { - file_writer.write_batch(batch).await.unwrap(); - } - file_writer.add_schema_metadata("foo", "bar"); - file_writer.finish().await.unwrap(); - - crate::testing::read_lance_file( - &fs, - Arc::::default(), - lance_encoding::decoder::FilterExpression::no_filter(), - ) - .await - } - - #[rstest::rstest] - #[case::multi_col(20, 2, 100)] - #[case::many_batches(50, 2, 100)] - #[tokio::test] - async fn test_page_metadata_spill_roundtrip( - #[case] num_batches: i32, - #[case] num_cols: usize, - #[case] rows_per_batch: i32, - ) { - let batches = make_batches(num_batches, num_cols, rows_per_batch); - let baseline = write_and_read_batches(&batches, None).await; - let (spill_path, spill_store) = spill_config(); - let spilled = - write_and_read_batches(&batches, Some((spill_store, spill_path.as_ref().clone()))) - .await; - assert_eq!(baseline, spilled); - } - - #[tokio::test] - async fn test_page_metadata_spill_many_columns() { - // Many columns forces small per-column buffer limits, exercising mid-write flushing. - let batches = make_batches(10, 500, 100); - let baseline = write_and_read_batches(&batches, None).await; - let (spill_path, spill_store) = spill_config(); - let spilled = - write_and_read_batches(&batches, Some((spill_store, spill_path.as_ref().clone()))) - .await; - assert_eq!(baseline, spilled); - } -} +#[path = "writer_tests.rs"] +mod writer_tests; diff --git a/rust/lance-file/src/writer/structural.rs b/rust/lance-file/src/writer/structural.rs new file mode 100644 index 00000000000..3732b4e0a64 --- /dev/null +++ b/rust/lance-file/src/writer/structural.rs @@ -0,0 +1,837 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +use core::panic; +use std::{collections::HashMap, sync::Arc}; + +use arrow_array::{ArrayRef, RecordBatch}; +use arrow_data::ArrayData; +use bytes::{Buf, Bytes, BytesMut}; +use futures::{StreamExt, stream::FuturesOrdered}; +use lance_core::{ + Error, Result, + datatypes::{Field, Schema}, + utils::bit::pad_bytes, +}; +use lance_encoding::{ + decoder::PageEncoding, + encoder::{ + BatchEncoder, EncodeTask, EncodedBatch, EncodedPage, EncodingOptions, FieldEncoder, + OutOfLineBuffers, + }, + repdef::RepDefBuilder, +}; +use lance_io::{object_store::ObjectStore, traits::Writer as ObjectWriter}; +use log::{debug, warn}; +use object_store::path::Path; +use prost::Message; +use prost_types::Any; +use tokio::io::{AsyncWrite, AsyncWriteExt}; +use tracing::instrument; + +use crate::{ + datatypes::FieldsWithMeta, + format::{pb, pbfile}, + writer::{ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, FileWriterOptions, PAGE_BUFFER_ALIGNMENT}, +}; + +const PAD_BUFFER: [u8; PAGE_BUFFER_ALIGNMENT] = [72; PAGE_BUFFER_ALIGNMENT]; + +// V2.1+ splits large pages on read instead of write. This write-time limit is +// retained as a best-effort memory bound and is not a grammar decision. +const MAX_PAGE_BYTES: usize = 32 * 1024 * 1024; + +// Total in-memory budget for serialized page metadata before spill. +const DEFAULT_SPILL_BUFFER_LIMIT: usize = 256 * 1024; + +struct PageMetadataSpill { + writer: Box, + object_store: Arc, + path: Path, + position: u64, + column_buffers: Vec>, + column_chunks: Vec>, + per_column_limit: usize, +} + +impl PageMetadataSpill { + async fn new(object_store: Arc, path: Path, num_columns: usize) -> Result { + let writer = object_store.create(&path).await?; + let per_column_limit = (DEFAULT_SPILL_BUFFER_LIMIT / num_columns.max(1)).max(64); + Ok(Self { + writer, + object_store, + path, + position: 0, + column_buffers: vec![Vec::new(); num_columns], + column_chunks: vec![Vec::new(); num_columns], + per_column_limit, + }) + } + + async fn append_page( + &mut self, + column_index: usize, + page: &pbfile::column_metadata::Page, + ) -> Result<()> { + page.encode_length_delimited(&mut self.column_buffers[column_index]) + .map_err(|error| { + Error::io_source(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + error, + ))) + })?; + if self.column_buffers[column_index].len() >= self.per_column_limit { + self.flush_column(column_index).await?; + } + Ok(()) + } + + async fn flush_column(&mut self, column_index: usize) -> Result<()> { + let buffer = &self.column_buffers[column_index]; + if buffer.is_empty() { + return Ok(()); + } + let len = buffer.len(); + self.writer.write_all(buffer).await?; + self.column_chunks[column_index].push((self.position, len as u32)); + self.position += len as u64; + self.column_buffers[column_index].clear(); + Ok(()) + } + + async fn shutdown_writer(&mut self) -> Result<()> { + for column_index in 0..self.column_buffers.len() { + self.flush_column(column_index).await?; + } + ObjectWriter::shutdown(self.writer.as_mut()).await?; + Ok(()) + } +} + +fn decode_spilled_chunk(data: &Bytes) -> Result> { + let mut pages = Vec::new(); + let mut cursor = data.clone(); + while cursor.has_remaining() { + let page = pbfile::column_metadata::Page::decode_length_delimited(&mut cursor).map_err( + |error| { + Error::io_source(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + error, + ))) + }, + )?; + pages.push(page); + } + Ok(pages) +} + +enum PageSpillState { + Pending(Arc, Path), + Active(PageMetadataSpill), +} + +fn initial_column_metadata() -> pbfile::ColumnMetadata { + pbfile::ColumnMetadata { + pages: Vec::new(), + buffer_offsets: Vec::new(), + buffer_sizes: Vec::new(), + encoding: None, + } +} + +/// Writes the structural page and metadata representation used by v2.1+. +/// +/// This component does not choose a file version, field encoding strategy, or +/// footer identity. Exact version writers opt into this representation and own +/// the order in which its operations are invoked. +pub struct StructuralFileSink { + writer: Box, + column_metadata: Vec, + num_columns: u32, + global_buffers: Vec<(u64, u64)>, + page_spill: Option, +} + +impl StructuralFileSink { + pub fn new(writer: Box) -> Self { + Self { + writer, + column_metadata: Vec::new(), + num_columns: 0, + global_buffers: Vec::new(), + page_spill: None, + } + } + + pub fn with_page_metadata_spill(&mut self, object_store: Arc, path: Path) { + self.page_spill = Some(PageSpillState::Pending(object_store, path)); + } + + pub fn initialize_columns(&mut self, num_columns: u32) { + self.num_columns = num_columns; + self.column_metadata = vec![initial_column_metadata(); num_columns as usize]; + } + + pub fn initialize_with_external_metadata( + &mut self, + column_metadata: Vec, + ) { + self.num_columns = column_metadata.len() as u32; + self.column_metadata = column_metadata; + } + + async fn write_aligned_buffer_to( + writer: &mut (impl AsyncWrite + Unpin), + buffer: &[u8], + ) -> Result<()> { + writer.write_all(buffer).await?; + let padding = pad_bytes::(buffer.len()); + writer.write_all(&PAD_BUFFER[..padding]).await?; + Ok(()) + } + + pub async fn write_aligned_buffer(&mut self, buffer: &[u8]) -> Result<()> { + Self::write_aligned_buffer_to(&mut self.writer, buffer).await + } + + pub async fn write_raw(&mut self, buffer: &[u8]) -> Result<()> { + self.writer.write_all(buffer).await?; + Ok(()) + } + + pub async fn write_page(&mut self, encoded_page: EncodedPage) -> Result<()> { + let buffers = encoded_page.data; + let mut buffer_offsets = Vec::with_capacity(buffers.len()); + let mut buffer_sizes = Vec::with_capacity(buffers.len()); + for buffer in buffers { + buffer_offsets.push(self.tell().await?); + buffer_sizes.push(buffer.len() as u64); + self.write_aligned_buffer(&buffer).await?; + } + let encoded_encoding = match encoded_page.description { + PageEncoding::Array(array_encoding) => Any::from_msg(&array_encoding)?.encode_to_vec(), + PageEncoding::Structural(page_layout) => Any::from_msg(&page_layout)?.encode_to_vec(), + }; + let page = pbfile::column_metadata::Page { + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { + encoding: encoded_encoding, + })), + }), + length: encoded_page.num_rows, + priority: encoded_page.row_number, + }; + let column_index = encoded_page.column_idx as usize; + if matches!(&self.page_spill, Some(PageSpillState::Pending(..))) { + let Some(PageSpillState::Pending(store, path)) = self.page_spill.take() else { + unreachable!() + }; + self.page_spill = Some(PageSpillState::Active( + PageMetadataSpill::new(store, path, self.num_columns as usize).await?, + )); + } + match &mut self.page_spill { + Some(PageSpillState::Active(spill)) => spill.append_page(column_index, &page).await?, + None => self.column_metadata[column_index].pages.push(page), + Some(PageSpillState::Pending(..)) => unreachable!(), + } + Ok(()) + } + + #[instrument(skip_all, level = "debug")] + pub async fn write_pages( + &mut self, + mut encoding_tasks: FuturesOrdered, + ) -> Result<()> { + while let Some(encoding_task) = encoding_tasks.next().await { + self.write_page(encoding_task?).await?; + } + self.writer.flush().await?; + Ok(()) + } + + pub async fn write_column_buffer_at( + &mut self, + column_index: usize, + position: u64, + buffer: &[u8], + ) -> Result<()> { + self.write_aligned_buffer(buffer).await?; + let metadata = &mut self.column_metadata[column_index]; + metadata.buffer_offsets.push(position); + metadata.buffer_sizes.push(buffer.len() as u64); + Ok(()) + } + + pub fn set_column_encoding(&mut self, column_index: usize, encoding: pbfile::Encoding) { + self.column_metadata[column_index].encoding = Some(encoding); + } + + pub async fn add_global_buffer(&mut self, buffer: Bytes) -> Result { + let position = self.tell().await?; + let len = buffer.len() as u64; + self.write_aligned_buffer(&buffer).await?; + self.global_buffers.push((position, len)); + Ok(self.global_buffers.len() as u32) + } + + pub async fn write_global_buffers( + &mut self, + descriptor: pb::FileDescriptor, + ) -> Result> { + let descriptor_bytes = descriptor.encode_to_vec(); + let descriptor_len = descriptor_bytes.len() as u64; + let descriptor_position = self.tell().await?; + self.writer.write_all(&descriptor_bytes).await?; + let mut offsets = Vec::with_capacity(1 + self.global_buffers.len()); + offsets.push((descriptor_position, descriptor_len)); + offsets.append(&mut self.global_buffers); + Ok(offsets) + } + + async fn write_column_metadata( + &mut self, + metadata: pbfile::ColumnMetadata, + ) -> Result<(u64, u64)> { + let metadata_bytes = metadata.encode_to_vec(); + let position = self.tell().await?; + let len = metadata_bytes.len() as u64; + self.writer.write_all(&metadata_bytes).await?; + Ok((position, len)) + } + + pub async fn write_column_metadatas(&mut self) -> Result> { + let metadatas = std::mem::take(&mut self.column_metadata); + let spill_state = self.page_spill.take(); + let (spill_chunks, spill_reader) = + if let Some(PageSpillState::Active(mut spill)) = spill_state { + spill.shutdown_writer().await?; + let reader = spill.object_store.open(&spill.path).await?; + let chunks = std::mem::take(&mut spill.column_chunks); + (chunks, Some(reader)) + } else { + (Vec::new(), None) + }; + + let mut metadata_positions = Vec::with_capacity(metadatas.len()); + for (column_index, mut metadata) in metadatas.into_iter().enumerate() { + if let Some(reader) = &spill_reader { + let mut pages = Vec::new(); + for &(offset, len) in &spill_chunks[column_index] { + let data = reader + .get_range(offset as usize..(offset as usize + len as usize)) + .await + .map_err(|error| Error::io_source(Box::new(error)))?; + pages.extend(decode_spilled_chunk(&data)?); + } + metadata.pages = pages; + } + metadata_positions.push(self.write_column_metadata(metadata).await?); + } + Ok(metadata_positions) + } + + pub async fn write_offset_table(&mut self, offsets: &[(u64, u64)]) -> Result { + let start = self.tell().await?; + for (position, len) in offsets { + self.writer.write_u64_le(*position).await?; + self.writer.write_u64_le(*len).await?; + } + Ok(start) + } + + pub async fn write_external_buffer(&mut self, bytes: &[u8]) -> Result<(u64, u64)> { + const ZERO_PADDING: [u8; PAGE_BUFFER_ALIGNMENT] = [0; PAGE_BUFFER_ALIGNMENT]; + let position = self.tell().await?; + let padding = (PAGE_BUFFER_ALIGNMENT - position as usize % PAGE_BUFFER_ALIGNMENT) + % PAGE_BUFFER_ALIGNMENT; + self.writer.write_all(&ZERO_PADDING[..padding]).await?; + let start = position + padding as u64; + self.writer.write_all(bytes).await?; + Ok((start, bytes.len() as u64)) + } + + pub fn output_mut(&mut self) -> &mut dyn ObjectWriter { + self.writer.as_mut() + } + + pub async fn tell(&mut self) -> Result { + Ok(self.writer.tell().await? as u64) + } + + pub fn num_columns(&self) -> u32 { + self.num_columns + } + + pub async fn shutdown(&mut self) -> Result { + let result = ObjectWriter::shutdown(self.writer.as_mut()).await?; + Ok(result.size as u64) + } +} + +/// Runs field encoders and row accounting without selecting a file grammar. +/// +/// The exact version writer constructs a [`BatchEncoder`] and supplies it when +/// a schema is initialized. This component only executes that decision. +pub struct EncodingPipeline { + schema: Option, + field_encoders: Vec>, + field_id_to_column_indices: Vec<(u32, u32)>, + rows_written: u64, + field_rows_written: Vec, + schema_metadata: HashMap, + options: FileWriterOptions, +} + +impl EncodingPipeline { + pub fn new(options: FileWriterOptions) -> Self { + Self { + schema: None, + field_encoders: Vec::new(), + field_id_to_column_indices: Vec::new(), + rows_written: 0, + field_rows_written: Vec::new(), + schema_metadata: HashMap::new(), + options, + } + } + + pub fn encoding_options(&self, schema: &Schema) -> EncodingOptions { + let cache_bytes_per_column = if let Some(data_cache_bytes) = self.options.data_cache_bytes { + data_cache_bytes / schema.fields.len() as u64 + } else { + 8 * 1024 * 1024 + }; + let max_page_bytes = self.options.max_page_bytes.unwrap_or_else(|| { + std::env::var(ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES) + .map(|value| { + value.parse::().unwrap_or_else(|error| { + warn!( + "Failed to parse {}: {}, using default", + ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, error + ); + MAX_PAGE_BYTES as u64 + }) + }) + .unwrap_or(MAX_PAGE_BYTES as u64) + }); + + EncodingOptions { + cache_bytes_per_column, + max_page_bytes, + keep_original_array: self.options.keep_original_array.unwrap_or(false), + buffer_alignment: PAGE_BUFFER_ALIGNMENT as u64, + } + } + + pub fn initialize( + &mut self, + mut schema: Schema, + encoder: BatchEncoder, + sink: &mut StructuralFileSink, + ) { + sink.initialize_columns(encoder.num_columns()); + self.field_rows_written = vec![0; encoder.field_encoders.len()]; + self.field_encoders = encoder.field_encoders; + self.field_id_to_column_indices = encoder.field_id_to_column_index; + self.schema_metadata + .extend(std::mem::take(&mut schema.metadata)); + self.schema = Some(schema); + } + + pub fn is_initialized(&self) -> bool { + self.schema.is_some() + } + + fn verify_field_nullability(array: &ArrayData, field: &Field) -> Result<()> { + if !field.nullable && array.null_count() > 0 { + return Err(Error::invalid_input(format!( + "The field `{}` contained null values even though the field is marked non-null in the schema", + field.name + ))); + } + for (child_field, child_array) in field.children.iter().zip(array.child_data()) { + Self::verify_field_nullability(child_array, child_field)?; + } + Ok(()) + } + + fn verify_nullability_constraints(&self, batch: &RecordBatch) -> Result<()> { + for (column, field) in batch + .columns() + .iter() + .zip(self.schema.as_ref().unwrap().fields.iter()) + { + Self::verify_field_nullability(&column.to_data(), field)?; + } + Ok(()) + } + + fn encode_columns( + &mut self, + fields: &[(usize, ArrayRef)], + external_buffers: &mut OutOfLineBuffers, + ) -> Result>> { + let row_numbers = fields + .iter() + .map(|(field_index, _)| self.field_rows_written[*field_index]) + .collect::>(); + fields + .iter() + .zip(row_numbers) + .map(|((field_index, array), row_number)| { + self.field_encoders[*field_index].maybe_encode( + array.clone(), + external_buffers, + RepDefBuilder::default(), + row_number, + array.len() as u64, + ) + }) + .collect() + } + + fn encode_batch( + &mut self, + batch: &RecordBatch, + external_buffers: &mut OutOfLineBuffers, + ) -> Result>> { + let field_arrays = self + .schema + .as_ref() + .unwrap() + .fields + .iter() + .enumerate() + .map(|(field_index, field)| { + let array = batch.column_by_name(&field.name).ok_or_else(|| { + Error::invalid_input_source( + format!( + "Cannot write batch. The batch was missing the column `{}`", + field.name + ) + .into(), + ) + })?; + Ok((field_index, array.clone())) + }) + .collect::>>()?; + self.encode_columns(&field_arrays, external_buffers) + } + + #[instrument(skip_all, level = "debug")] + pub async fn write_batch( + &mut self, + batch: &RecordBatch, + sink: &mut StructuralFileSink, + ) -> Result<()> { + debug!( + "write_batch called with {} rows, {} columns, and {} bytes of data", + batch.num_rows(), + batch.num_columns(), + batch.get_array_memory_size() + ); + self.verify_nullability_constraints(batch)?; + let num_rows = batch.num_rows() as u64; + if num_rows == 0 { + return Ok(()); + } + if num_rows > u32::MAX as u64 { + return Err(Error::invalid_input_source( + "cannot write Lance files with more than 2^32 rows".into(), + )); + } + + let mut external_buffers = + OutOfLineBuffers::new(sink.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let encoding_tasks = self.encode_batch(batch, &mut external_buffers)?; + for external_buffer in external_buffers.take_buffers() { + sink.write_aligned_buffer(&external_buffer).await?; + } + let encoding_tasks = encoding_tasks + .into_iter() + .flatten() + .collect::>(); + + if self.rows_written.checked_add(num_rows).is_none() { + return Err(Error::invalid_input_source(format!( + "cannot write batch with {} rows because {} rows have already been written and Lance files cannot contain more than 2^64 rows", + num_rows, self.rows_written + ).into())); + } + for field_rows in &mut self.field_rows_written { + *field_rows += num_rows; + } + self.rows_written += num_rows; + sink.write_pages(encoding_tasks).await + } + + pub async fn write_column( + &mut self, + column_index: usize, + array: ArrayRef, + sink: &mut StructuralFileSink, + ) -> Result<()> { + let schema = self.schema.as_ref().ok_or_else(|| { + Error::invalid_input_source( + "write_column requires the writer to be created with an explicit schema".into(), + ) + })?; + let field = schema.fields.get(column_index).ok_or_else(|| { + Error::invalid_input_source( + format!( + "write_column: field index {} is out of bounds (schema has {} fields)", + column_index, + schema.fields.len() + ) + .into(), + ) + })?; + if array.len() as u64 > u32::MAX as u64 { + return Err(Error::invalid_input_source( + "cannot write Lance files with more than 2^32 rows".into(), + )); + } + Self::verify_field_nullability(&array.to_data(), field)?; + if array.is_empty() { + return Ok(()); + } + + let fields = [(column_index, array)]; + let mut external_buffers = + OutOfLineBuffers::new(sink.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let encoding_tasks = self.encode_columns(&fields, &mut external_buffers)?; + for external_buffer in external_buffers.take_buffers() { + sink.write_aligned_buffer(&external_buffer).await?; + } + let encoding_tasks = encoding_tasks + .into_iter() + .flatten() + .collect::>(); + for (field_index, array) in &fields { + let new_total = self.field_rows_written[*field_index] + array.len() as u64; + self.field_rows_written[*field_index] = new_total; + self.rows_written = self.rows_written.max(new_total); + } + sink.write_pages(encoding_tasks).await + } + + pub async fn flush(&mut self, sink: &mut StructuralFileSink) -> Result<()> { + let mut external_buffers = + OutOfLineBuffers::new(sink.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let encoding_tasks = self + .field_encoders + .iter_mut() + .map(|writer| writer.flush(&mut external_buffers)) + .collect::>>()?; + for external_buffer in external_buffers.take_buffers() { + sink.write_aligned_buffer(&external_buffer).await?; + } + sink.write_pages( + encoding_tasks + .into_iter() + .flatten() + .collect::>(), + ) + .await + } + + pub async fn finish_encoders(&mut self, sink: &mut StructuralFileSink) -> Result<()> { + if self.field_encoders.is_empty() { + return Ok(()); + } + let mut column_index = 0; + for mut writer in std::mem::take(&mut self.field_encoders) { + let mut external_buffers = + OutOfLineBuffers::new(sink.tell().await?, PAGE_BUFFER_ALIGNMENT as u64); + let columns = writer.finish(&mut external_buffers).await?; + for buffer in external_buffers.take_buffers() { + sink.write_raw(&buffer).await?; + } + debug_assert_eq!( + columns.len(), + writer.num_columns() as usize, + "Expected {} columns from column at index {} and got {}", + writer.num_columns(), + column_index, + columns.len() + ); + for column in columns { + for page in column.final_pages { + sink.write_page(page).await?; + } + let mut buffer_position = sink.tell().await?; + for buffer in column.column_buffers { + sink.write_column_buffer_at(column_index, buffer_position, &buffer) + .await?; + buffer_position += buffer.len() as u64; + } + let encoded_encoding = Any::from_msg(&column.encoding)?.encode_to_vec(); + sink.set_column_encoding( + column_index, + pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct( + pbfile::DirectEncoding { + encoding: encoded_encoding, + }, + )), + }, + ); + column_index += 1; + } + } + if column_index != sink.num_columns() as usize { + panic!( + "Column writers finished with {} columns but we expected {}", + column_index, + sink.num_columns() + ); + } + Ok(()) + } + + pub fn add_schema_metadata(&mut self, key: impl Into, value: impl Into) { + self.schema_metadata.insert(key.into(), value.into()); + } + + pub fn initialize_with_external_metadata(&mut self, schema: Schema, rows_written: u64) { + self.schema = Some(schema); + self.rows_written = rows_written; + } + + pub fn make_file_descriptor(&mut self) -> Result { + let schema = self.schema.as_mut().ok_or_else(|| { + Error::invalid_input( + "No schema provided on writer open and no data provided. Schema is unknown and file cannot be created", + ) + })?; + schema.metadata = std::mem::take(&mut self.schema_metadata); + schema + .fields + .iter_mut() + .for_each(|field| field.unload_blobs_recursive()); + make_file_descriptor(schema, self.rows_written) + } + + pub fn rows_written(&self) -> u64 { + self.rows_written + } + + pub fn field_id_to_column_indices(&self) -> &[(u32, u32)] { + &self.field_id_to_column_indices + } +} + +fn make_file_descriptor(schema: &Schema, num_rows: u64) -> Result { + let fields_with_meta = FieldsWithMeta::from(schema); + Ok(pb::FileDescriptor { + schema: Some(pb::Schema { + fields: fields_with_meta.fields.0, + metadata: fields_with_meta.metadata, + }), + length: num_rows, + }) +} + +/// Structural file body produced before an exact version appends its footer. +pub struct EncodedBatchBody { + pub data: BytesMut, + pub column_metadata_start: u64, + pub column_metadata_offsets_start: u64, + pub global_buffer_offsets_start: u64, + pub num_global_buffers: u32, + pub num_columns: u32, +} + +pub fn encode_batch_body(batch: &EncodedBatch, write_schema: bool) -> Result { + use bytes::BufMut; + + let mut data = BytesMut::with_capacity(batch.data.len() + 1024 * 1024); + data.extend_from_slice(&batch.data); + let global_buffers = if write_schema { + let schema_start = data.len() as u64; + let schema = Schema::try_from(batch.schema.as_ref())?; + let descriptor = make_file_descriptor(&schema, batch.num_rows)?; + let descriptor_bytes = descriptor.encode_to_vec(); + let descriptor_len = descriptor_bytes.len() as u64; + data.extend_from_slice(&descriptor_bytes); + vec![(schema_start, descriptor_len)] + } else { + Vec::new() + }; + let column_metadata_start = data.len() as u64; + + let mut column_metadata_positions = Vec::with_capacity(batch.page_table.len()); + for column in &batch.page_table { + let position = data.len() as u64; + let pages = column + .page_infos + .iter() + .map(|page_info| { + let encoded_encoding = match &page_info.encoding { + PageEncoding::Array(array_encoding) => { + Any::from_msg(array_encoding)?.encode_to_vec() + } + PageEncoding::Structural(page_layout) => { + Any::from_msg(page_layout)?.encode_to_vec() + } + }; + let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = + page_info.buffer_offsets_and_sizes.iter().copied().unzip(); + Ok(pbfile::column_metadata::Page { + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct( + pbfile::DirectEncoding { + encoding: encoded_encoding, + }, + )), + }), + length: page_info.num_rows, + priority: page_info.priority, + }) + }) + .collect::>>()?; + let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = + column.buffer_offsets_and_sizes.iter().copied().unzip(); + let encoded_column_encoding = Any::from_msg(&column.encoding)?.encode_to_vec(); + let metadata = pbfile::ColumnMetadata { + pages, + buffer_offsets, + buffer_sizes, + encoding: Some(pbfile::Encoding { + location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { + encoding: encoded_column_encoding, + })), + }), + }; + let metadata_bytes = metadata.encode_to_vec(); + column_metadata_positions.push((position, metadata_bytes.len() as u64)); + data.extend_from_slice(&metadata_bytes); + } + + let column_metadata_offsets_start = data.len() as u64; + for (position, len) in column_metadata_positions { + data.put_u64_le(position); + data.put_u64_le(len); + } + let global_buffer_offsets_start = data.len() as u64; + let num_global_buffers = global_buffers.len() as u32; + for (position, len) in global_buffers { + data.put_u64_le(position); + data.put_u64_le(len); + } + + Ok(EncodedBatchBody { + data, + column_metadata_start, + column_metadata_offsets_start, + global_buffer_offsets_start, + num_global_buffers, + num_columns: batch.page_table.len() as u32, + }) +} diff --git a/rust/lance-file/src/writer_tests.rs b/rust/lance-file/src/writer_tests.rs new file mode 100644 index 00000000000..c3d69c33f7a --- /dev/null +++ b/rust/lance-file/src/writer_tests.rs @@ -0,0 +1,1887 @@ +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::sync::Arc; + + use crate::reader::{FileReader, FileReaderOptions, describe_encoding}; + use crate::testing::FsFixture; + use crate::version::ConcreteFileVersion; + use crate::versions; + use crate::writer::{ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, FileWriter, FileWriterOptions}; + use arrow_array::builder::{Float32Builder, Int32Builder, StringDictionaryBuilder}; + use arrow_array::types::{Float64Type, Int8Type, Int32Type}; + use arrow_array::{ + Array, ArrayRef, Int32Array, LargeBinaryArray, ListArray, RecordBatch, RecordBatchReader, + StringArray, UInt64Array, cast::AsArray, + }; + use arrow_schema::{DataType, Field, Field as ArrowField, Schema, Schema as ArrowSchema}; + use lance_core::cache::LanceCache; + use lance_core::datatypes::Schema as LanceSchema; + use lance_core::utils::tempfile::TempObjFile; + use lance_datagen::{BatchCount, RowCount, array, gen_batch}; + use lance_encoding::compression_config::{CompressionFieldParams, CompressionParams}; + use lance_encoding::decoder::DecoderPlugins; + use lance_io::object_store::ObjectStore; + use lance_io::traits::Writer; + use lance_io::utils::CachedFileSize; + use rstest::rstest; + use tokio::io::AsyncWriteExt; + + fn create_writer( + object_writer: Box, + schema: LanceSchema, + version: ConcreteFileVersion, + options: FileWriterOptions, + ) -> lance_core::Result { + versions::create_writer(version, object_writer, schema, options) + } + + fn create_v2_1_writer_with_compression( + object_writer: Box, + schema: LanceSchema, + options: FileWriterOptions, + compression: CompressionParams, + ) -> lance_core::Result { + versions::v2_1::create_writer_with_compression(object_writer, schema, options, compression) + .map(Into::into) + } + + #[tokio::test] + async fn current_writer_dispatch_rejects_legacy_version() { + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + let object_writer = object_store.create(&path).await.unwrap(); + let Err(error) = versions::create_lazy_writer( + ConcreteFileVersion::V1, + object_writer, + FileWriterOptions::default(), + ) else { + panic!("legacy v1 unexpectedly created a current-format writer"); + }; + assert!(matches!(error, lance_core::Error::NotSupported { .. })); + assert!( + error + .to_string() + .contains("legacy v1 files require an explicit schema and manifest provider") + ); + } + + #[rstest] + #[case::v2_0(ConcreteFileVersion::V2_0)] + #[case::v2_1(ConcreteFileVersion::V2_1)] + #[case::v2_2(ConcreteFileVersion::V2_2)] + #[case::v2_3(ConcreteFileVersion::V2_3)] + #[tokio::test] + async fn version_leaf_writes_exact_standard_footer(#[case] version: ConcreteFileVersion) { + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + let schema = LanceSchema::try_from(&ArrowSchema::new(vec![ArrowField::new( + "value", + DataType::Int32, + true, + )])) + .unwrap(); + let mut writer = create_writer( + object_store.create(&path).await.unwrap(), + schema, + version, + FileWriterOptions::default(), + ) + .unwrap(); + let summary = writer.finish().await.unwrap(); + let footer = object_store + .open(&path) + .await + .unwrap() + .get_range(summary.size_bytes as usize - 8..summary.size_bytes as usize) + .await + .unwrap(); + let actual = ( + u16::from_le_bytes([footer[0], footer[1]]), + u16::from_le_bytes([footer[2], footer[3]]), + ); + assert_eq!(actual, version.to_standard_footer_numbers()); + } + + #[rstest] + #[case::v2_0(ConcreteFileVersion::V2_0)] + #[case::v2_1(ConcreteFileVersion::V2_1)] + #[case::v2_2(ConcreteFileVersion::V2_2)] + #[case::v2_3(ConcreteFileVersion::V2_3)] + fn packed_struct_is_one_physical_column(#[case] version: ConcreteFileVersion) { + let packed = ArrowField::new( + "packed", + DataType::Struct(vec![ArrowField::new("child", DataType::Int32, true)].into()), + true, + ) + .with_metadata(HashMap::from([("packed".to_string(), "true".to_string())])); + let schema = LanceSchema::try_from(&ArrowSchema::new(vec![ + packed, + ArrowField::new("tail", DataType::Int32, true), + ])) + .unwrap(); + + let projection = + versions::reader_projection_from_column_names(version, &schema, &["tail"]).unwrap(); + assert_eq!(projection.column_indices, vec![1]); + + let (field_ids, column_indices) = versions::data_file_columns(version, &schema); + assert_eq!(field_ids.len(), 2); + assert_eq!(column_indices, vec![0, 1]); + assert_eq!( + schema + .fields + .iter() + .map(|field| versions::physical_column_count(version, field)) + .sum::(), + 2 + ); + } + + #[rstest] + #[case::v1(ConcreteFileVersion::V1, &[0, 1, 2], &[0, 1, 2])] + #[case::v2_0(ConcreteFileVersion::V2_0, &[0, 1, 2], &[0, 1, 2])] + #[case::v2_1(ConcreteFileVersion::V2_1, &[1, 2], &[0, 1])] + #[case::v2_2(ConcreteFileVersion::V2_2, &[1, 2], &[0, 1])] + #[case::v2_3(ConcreteFileVersion::V2_3, &[1, 2], &[0, 1])] + fn data_file_mapping_tracks_only_version_physical_fields( + #[case] version: ConcreteFileVersion, + #[case] expected_field_ids: &[i32], + #[case] expected_column_indices: &[i32], + ) { + let schema = LanceSchema::try_from(&ArrowSchema::new(vec![ + ArrowField::new( + "nested", + DataType::Struct(vec![ArrowField::new("child", DataType::Int32, true)].into()), + true, + ), + ArrowField::new("tail", DataType::Int32, true), + ])) + .unwrap(); + + let (field_ids, column_indices) = versions::data_file_columns(version, &schema); + assert_eq!(field_ids, expected_field_ids); + assert_eq!(column_indices, expected_column_indices); + } + + fn compatibility_fixture_batch() -> RecordBatch { + let row_count = 4097; + let ids = Arc::new(Int32Array::from_iter_values(0..row_count)) as ArrayRef; + let names = Arc::new(StringArray::from_iter((0..row_count).map(|index| { + (index % 7 != 0).then(|| format!("value-{index:04}-deterministic-fixture")) + }))) as ArrayRef; + let items = Arc::new(ListArray::from_iter_primitive::( + (0..row_count).map(|index| { + (index % 11 != 0).then(|| { + vec![ + Some(index), + (index % 5 != 0).then_some(index * 2), + Some(index * 3), + ] + }) + }), + )) as ArrayRef; + let mut categories = StringDictionaryBuilder::::new(); + for index in 0..row_count { + if index % 13 == 0 { + categories.append_null(); + } else { + categories + .append(match index % 3 { + 0 => "red", + 1 => "green", + _ => "blue", + }) + .unwrap(); + } + } + let categories = Arc::new(categories.finish()) as ArrayRef; + let blobs = Arc::new(LargeBinaryArray::from_iter_values( + (0..row_count) + .map(|index| format!("blob-{index:04}-deterministic-payload").into_bytes()), + )) as ArrayRef; + + let schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("id", DataType::Int32, false), + ArrowField::new("name", DataType::Utf8, true).with_metadata(HashMap::from([( + "lance-encoding:compression".to_string(), + "none".to_string(), + )])), + ArrowField::new( + "items", + DataType::List(Arc::new(ArrowField::new("item", DataType::Int32, true))), + true, + ), + ArrowField::new( + "category", + DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8)), + true, + ) + .with_metadata(HashMap::from([( + "lance-encoding:dict-values-compression".to_string(), + "none".to_string(), + )])), + ArrowField::new("blob", DataType::LargeBinary, true).with_metadata(HashMap::from([( + "lance-encoding:blob".to_string(), + "true".to_string(), + )])), + ])); + RecordBatch::try_new(schema, vec![ids, names, items, categories, blobs]).unwrap() + } + + fn stable_current_fixture(version: ConcreteFileVersion) -> &'static [u8] { + match version { + ConcreteFileVersion::V1 => unreachable!("v1 uses the legacy writer fixture"), + ConcreteFileVersion::V2_0 => { + include_bytes!("../test_data/exact_versions/v2_0.lance") + } + ConcreteFileVersion::V2_1 => { + include_bytes!("../test_data/exact_versions/v2_1.lance") + } + ConcreteFileVersion::V2_2 => { + include_bytes!("../test_data/exact_versions/v2_2.lance") + } + ConcreteFileVersion::V2_3 => { + unreachable!("v2.3 is unstable and does not have a compatibility fixture") + } + } + } + + fn assert_blob_column_eq(actual: &dyn Array, expected: &dyn Array) { + let actual = actual.as_binary::(); + let expected = expected.as_binary::(); + assert_eq!(actual.len(), expected.len()); + for index in 0..actual.len() { + assert_eq!( + actual.is_null(index), + expected.is_null(index), + "blob validity differs at row {index}" + ); + if actual.is_valid(index) { + assert_eq!( + actual.value(index), + expected.value(index), + "blob payload differs at row {index}" + ); + } + } + } + + fn assert_wire_bytes_equal(actual: &[u8], expected: &[u8]) { + if let Some(offset) = actual + .iter() + .zip(expected) + .position(|(actual, expected)| actual != expected) + { + panic!( + "wire fixture first differs at byte {offset}: actual={}, expected={}", + actual[offset], expected[offset] + ); + } + assert_eq!( + actual.len(), + expected.len(), + "wire fixture length changed after a common {}-byte prefix", + actual.len().min(expected.len()) + ); + } + + #[rstest] + #[case::v2_0(ConcreteFileVersion::V2_0)] + #[case::v2_1(ConcreteFileVersion::V2_1)] + #[case::v2_2(ConcreteFileVersion::V2_2)] + #[tokio::test] + async fn stable_current_writer_is_byte_compatible(#[case] version: ConcreteFileVersion) { + use futures::TryStreamExt; + use lance_encoding::decoder::FilterExpression; + + let batch = compatibility_fixture_batch(); + let mut schema = LanceSchema::try_from(batch.schema().as_ref()).unwrap(); + schema.set_dictionary(&batch).unwrap(); + let fs = FsFixture::default(); + let object_writer = fs.object_store.create(&fs.tmp_path).await.unwrap(); + let options = FileWriterOptions { + data_cache_bytes: Some(1), + max_page_bytes: Some(1024), + ..Default::default() + }; + let mut writer: FileWriter = match version { + ConcreteFileVersion::V1 => unreachable!(), + ConcreteFileVersion::V2_0 => { + versions::v2_0::create_writer(object_writer, schema.clone(), options) + .map(Into::into) + } + ConcreteFileVersion::V2_1 => { + versions::v2_1::create_writer(object_writer, schema.clone(), options) + .map(Into::into) + } + ConcreteFileVersion::V2_2 => { + versions::v2_2::create_writer(object_writer, schema.clone(), options) + .map(Into::into) + } + ConcreteFileVersion::V2_3 => unreachable!(), + } + .unwrap(); + for offset in (0..batch.num_rows()).step_by(1024) { + let slice = batch.slice(offset, (batch.num_rows() - offset).min(1024)); + writer.write_batch(&slice).await.unwrap(); + } + let summary = writer.finish().await.unwrap(); + let actual = fs + .object_store + .open(&fs.tmp_path) + .await + .unwrap() + .get_range(0..summary.size_bytes as usize) + .await + .unwrap(); + let expected = stable_current_fixture(version); + assert_wire_bytes_equal(actual.as_ref(), expected); + + let fixture_fs = FsFixture::default(); + let mut fixture_writer = fixture_fs + .object_store + .create(&fixture_fs.tmp_path) + .await + .unwrap(); + fixture_writer.write_all(expected).await.unwrap(); + Writer::shutdown(fixture_writer.as_mut()).await.unwrap(); + let scheduler = fixture_fs + .scheduler + .open_file( + &fixture_fs.tmp_path, + &CachedFileSize::new(expected.len() as u64), + ) + .await + .unwrap(); + let reader = FileReader::try_open( + scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + assert_eq!(reader.metadata().version, version); + assert!( + reader + .metadata() + .column_metadatas + .iter() + .any(|metadata| metadata.pages.len() > 1) + ); + let batches = reader + .read_stream( + lance_io::ReadBatchParams::RangeFull, + 1024, + 16, + FilterExpression::no_filter(), + ) + .await + .unwrap() + .try_collect::>() + .await + .unwrap(); + assert_eq!( + batches.iter().map(RecordBatch::num_rows).sum::(), + batch.num_rows() + ); + assert!( + batches + .iter() + .all(|actual| actual.schema_ref() == batch.schema_ref()) + ); + let mut row_offset = 0; + for actual in &batches { + let expected = batch.slice(row_offset, actual.num_rows()); + assert_blob_column_eq(actual.column(4).as_ref(), expected.column(4).as_ref()); + row_offset += actual.num_rows(); + } + assert_eq!(row_offset, batch.num_rows()); + } + + async fn write_v2_3_fixture(batch: &RecordBatch, schema: &LanceSchema) -> Vec { + let fs = FsFixture::default(); + let object_writer = fs.object_store.create(&fs.tmp_path).await.unwrap(); + let mut writer = versions::v2_3::create_writer( + object_writer, + schema.clone(), + FileWriterOptions { + data_cache_bytes: Some(1), + max_page_bytes: Some(1024), + ..Default::default() + }, + ) + .unwrap(); + for offset in (0..batch.num_rows()).step_by(1024) { + let slice = batch.slice(offset, (batch.num_rows() - offset).min(1024)); + writer.write_batch(&slice).await.unwrap(); + } + let summary = writer.finish().await.unwrap(); + fs.object_store + .open(&fs.tmp_path) + .await + .unwrap() + .get_range(0..summary.size_bytes as usize) + .await + .unwrap() + .to_vec() + } + + #[tokio::test] + async fn v2_3_writer_emits_current_exact_grammar() { + use futures::TryStreamExt; + use lance_encoding::decoder::FilterExpression; + + let batch = compatibility_fixture_batch(); + let mut schema = LanceSchema::try_from(batch.schema().as_ref()).unwrap(); + schema.set_dictionary(&batch).unwrap(); + let first = write_v2_3_fixture(&batch, &schema).await; + let second = write_v2_3_fixture(&batch, &schema).await; + assert_eq!(first, second); + assert_eq!( + &first[first.len() - 8..], + &[2, 0, 3, 0, b'L', b'A', b'N', b'C'] + ); + + let fs = FsFixture::default(); + let mut object_writer = fs.object_store.create(&fs.tmp_path).await.unwrap(); + object_writer.write_all(&first).await.unwrap(); + Writer::shutdown(object_writer.as_mut()).await.unwrap(); + let scheduler = fs + .scheduler + .open_file(&fs.tmp_path, &CachedFileSize::new(first.len() as u64)) + .await + .unwrap(); + let reader = FileReader::try_open( + scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + assert_eq!(reader.metadata().version, ConcreteFileVersion::V2_3); + assert!( + reader + .metadata() + .column_metadatas + .iter() + .any(|metadata| metadata.pages.len() > 1) + ); + let batches = reader + .read_stream( + lance_io::ReadBatchParams::RangeFull, + 1024, + 16, + FilterExpression::no_filter(), + ) + .await + .unwrap() + .try_collect::>() + .await + .unwrap(); + let mut row_offset = 0; + for actual in &batches { + let expected = batch.slice(row_offset, actual.num_rows()); + assert_blob_column_eq(actual.column(4).as_ref(), expected.column(4).as_ref()); + row_offset += actual.num_rows(); + } + assert_eq!(row_offset, batch.num_rows()); + } + + #[tokio::test] + async fn v1_writer_is_byte_compatible() { + use crate::versions::v1::reader::FileReader as V1Reader; + use crate::versions::v1::writer::{ + FileWriter as V1Writer, FileWriterOptions as V1WriterOptions, NotSelfDescribing, + }; + + let expected = include_bytes!("../test_data/exact_versions/v1.lance"); + let batch = compatibility_fixture_batch(); + let mut schema = LanceSchema::try_from(batch.schema().as_ref()).unwrap(); + schema.set_dictionary(&batch).unwrap(); + let fs = FsFixture::default(); + let mut writer = V1Writer::::try_new( + fs.object_store.as_ref(), + &fs.tmp_path, + schema.clone(), + &V1WriterOptions { + collect_stats_for_fields: Some(Vec::new()), + }, + ) + .await + .unwrap(); + for offset in (0..batch.num_rows()).step_by(1024) { + let slice = batch.slice(offset, (batch.num_rows() - offset).min(1024)); + writer.write(std::slice::from_ref(&slice)).await.unwrap(); + } + let summary = writer.finish().await.unwrap(); + let actual = fs + .object_store + .open(&fs.tmp_path) + .await + .unwrap() + .get_range(0..summary.size_bytes as usize) + .await + .unwrap(); + assert_wire_bytes_equal(actual.as_ref(), expected); + + let fixture_fs = FsFixture::default(); + let mut fixture_writer = fixture_fs + .object_store + .create(&fixture_fs.tmp_path) + .await + .unwrap(); + fixture_writer.write_all(expected).await.unwrap(); + Writer::shutdown(fixture_writer.as_mut()).await.unwrap(); + let reader = V1Reader::try_new( + fixture_fs.object_store.as_ref(), + &fixture_fs.tmp_path, + schema.clone(), + ) + .await + .unwrap(); + let actual_batch = reader + .read_range(0..batch.num_rows(), &schema) + .await + .unwrap(); + assert_eq!(reader.num_batches(), 5); + assert_eq!(actual_batch.num_rows(), batch.num_rows()); + assert_eq!(actual_batch.column(0).to_data(), batch.column(0).to_data()); + assert_eq!(actual_batch.column(1).to_data(), batch.column(1).to_data()); + assert_blob_column_eq(actual_batch.column(4).as_ref(), batch.column(4).as_ref()); + } + + #[tokio::test] + async fn test_basic_write() { + let tmp_path = TempObjFile::default(); + let obj_store = Arc::new(ObjectStore::local()); + + let reader = gen_batch() + .col("score", array::rand::()) + .into_reader_rows(RowCount::from(1000), BatchCount::from(10)); + + let writer = obj_store.create(&tmp_path).await.unwrap(); + + let lance_schema = + lance_core::datatypes::Schema::try_from(reader.schema().as_ref()).unwrap(); + + let mut file_writer = create_writer( + writer, + lance_schema, + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), + ) + .unwrap(); + + for batch in reader { + file_writer.write_batch(&batch.unwrap()).await.unwrap(); + } + file_writer.add_schema_metadata("foo", "bar"); + file_writer.finish().await.unwrap(); + // Tests asserting the contents of the written file are in reader.rs + } + + #[tokio::test] + async fn test_write_empty() { + let tmp_path = TempObjFile::default(); + let obj_store = Arc::new(ObjectStore::local()); + + let reader = gen_batch() + .col("score", array::rand::()) + .into_reader_rows(RowCount::from(0), BatchCount::from(0)); + + let writer = obj_store.create(&tmp_path).await.unwrap(); + + let lance_schema = + lance_core::datatypes::Schema::try_from(reader.schema().as_ref()).unwrap(); + + let mut file_writer = create_writer( + writer, + lance_schema, + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), + ) + .unwrap(); + + for batch in reader { + file_writer.write_batch(&batch.unwrap()).await.unwrap(); + } + file_writer.add_schema_metadata("foo", "bar"); + file_writer.finish().await.unwrap(); + } + + // Read a single column back at an explicit range/index set, returning its + // `Int32` values. Reading one column (or an equal-length group) at a time is + // how unequal-length files are consumed: a full scan across columns of + // differing lengths cannot form a single rectangular batch. + async fn read_int32_column( + reader: &FileReader, + schema: &LanceSchema, + version: ConcreteFileVersion, + name: &str, + params: lance_io::ReadBatchParams, + ) -> Vec> { + use futures::TryStreamExt; + use lance_encoding::decoder::FilterExpression; + + let projection = + versions::reader_projection_from_column_names(version, schema, &[name]).unwrap(); + let batches: Vec = reader + .read_stream_projected(params, 1024, 16, projection, FilterExpression::no_filter()) + .await + .unwrap() + .try_collect() + .await + .unwrap(); + batches + .iter() + .flat_map(|b| { + b.column(0) + .as_any() + .downcast_ref::() + .unwrap() + .iter() + .collect::>() + }) + .collect() + } + + /// A single file may hold columns of differing item counts, written by + /// advancing each column's row counter independently (no shared global + /// counter). + #[rstest] + #[tokio::test] + async fn test_write_columns_unequal_lengths( + #[values(ConcreteFileVersion::V2_0, ConcreteFileVersion::V2_1)] + version: ConcreteFileVersion, + ) { + use lance_io::ReadBatchParams; + + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("a", DataType::Int32, true), + ArrowField::new("b", DataType::Int32, true), + ArrowField::new("c", DataType::Int32, true), + ])); + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + + let fs = FsFixture::default(); + let mut writer = create_writer( + fs.object_store.create(&fs.tmp_path).await.unwrap(), + lance_schema.clone(), + version, + FileWriterOptions::default(), + ) + .unwrap(); + + // Field "a" gets 5 values across two calls (appending), field "b" gets a + // single value, and field "c" is never written (a zero-length column). + let a1: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); + let b: ArrayRef = Arc::new(Int32Array::from(vec![10])); + writer.write_column(0, a1).await.unwrap(); + writer.write_column(1, b).await.unwrap(); + let a2: ArrayRef = Arc::new(Int32Array::from(vec![4, 5])); + writer.write_column(0, a2).await.unwrap(); + // An empty array is a no-op whether or not the field already has rows: + // field "a" keeps its 5 rows, field "c" stays a zero-length column. + let empty: ArrayRef = Arc::new(Int32Array::from(Vec::::new())); + writer.write_column(0, empty.clone()).await.unwrap(); + writer.write_column(2, empty).await.unwrap(); + + let summary = writer.finish().await.unwrap(); + // The file's logical length is the longest column. + assert_eq!(summary.num_rows, 5); + + let file_scheduler = fs + .scheduler + .open_file(&fs.tmp_path, &CachedFileSize::unknown()) + .await + .unwrap(); + let reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + // Per-column row counts are recorded in / derivable from file metadata. + assert_eq!(reader.num_rows(), 5); + assert_eq!(reader.column_num_rows(0).unwrap(), 5); + assert_eq!(reader.column_num_rows(1).unwrap(), 1); + assert_eq!(reader.column_num_rows(2).unwrap(), 0); + assert!(reader.column_num_rows(3).is_err()); + + // Each column reads back independently at its own length. + assert_eq!( + read_int32_column( + &reader, + &lance_schema, + version, + "a", + ReadBatchParams::Range(0..5) + ) + .await, + vec![Some(1), Some(2), Some(3), Some(4), Some(5)], + ); + assert_eq!( + read_int32_column( + &reader, + &lance_schema, + version, + "b", + ReadBatchParams::Range(0..1) + ) + .await, + vec![Some(10)], + ); + + // Random access by position within the longer column returns the right + // value even though other columns are shorter. (The take path requires + // strictly increasing indices.) + assert_eq!( + read_int32_column( + &reader, + &lance_schema, + version, + "a", + ReadBatchParams::Indices(arrow_array::UInt32Array::from(vec![0, 2, 4])), + ) + .await, + vec![Some(1), Some(3), Some(5)], + ); + } + + /// Reading an unequal-length file: + /// - a projection whose columns are equal length full-scans normally; + /// - a full scan across columns of differing length is rejected up front, + /// before any batch is produced (even though a prefix would be rectangular); + /// - a bounded read is valid as long as every projected column covers it; + /// - a single-column `RangeFull` resolves to that column's own length, not + /// the file's (maximum) length. + #[rstest] + #[tokio::test] + async fn test_read_unequal_length_projection( + #[values(ConcreteFileVersion::V2_0, ConcreteFileVersion::V2_1)] + version: ConcreteFileVersion, + ) { + use futures::TryStreamExt; + use lance_encoding::decoder::FilterExpression; + use lance_io::ReadBatchParams; + + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("a", DataType::Int32, true), + ArrowField::new("b", DataType::Int32, true), + ArrowField::new("c", DataType::Int32, true), + ])); + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + let fs = FsFixture::default(); + let mut writer = create_writer( + fs.object_store.create(&fs.tmp_path).await.unwrap(), + lance_schema.clone(), + version, + FileWriterOptions::default(), + ) + .unwrap(); + // "a" and "b" are equal length (5); "c" is shorter (1). + writer + .write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5]))) + .await + .unwrap(); + writer + .write_column(1, Arc::new(Int32Array::from(vec![6, 7, 8, 9, 10]))) + .await + .unwrap(); + writer + .write_column(2, Arc::new(Int32Array::from(vec![100]))) + .await + .unwrap(); + writer.finish().await.unwrap(); + + let file_scheduler = fs + .scheduler + .open_file(&fs.tmp_path, &CachedFileSize::unknown()) + .await + .unwrap(); + let reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + let read = |names: &'static [&'static str], params: ReadBatchParams| { + let projection = + versions::reader_projection_from_column_names(version, &lance_schema, names) + .unwrap(); + async { + match reader + .read_stream_projected( + params, + 1024, + 16, + projection, + FilterExpression::no_filter(), + ) + .await + { + Ok(stream) => stream.try_collect::>().await, + Err(e) => Err(e), + } + } + }; + let col_values = |batches: &[RecordBatch], idx: usize| -> Vec> { + batches + .iter() + .flat_map(|b| { + b.column(idx) + .as_any() + .downcast_ref::() + .unwrap() + .iter() + .collect::>() + }) + .collect() + }; + + // Equal-length projection [a, b] full-scans into rectangular batches. + let batches = read(&["a", "b"], ReadBatchParams::RangeFull).await.unwrap(); + assert_eq!( + col_values(&batches, 0), + vec![Some(1), Some(2), Some(3), Some(4), Some(5)] + ); + assert_eq!( + col_values(&batches, 1), + vec![Some(6), Some(7), Some(8), Some(9), Some(10)] + ); + + // A mismatched-length projection [a, c] (5 vs 1) is rejected before any + // batch is yielded, regardless of the read params — its columns cannot + // be combined into rectangular batches. The error names each column's + // length so the caller can see which column is the odd one out. + let err = read(&["a", "c"], ReadBatchParams::RangeFull) + .await + .unwrap_err() + .to_string(); + assert!( + err.contains("a=5") && err.contains("c=1"), + "error should name each column's length, got: {err}" + ); + assert!( + read(&["a", "c"], ReadBatchParams::Range(0..1)) + .await + .is_err(), + "even a common-prefix read of unequal-length columns must error" + ); + + // A single-column RangeFull resolves to that column's own length. + let batches = read(&["c"], ReadBatchParams::RangeFull).await.unwrap(); + assert_eq!(col_values(&batches, 0), vec![Some(100)]); + let batches = read(&["a"], ReadBatchParams::RangeFull).await.unwrap(); + assert_eq!( + col_values(&batches, 0), + vec![Some(1), Some(2), Some(3), Some(4), Some(5)] + ); + + // RangeFrom/RangeTo likewise resolve against the projected column's own + // length rather than the file's longest column. + let batches = read(&["a"], ReadBatchParams::RangeFrom(2..)).await.unwrap(); + assert_eq!(col_values(&batches, 0), vec![Some(3), Some(4), Some(5)]); + // RangeFrom on the short column "c" resolves to length 1, not 5. + let batches = read(&["c"], ReadBatchParams::RangeFrom(0..)).await.unwrap(); + assert_eq!(col_values(&batches, 0), vec![Some(100)]); + let batches = read(&["a"], ReadBatchParams::RangeTo(..3)).await.unwrap(); + assert_eq!(col_values(&batches, 0), vec![Some(1), Some(2), Some(3)]); + // A bound past the projected column's length errors. + assert!( + read(&["a"], ReadBatchParams::RangeTo(..6)).await.is_err(), + "RangeTo past the column length must error" + ); + assert!( + read(&["c"], ReadBatchParams::RangeFrom(2..)).await.is_err(), + "RangeFrom past the column length must error" + ); + } + + /// A struct and a list column each map to multiple physical columns, and a + /// list's item column is longer than its top-level row count. The + /// projection-length check must partition `column_indices` by top-level + /// field and use each field's root column, so an ordinary (rectangular) file + /// with nested columns still reads under the new validation path. + #[rstest] + #[tokio::test] + async fn test_read_nested_columns_under_validation( + #[values(ConcreteFileVersion::V2_0, ConcreteFileVersion::V2_1)] + version: ConcreteFileVersion, + ) { + use arrow_array::types::Int32Type; + use arrow_array::{ListArray, StructArray}; + use futures::TryStreamExt; + use lance_encoding::decoder::FilterExpression; + use lance_io::ReadBatchParams; + + let struct_type = DataType::Struct( + vec![ + ArrowField::new("x", DataType::Int32, true), + ArrowField::new("y", DataType::Int32, true), + ] + .into(), + ); + let list_type = DataType::List(Arc::new(ArrowField::new("item", DataType::Int32, true))); + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("a", DataType::Int32, true), + ArrowField::new("s", struct_type, true), + ArrowField::new("lst", list_type, true), + ])); + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + + let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); + let s: ArrayRef = Arc::new(StructArray::from(vec![ + ( + Arc::new(ArrowField::new("x", DataType::Int32, true)), + Arc::new(Int32Array::from(vec![10, 20, 30])) as ArrayRef, + ), + ( + Arc::new(ArrowField::new("y", DataType::Int32, true)), + Arc::new(Int32Array::from(vec![11, 21, 31])) as ArrayRef, + ), + ])); + // 3 lists, 6 items: the item column is longer than the top-level rows. + let lst: ArrayRef = Arc::new(ListArray::from_iter_primitive::(vec![ + Some(vec![Some(1), Some(2)]), + Some(vec![Some(3)]), + Some(vec![Some(4), Some(5), Some(6)]), + ])); + let batch = RecordBatch::try_new(arrow_schema.clone(), vec![a, s, lst]).unwrap(); + + let fs = FsFixture::default(); + let mut writer = create_writer( + fs.object_store.create(&fs.tmp_path).await.unwrap(), + lance_schema.clone(), + version, + FileWriterOptions::default(), + ) + .unwrap(); + writer.write_batch(&batch).await.unwrap(); + writer.finish().await.unwrap(); + + let file_scheduler = fs + .scheduler + .open_file(&fs.tmp_path, &CachedFileSize::unknown()) + .await + .unwrap(); + let reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + // If `validate_field_length` mispartitioned the physical columns, the + // length check would read the wrong root column (e.g. the list's item + // column, length 6) and spuriously reject this rectangular file. + for names in [&["a", "s", "lst"][..], &["a", "lst"][..], &["a", "s"][..]] { + let projection = + versions::reader_projection_from_column_names(version, &lance_schema, names) + .unwrap(); + let batches: Vec = reader + .read_stream_projected( + ReadBatchParams::RangeFull, + 1024, + 16, + projection, + FilterExpression::no_filter(), + ) + .await + .unwrap() + .try_collect() + .await + .unwrap(); + let total_rows: usize = batches.iter().map(|b| b.num_rows()).sum(); + assert_eq!( + total_rows, 3, + "projection {names:?} should read 3 top-level rows" + ); + } + } + + /// `write_column` rejects invalid inputs at the API boundary with + /// descriptive errors: a writer without an explicit schema, an + /// out-of-bounds field index, and a null written into a non-nullable field. + #[tokio::test] + async fn test_write_column_validation_errors() { + // A lazy-schema writer cannot infer the schema from a single column. + let fs = FsFixture::default(); + let mut lazy_writer = versions::v2_1::create_lazy_writer( + fs.object_store.create(&fs.tmp_path).await.unwrap(), + FileWriterOptions::default(), + ); + let err = lazy_writer + .write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3]))) + .await + .unwrap_err() + .to_string(); + assert!( + err.contains("explicit schema"), + "expected explicit-schema error, got: {err}" + ); + + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("a", DataType::Int32, false), + ArrowField::new("b", DataType::Int32, true), + ])); + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + + // An out-of-bounds field index is rejected, naming the index and count. + let fs = FsFixture::default(); + let mut writer = create_writer( + fs.object_store.create(&fs.tmp_path).await.unwrap(), + lance_schema.clone(), + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), + ) + .unwrap(); + let err = writer + .write_column(5, Arc::new(Int32Array::from(vec![1]))) + .await + .unwrap_err() + .to_string(); + assert!( + err.contains('5') && err.contains('2'), + "expected out-of-bounds error naming index 5 and 2 fields, got: {err}" + ); + + // A null in a non-nullable field ("a") is rejected. + let err = writer + .write_column(0, Arc::new(Int32Array::from(vec![Some(1), None, Some(3)]))) + .await + .unwrap_err() + .to_string(); + assert!( + err.contains("non-null"), + "expected nullability error, got: {err}" + ); + } + + /// The blocking read path applies the same projection-length validation as + /// the async path: a short single column resolves to its own length, and a + /// mismatched-length projection errors up front. + #[rstest] + #[tokio::test] + async fn test_blocking_read_unequal_length( + #[values(ConcreteFileVersion::V2_0, ConcreteFileVersion::V2_1)] + version: ConcreteFileVersion, + ) { + use lance_encoding::decoder::FilterExpression; + use lance_io::ReadBatchParams; + + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("a", DataType::Int32, true), + ArrowField::new("c", DataType::Int32, true), + ])); + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + let fs = FsFixture::default(); + let mut writer = create_writer( + fs.object_store.create(&fs.tmp_path).await.unwrap(), + lance_schema.clone(), + version, + FileWriterOptions::default(), + ) + .unwrap(); + writer + .write_column(0, Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5]))) + .await + .unwrap(); + writer + .write_column(1, Arc::new(Int32Array::from(vec![100]))) + .await + .unwrap(); + writer.finish().await.unwrap(); + + let file_scheduler = fs + .scheduler + .open_file(&fs.tmp_path, &CachedFileSize::unknown()) + .await + .unwrap(); + let reader = Arc::new( + FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(), + ); + + // Single short column: RangeFull resolves to its own length (1). + let proj_c = + versions::reader_projection_from_column_names(version, &lance_schema, &["c"]).unwrap(); + let reader_c = reader.clone(); + let batches = tokio::task::spawn_blocking(move || { + reader_c + .read_stream_projected_blocking( + ReadBatchParams::RangeFull, + 1024, + Some(proj_c), + FilterExpression::no_filter(), + ) + .unwrap() + .collect::, _>>() + .unwrap() + }) + .await + .unwrap(); + let total_rows: usize = batches.iter().map(|b| b.num_rows()).sum(); + assert_eq!(total_rows, 1); + + // A mismatched projection [a, c] errors on the blocking path too. + let proj_ac = + versions::reader_projection_from_column_names(version, &lance_schema, &["a", "c"]) + .unwrap(); + let reader_ac = reader.clone(); + let is_err = tokio::task::spawn_blocking(move || { + reader_ac + .read_stream_projected_blocking( + ReadBatchParams::RangeFull, + 1024, + Some(proj_ac), + FilterExpression::no_filter(), + ) + .is_err() + }) + .await + .unwrap(); + assert!( + is_err, + "blocking full scan across unequal-length columns must error" + ); + } + + /// Files written the ordinary (rectangular) way keep equal column lengths, + /// so the unequal-length support is backwards compatible. + #[tokio::test] + async fn test_write_batch_keeps_equal_lengths() { + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("a", DataType::Int32, true), + ArrowField::new("b", DataType::Int32, true), + ])); + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + + let fs = FsFixture::default(); + let mut writer = create_writer( + fs.object_store.create(&fs.tmp_path).await.unwrap(), + lance_schema, + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), + ) + .unwrap(); + let batch = RecordBatch::try_new( + arrow_schema.clone(), + vec![ + Arc::new(Int32Array::from(vec![1, 2, 3])), + Arc::new(Int32Array::from(vec![4, 5, 6])), + ], + ) + .unwrap(); + writer.write_batch(&batch).await.unwrap(); + let summary = writer.finish().await.unwrap(); + assert_eq!(summary.num_rows, 3); + + let file_scheduler = fs + .scheduler + .open_file(&fs.tmp_path, &CachedFileSize::unknown()) + .await + .unwrap(); + let reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + assert_eq!(reader.column_num_rows(0).unwrap(), 3); + assert_eq!(reader.column_num_rows(1).unwrap(), 3); + } + + #[tokio::test] + async fn test_max_page_bytes_enforced() { + let arrow_field = Field::new("data", DataType::UInt64, false); + let arrow_schema = Schema::new(vec![arrow_field]); + let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); + + // 8MiB + let data: Vec = (0..1_000_000).collect(); + let array = UInt64Array::from(data); + let batch = + RecordBatch::try_new(arrow_schema.clone().into(), vec![Arc::new(array)]).unwrap(); + + let options = FileWriterOptions { + max_page_bytes: Some(1024 * 1024), // 1MB + ..Default::default() + }; + + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + let mut writer = create_writer( + object_store.create(&path).await.unwrap(), + lance_schema, + ConcreteFileVersion::V2_0, + options, + ) + .unwrap(); + + writer.write_batch(&batch).await.unwrap(); + writer.finish().await.unwrap(); + + let fs = FsFixture::default(); + let file_scheduler = fs + .scheduler + .open_file(&path, &CachedFileSize::unknown()) + .await + .unwrap(); + let file_reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + let column_meta = file_reader.metadata(); + + let mut total_page_num: u32 = 0; + for (col_idx, col_metadata) in column_meta.column_metadatas.iter().enumerate() { + assert!( + !col_metadata.pages.is_empty(), + "Column {} has no pages", + col_idx + ); + + for (page_idx, page) in col_metadata.pages.iter().enumerate() { + total_page_num += 1; + let total_size: u64 = page.buffer_sizes.iter().sum(); + assert!( + total_size <= 1024 * 1024, + "Column {} Page {} size {} exceeds 1MB limit", + col_idx, + page_idx, + total_size + ); + } + } + + assert_eq!(total_page_num, 8) + } + + #[tokio::test(flavor = "current_thread")] + async fn test_max_page_bytes_env_var() { + let arrow_field = Field::new("data", DataType::UInt64, false); + let arrow_schema = Schema::new(vec![arrow_field]); + let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); + // 4MiB + let data: Vec = (0..500_000).collect(); + let array = UInt64Array::from(data); + let batch = + RecordBatch::try_new(arrow_schema.clone().into(), vec![Arc::new(array)]).unwrap(); + + // 2MiB + unsafe { + std::env::set_var(ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, "2097152"); + } + + let options = FileWriterOptions { + max_page_bytes: None, // enforce env + ..Default::default() + }; + + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + let mut writer = create_writer( + object_store.create(&path).await.unwrap(), + lance_schema.clone(), + ConcreteFileVersion::V2_1, + options, + ) + .unwrap(); + + writer.write_batch(&batch).await.unwrap(); + writer.finish().await.unwrap(); + + let fs = FsFixture::default(); + let file_scheduler = fs + .scheduler + .open_file(&path, &CachedFileSize::unknown()) + .await + .unwrap(); + let file_reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + for col_metadata in file_reader.metadata().column_metadatas.iter() { + for page in col_metadata.pages.iter() { + let total_size: u64 = page.buffer_sizes.iter().sum(); + assert!( + total_size <= 2 * 1024 * 1024, + "Page size {} exceeds 2MB limit", + total_size + ); + } + } + + unsafe { + std::env::set_var(ENV_LANCE_FILE_WRITER_MAX_PAGE_BYTES, ""); + } + } + + #[tokio::test] + async fn test_compression_overrides_end_to_end() { + // Create test schema with different column types + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("customer_id", DataType::Int32, false), + ArrowField::new("product_id", DataType::Int32, false), + ArrowField::new("quantity", DataType::Int32, false), + ArrowField::new("price", DataType::Float32, false), + ArrowField::new("description", DataType::Utf8, false), + ])); + + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + + // Create test data with patterns suitable for different compression + let mut customer_ids = Int32Builder::new(); + let mut product_ids = Int32Builder::new(); + let mut quantities = Int32Builder::new(); + let mut prices = Float32Builder::new(); + let mut descriptions = Vec::new(); + + // Generate data with specific patterns: + // - customer_id: highly repetitive (good for RLE) + // - product_id: moderately repetitive (good for RLE) + // - quantity: random values (not good for RLE) + // - price: some repetition + // - description: long strings (good for Zstd) + for i in 0..10000 { + // Customer ID repeats every 100 rows (100 unique customers) + // This creates runs of 100 identical values + customer_ids.append_value(i / 100); + + // Product ID has only 5 unique values with long runs + product_ids.append_value(i / 2000); + + // Quantity is mostly 1 with occasional other values + quantities.append_value(if i % 10 == 0 { 5 } else { 1 }); + + // Price has only 3 unique values + prices.append_value(match i % 3 { + 0 => 9.99, + 1 => 19.99, + _ => 29.99, + }); + + // Descriptions are repetitive but we'll keep them simple + descriptions.push(format!("Product {}", i / 2000)); + } + + let batch = RecordBatch::try_new( + arrow_schema.clone(), + vec![ + Arc::new(customer_ids.finish()), + Arc::new(product_ids.finish()), + Arc::new(quantities.finish()), + Arc::new(prices.finish()), + Arc::new(StringArray::from(descriptions)), + ], + ) + .unwrap(); + + // Configure compression parameters + let mut params = CompressionParams::new(); + + // RLE for ID columns (ends with _id) + params.columns.insert( + "*_id".to_string(), + CompressionFieldParams { + rle_threshold: Some(0.5), // Lower threshold to trigger RLE more easily + compression: None, // Will use default compression if any + compression_level: None, + bss: Some(lance_encoding::compression_config::BssMode::Off), // Explicitly disable BSS to ensure RLE is used + minichunk_size: None, + }, + ); + + // For now, we'll skip Zstd compression since it's not imported + // In a real implementation, you could add other compression types here + + // Configure file writer options + let options = FileWriterOptions { + max_page_bytes: Some(64 * 1024), // 64KB pages + ..Default::default() + }; + + // Write the file + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + + let mut writer = create_v2_1_writer_with_compression( + object_store.create(&path).await.unwrap(), + lance_schema.clone(), + options, + params, + ) + .unwrap(); + + writer.write_batch(&batch).await.unwrap(); + writer.add_schema_metadata("compression_test", "configured_compression"); + writer.finish().await.unwrap(); + + // Now write the same data without compression overrides for comparison + let path_no_compression = TempObjFile::default(); + let default_options = FileWriterOptions { + max_page_bytes: Some(64 * 1024), + ..Default::default() + }; + + let mut writer_no_compression = create_writer( + object_store.create(&path_no_compression).await.unwrap(), + lance_schema.clone(), + ConcreteFileVersion::V2_1, + default_options, + ) + .unwrap(); + + writer_no_compression.write_batch(&batch).await.unwrap(); + writer_no_compression.finish().await.unwrap(); + + // Note: With our current data patterns and RLE compression, the compressed file + // might actually be slightly larger due to compression metadata overhead. + // This is expected and the test is mainly to verify the system works end-to-end. + + // Read back the compressed file and verify data integrity + let fs = FsFixture::default(); + let file_scheduler = fs + .scheduler + .open_file(&path, &CachedFileSize::unknown()) + .await + .unwrap(); + + let file_reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + // Verify metadata + let metadata = file_reader.metadata(); + assert_eq!(metadata.version, ConcreteFileVersion::V2_1); + + let schema = file_reader.schema(); + assert_eq!( + schema.metadata.get("compression_test"), + Some(&"configured_compression".to_string()) + ); + + // Verify the actual encodings used + let column_metadatas = &metadata.column_metadatas; + + // Check customer_id column (index 0) - should use RLE due to our configuration + assert!(!column_metadatas[0].pages.is_empty()); + let customer_id_encoding = describe_encoding(&column_metadatas[0].pages[0]); + assert!( + customer_id_encoding.contains("RLE") || customer_id_encoding.contains("Rle"), + "customer_id column should use RLE encoding due to '*_id' pattern match, but got: {}", + customer_id_encoding + ); + + // Check product_id column (index 1) - should use RLE due to our configuration + assert!(!column_metadatas[1].pages.is_empty()); + let product_id_encoding = describe_encoding(&column_metadatas[1].pages[0]); + assert!( + product_id_encoding.contains("RLE") || product_id_encoding.contains("Rle"), + "product_id column should use RLE encoding due to '*_id' pattern match, but got: {}", + product_id_encoding + ); + } + + #[tokio::test] + async fn test_field_metadata_compression() { + // Test that field metadata compression settings are respected + let mut metadata = HashMap::new(); + metadata.insert( + lance_encoding::constants::COMPRESSION_META_KEY.to_string(), + "zstd".to_string(), + ); + metadata.insert( + lance_encoding::constants::COMPRESSION_LEVEL_META_KEY.to_string(), + "6".to_string(), + ); + + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("id", DataType::Int32, false), + ArrowField::new("text", DataType::Utf8, false).with_metadata(metadata.clone()), + ArrowField::new("data", DataType::Int32, false).with_metadata(HashMap::from([( + lance_encoding::constants::COMPRESSION_META_KEY.to_string(), + "none".to_string(), + )])), + ])); + + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + + // Create test data + let id_array = Int32Array::from_iter_values(0..1000); + let text_array = StringArray::from_iter_values( + (0..1000).map(|i| format!("test string {} repeated text", i)), + ); + let data_array = Int32Array::from_iter_values((0..1000).map(|i| i * 2)); + + let batch = RecordBatch::try_new( + arrow_schema.clone(), + vec![ + Arc::new(id_array), + Arc::new(text_array), + Arc::new(data_array), + ], + ) + .unwrap(); + + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + + // Create encoding strategy that will read from field metadata + let params = CompressionParams::new(); + let options = FileWriterOptions::default(); + let mut writer = create_v2_1_writer_with_compression( + object_store.create(&path).await.unwrap(), + lance_schema.clone(), + options, + params, + ) + .unwrap(); + + writer.write_batch(&batch).await.unwrap(); + writer.finish().await.unwrap(); + + // Read back metadata + let fs = FsFixture::default(); + let file_scheduler = fs + .scheduler + .open_file(&path, &CachedFileSize::unknown()) + .await + .unwrap(); + let file_reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + let column_metadatas = &file_reader.metadata().column_metadatas; + + // The text column (index 1) should use zstd compression based on metadata + let text_encoding = describe_encoding(&column_metadatas[1].pages[0]); + // For string columns, we expect Binary encoding with zstd compression + assert!( + text_encoding.contains("Zstd"), + "text column should use zstd compression from field metadata, but got: {}", + text_encoding + ); + + // The data column (index 2) should use no compression based on metadata + let data_encoding = describe_encoding(&column_metadatas[2].pages[0]); + // For Int32 columns with "none" compression, we expect Flat encoding without compression + assert!( + data_encoding.contains("Flat") && data_encoding.contains("compression: None"), + "data column should use no compression from field metadata, but got: {}", + data_encoding + ); + } + + #[tokio::test] + async fn test_field_metadata_rle_threshold() { + // Test that RLE threshold from field metadata is respected + let mut metadata = HashMap::new(); + metadata.insert( + lance_encoding::constants::RLE_THRESHOLD_META_KEY.to_string(), + "0.9".to_string(), + ); + // Also set compression to ensure RLE is used + metadata.insert( + lance_encoding::constants::COMPRESSION_META_KEY.to_string(), + "lz4".to_string(), + ); + // Explicitly disable BSS to ensure RLE is tested + metadata.insert( + lance_encoding::constants::BSS_META_KEY.to_string(), + "off".to_string(), + ); + + let arrow_schema = Arc::new(ArrowSchema::new(vec![ + ArrowField::new("status", DataType::Int32, false).with_metadata(metadata), + ])); + + let lance_schema = LanceSchema::try_from(arrow_schema.as_ref()).unwrap(); + + // Create data with very high repetition (3 runs for 10000 values = 0.0003 ratio) + let status_array = Int32Array::from_iter_values( + std::iter::repeat_n(200, 8000) + .chain(std::iter::repeat_n(404, 1500)) + .chain(std::iter::repeat_n(500, 500)), + ); + + let batch = + RecordBatch::try_new(arrow_schema.clone(), vec![Arc::new(status_array)]).unwrap(); + + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + + // Create encoding strategy that will read from field metadata + let params = CompressionParams::new(); + let options = FileWriterOptions::default(); + let mut writer = create_v2_1_writer_with_compression( + object_store.create(&path).await.unwrap(), + lance_schema.clone(), + options, + params, + ) + .unwrap(); + + writer.write_batch(&batch).await.unwrap(); + writer.finish().await.unwrap(); + + // Read back and check encoding + let fs = FsFixture::default(); + let file_scheduler = fs + .scheduler + .open_file(&path, &CachedFileSize::unknown()) + .await + .unwrap(); + let file_reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + FileReaderOptions::default(), + ) + .await + .unwrap(); + + let column_metadatas = &file_reader.metadata().column_metadatas; + let status_encoding = describe_encoding(&column_metadatas[0].pages[0]); + assert!( + status_encoding.contains("RLE") || status_encoding.contains("Rle"), + "status column should use RLE encoding due to metadata threshold, but got: {}", + status_encoding + ); + } + + #[tokio::test] + async fn test_large_page_split_on_read() { + use arrow_array::Array; + use futures::TryStreamExt; + use lance_encoding::decoder::FilterExpression; + use lance_io::ReadBatchParams; + + // Test that large pages written with relaxed limits can be split during read + + let arrow_field = ArrowField::new("data", DataType::Binary, false); + let arrow_schema = ArrowSchema::new(vec![arrow_field]); + let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); + + // Create a large binary value (40MB) to trigger large page creation + let large_value = vec![42u8; 40 * 1024 * 1024]; + let array = arrow_array::BinaryArray::from(vec![ + Some(large_value.as_slice()), + Some(b"small value"), + ]); + let batch = RecordBatch::try_new(Arc::new(arrow_schema), vec![Arc::new(array)]).unwrap(); + + // Write with relaxed page size limit (128MB) + let options = FileWriterOptions { + max_page_bytes: Some(128 * 1024 * 1024), + ..Default::default() + }; + + let fs = FsFixture::default(); + let path = fs.tmp_path; + + let mut writer = create_writer( + fs.object_store.create(&path).await.unwrap(), + lance_schema.clone(), + ConcreteFileVersion::V2_1, + options, + ) + .unwrap(); + + writer.write_batch(&batch).await.unwrap(); + let write_summary = writer.finish().await.unwrap(); + assert_eq!(write_summary.num_rows, 2); + assert_eq!( + write_summary.size_bytes, + fs.object_store.size(&path).await.unwrap() + ); + + // Read back with split configuration + let file_scheduler = fs + .scheduler + .open_file(&path, &CachedFileSize::unknown()) + .await + .unwrap(); + + // Configure reader to split pages larger than 10MB into chunks + let reader_options = FileReaderOptions { + read_chunk_size: 10 * 1024 * 1024, // 10MB chunks + ..Default::default() + }; + + let file_reader = FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &LanceCache::no_cache(), + reader_options, + ) + .await + .unwrap(); + + // Read the data back + let stream = file_reader + .read_stream( + ReadBatchParams::RangeFull, + 1024, + 10, // batch_readahead + FilterExpression::no_filter(), + ) + .await + .unwrap(); + + let batches: Vec = stream.try_collect().await.unwrap(); + assert_eq!(batches.len(), 1); + + // Verify the data is correctly read despite splitting + let read_array = batches[0].column(0); + let read_binary = read_array + .as_any() + .downcast_ref::() + .unwrap(); + + assert_eq!(read_binary.len(), 2); + assert_eq!(read_binary.value(0).len(), 40 * 1024 * 1024); + assert_eq!(read_binary.value(1), b"small value"); + + // Verify first value matches what we wrote + assert!(read_binary.value(0).iter().all(|&b| b == 42u8)); + } + + fn spill_config() -> (TempObjFile, Arc) { + let spill_path = TempObjFile::default(); + (spill_path, Arc::new(ObjectStore::local())) + } + + fn make_batches(num_batches: i32, num_cols: usize, rows_per_batch: i32) -> Vec { + let fields: Vec<_> = (0..num_cols) + .map(|c| ArrowField::new(format!("c{c}"), DataType::Int32, false)) + .collect(); + let schema = Arc::new(ArrowSchema::new(fields)); + (0..num_batches) + .map(|i| { + let cols: Vec> = (0..num_cols) + .map(|c| { + let start = (i * rows_per_batch + c as i32) * 100; + Arc::new(Int32Array::from_iter_values(start..start + rows_per_batch)) + as Arc + }) + .collect(); + RecordBatch::try_new(schema.clone(), cols).unwrap() + }) + .collect() + } + + async fn write_and_read_batches( + batches: &[RecordBatch], + spill: Option<(Arc, object_store::path::Path)>, + ) -> Vec { + let fs = FsFixture::default(); + let lance_schema = LanceSchema::try_from(batches[0].schema().as_ref()).unwrap(); + let writer = fs.object_store.create(&fs.tmp_path).await.unwrap(); + let mut file_writer = create_writer( + writer, + lance_schema, + ConcreteFileVersion::V2_1, + FileWriterOptions::default(), + ) + .unwrap(); + if let Some((store, path)) = spill { + file_writer = file_writer.with_page_metadata_spill(store, path); + } + for batch in batches { + file_writer.write_batch(batch).await.unwrap(); + } + file_writer.add_schema_metadata("foo", "bar"); + file_writer.finish().await.unwrap(); + + crate::testing::read_lance_file( + &fs, + Arc::::default(), + lance_encoding::decoder::FilterExpression::no_filter(), + ) + .await + } + + #[rstest::rstest] + #[case::multi_col(20, 2, 100)] + #[case::many_batches(50, 2, 100)] + #[tokio::test] + async fn test_page_metadata_spill_roundtrip( + #[case] num_batches: i32, + #[case] num_cols: usize, + #[case] rows_per_batch: i32, + ) { + let batches = make_batches(num_batches, num_cols, rows_per_batch); + let baseline = write_and_read_batches(&batches, None).await; + let (spill_path, spill_store) = spill_config(); + let spilled = + write_and_read_batches(&batches, Some((spill_store, spill_path.as_ref().clone()))) + .await; + assert_eq!(baseline, spilled); + } + + #[tokio::test] + async fn test_page_metadata_spill_many_columns() { + // Many columns forces small per-column buffer limits, exercising mid-write flushing. + let batches = make_batches(10, 500, 100); + let baseline = write_and_read_batches(&batches, None).await; + let (spill_path, spill_store) = spill_config(); + let spilled = + write_and_read_batches(&batches, Some((spill_store, spill_path.as_ref().clone()))) + .await; + assert_eq!(baseline, spilled); + } +} diff --git a/rust/lance-file/test_data/exact_versions/README.md b/rust/lance-file/test_data/exact_versions/README.md new file mode 100644 index 00000000000..fd63b1284ab --- /dev/null +++ b/rust/lance-file/test_data/exact_versions/README.md @@ -0,0 +1,26 @@ +# Exact File-Version Compatibility Fixtures + +These files were generated with the writers at baseline commit +`3a72f8a61e14613f517dded6816d4bfc77817c93`. The deterministic input batch is +defined by `compatibility_fixture_batch` in `src/writer_tests.rs` and covers +primitive, nullable UTF-8, nullable list, nullable dictionary, blob, multiple +input batches, and multiple pages. + +The baseline generator was run twice in separate processes with a Cargo target +directory isolated from the refactored checkout. Both runs produced identical +bytes: + +| File | SHA-256 | +| --- | --- | +| `v1.lance` | `fa8b3d81b9d4fd4ade5a7c3d077ebf2155664e12b9335e26fac1c0d0774e916c` | +| `v2_0.lance` | `073c8c24eb4433b83d0dda95bf7a731a9f5d8f32d78440f2f391474e99b9c49a` | +| `v2_1.lance` | `3af97ba176b72c7e00a248b4a270a53402a72e594631950f76eb3daab45c50ce` | +| `v2_2.lance` | `8298cd9301e657417b0725461345c27cf46515529d2a8b35824be139e3466a14` | + +The compatibility tests require each refactored stable writer to reproduce its +fixture byte-for-byte and each refactored reader to open and read the baseline +file. V2.3 is unstable, so it has deterministic current-revision tests instead +of a checked-in compatibility fixture. + +Regenerate these fixtures only from the baseline writer APIs. Files generated +with the implementation under test are not independent compatibility evidence. diff --git a/rust/lance-file/test_data/exact_versions/v1.lance b/rust/lance-file/test_data/exact_versions/v1.lance new file mode 100644 index 00000000000..267835ffb2f Binary files /dev/null and b/rust/lance-file/test_data/exact_versions/v1.lance differ diff --git a/rust/lance-file/test_data/exact_versions/v2_0.lance b/rust/lance-file/test_data/exact_versions/v2_0.lance new file mode 100644 index 00000000000..5c713fc36b3 --- /dev/null +++ b/rust/lance-file/test_data/exact_versions/v2_0.lance @@ -0,0 +1,10040 @@ +blob-0000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +  !"#$%&'()*+,-./0123456789:;<HHHHHHHHHHHH=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyHHHHHHHHHHHHz{|}~HHHHHHHHHHHHHHHHHHHHHHHH  +    !"#$%&'()*+,-./0HHHHHHHHHHHH123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmHHHHHHHHHHHHnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH  +    !"#$HHHHHHHHHHHH%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`aHHHHHHHHHHHHbcdefghijklmnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH  +   HHHHHHHHHHHH !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUHHHHHHHHHHHHVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH @`value-0001-deterministic-fixturevalue-0002-deterministic-fixturevalue-0003-deterministic-fixturevalue-0004-deterministic-fixturevalue-0005-deterministic-fixturevalue-0006-deterministic-fixture @`value-0008-deterministic-fixturevalue-0009-deterministic-fixturevalue-0010-deterministic-fixturevalue-0011-deterministic-fixturevalue-0012-deterministic-fixturevalue-0013-deterministic-fixturevalue-0015-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0016-deterministic-fixturevalue-0017-deterministic-fixturevalue-0018-deterministic-fixturevalue-0019-deterministic-fixturevalue-0020-deterministic-fixturevalue-0022-deterministic-fixturevalue-0023-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0024-deterministic-fixturevalue-0025-deterministic-fixturevalue-0026-deterministic-fixturevalue-0027-deterministic-fixturevalue-0029-deterministic-fixturevalue-0030-deterministic-fixturevalue-0031-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0032-deterministic-fixturevalue-0033-deterministic-fixturevalue-0034-deterministic-fixturevalue-0036-deterministic-fixturevalue-0037-deterministic-fixturevalue-0038-deterministic-fixturevalue-0039-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0040-deterministic-fixturevalue-0041-deterministic-fixturevalue-0043-deterministic-fixturevalue-0044-deterministic-fixturevalue-0045-deterministic-fixturevalue-0046-deterministic-fixturevalue-0047-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0048-deterministic-fixturevalue-0050-deterministic-fixturevalue-0051-deterministic-fixturevalue-0052-deterministic-fixturevalue-0053-deterministic-fixturevalue-0054-deterministic-fixturevalue-0055-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0057-deterministic-fixturevalue-0058-deterministic-fixturevalue-0059-deterministic-fixturevalue-0060-deterministic-fixturevalue-0061-deterministic-fixturevalue-0062-deterministic-fixture @`value-0064-deterministic-fixturevalue-0065-deterministic-fixturevalue-0066-deterministic-fixturevalue-0067-deterministic-fixturevalue-0068-deterministic-fixturevalue-0069-deterministic-fixturevalue-0071-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0072-deterministic-fixturevalue-0073-deterministic-fixturevalue-0074-deterministic-fixturevalue-0075-deterministic-fixturevalue-0076-deterministic-fixturevalue-0078-deterministic-fixturevalue-0079-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0080-deterministic-fixturevalue-0081-deterministic-fixturevalue-0082-deterministic-fixturevalue-0083-deterministic-fixturevalue-0085-deterministic-fixturevalue-0086-deterministic-fixturevalue-0087-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0088-deterministic-fixturevalue-0089-deterministic-fixturevalue-0090-deterministic-fixturevalue-0092-deterministic-fixturevalue-0093-deterministic-fixturevalue-0094-deterministic-fixturevalue-0095-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0096-deterministic-fixturevalue-0097-deterministic-fixturevalue-0099-deterministic-fixturevalue-0100-deterministic-fixturevalue-0101-deterministic-fixturevalue-0102-deterministic-fixturevalue-0103-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0104-deterministic-fixturevalue-0106-deterministic-fixturevalue-0107-deterministic-fixturevalue-0108-deterministic-fixturevalue-0109-deterministic-fixturevalue-0110-deterministic-fixturevalue-0111-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0113-deterministic-fixturevalue-0114-deterministic-fixturevalue-0115-deterministic-fixturevalue-0116-deterministic-fixturevalue-0117-deterministic-fixturevalue-0118-deterministic-fixture @`value-0120-deterministic-fixturevalue-0121-deterministic-fixturevalue-0122-deterministic-fixturevalue-0123-deterministic-fixturevalue-0124-deterministic-fixturevalue-0125-deterministic-fixturevalue-0127-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0128-deterministic-fixturevalue-0129-deterministic-fixturevalue-0130-deterministic-fixturevalue-0131-deterministic-fixturevalue-0132-deterministic-fixturevalue-0134-deterministic-fixturevalue-0135-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0136-deterministic-fixturevalue-0137-deterministic-fixturevalue-0138-deterministic-fixturevalue-0139-deterministic-fixturevalue-0141-deterministic-fixturevalue-0142-deterministic-fixturevalue-0143-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0144-deterministic-fixturevalue-0145-deterministic-fixturevalue-0146-deterministic-fixturevalue-0148-deterministic-fixturevalue-0149-deterministic-fixturevalue-0150-deterministic-fixturevalue-0151-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0152-deterministic-fixturevalue-0153-deterministic-fixturevalue-0155-deterministic-fixturevalue-0156-deterministic-fixturevalue-0157-deterministic-fixturevalue-0158-deterministic-fixturevalue-0159-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0160-deterministic-fixturevalue-0162-deterministic-fixturevalue-0163-deterministic-fixturevalue-0164-deterministic-fixturevalue-0165-deterministic-fixturevalue-0166-deterministic-fixturevalue-0167-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0169-deterministic-fixturevalue-0170-deterministic-fixturevalue-0171-deterministic-fixturevalue-0172-deterministic-fixturevalue-0173-deterministic-fixturevalue-0174-deterministic-fixture @`value-0176-deterministic-fixturevalue-0177-deterministic-fixturevalue-0178-deterministic-fixturevalue-0179-deterministic-fixturevalue-0180-deterministic-fixturevalue-0181-deterministic-fixturevalue-0183-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0184-deterministic-fixturevalue-0185-deterministic-fixturevalue-0186-deterministic-fixturevalue-0187-deterministic-fixturevalue-0188-deterministic-fixturevalue-0190-deterministic-fixturevalue-0191-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0192-deterministic-fixturevalue-0193-deterministic-fixturevalue-0194-deterministic-fixturevalue-0195-deterministic-fixturevalue-0197-deterministic-fixturevalue-0198-deterministic-fixturevalue-0199-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0200-deterministic-fixturevalue-0201-deterministic-fixturevalue-0202-deterministic-fixturevalue-0204-deterministic-fixturevalue-0205-deterministic-fixturevalue-0206-deterministic-fixturevalue-0207-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0208-deterministic-fixturevalue-0209-deterministic-fixturevalue-0211-deterministic-fixturevalue-0212-deterministic-fixturevalue-0213-deterministic-fixturevalue-0214-deterministic-fixturevalue-0215-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0216-deterministic-fixturevalue-0218-deterministic-fixturevalue-0219-deterministic-fixturevalue-0220-deterministic-fixturevalue-0221-deterministic-fixturevalue-0222-deterministic-fixturevalue-0223-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0225-deterministic-fixturevalue-0226-deterministic-fixturevalue-0227-deterministic-fixturevalue-0228-deterministic-fixturevalue-0229-deterministic-fixturevalue-0230-deterministic-fixture @`value-0232-deterministic-fixturevalue-0233-deterministic-fixturevalue-0234-deterministic-fixturevalue-0235-deterministic-fixturevalue-0236-deterministic-fixturevalue-0237-deterministic-fixturevalue-0239-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0240-deterministic-fixturevalue-0241-deterministic-fixturevalue-0242-deterministic-fixturevalue-0243-deterministic-fixturevalue-0244-deterministic-fixturevalue-0246-deterministic-fixturevalue-0247-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0248-deterministic-fixturevalue-0249-deterministic-fixturevalue-0250-deterministic-fixturevalue-0251-deterministic-fixturevalue-0253-deterministic-fixturevalue-0254-deterministic-fixturevalue-0255-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0256-deterministic-fixturevalue-0257-deterministic-fixturevalue-0258-deterministic-fixturevalue-0260-deterministic-fixturevalue-0261-deterministic-fixturevalue-0262-deterministic-fixturevalue-0263-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0264-deterministic-fixturevalue-0265-deterministic-fixturevalue-0267-deterministic-fixturevalue-0268-deterministic-fixturevalue-0269-deterministic-fixturevalue-0270-deterministic-fixturevalue-0271-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0272-deterministic-fixturevalue-0274-deterministic-fixturevalue-0275-deterministic-fixturevalue-0276-deterministic-fixturevalue-0277-deterministic-fixturevalue-0278-deterministic-fixturevalue-0279-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0281-deterministic-fixturevalue-0282-deterministic-fixturevalue-0283-deterministic-fixturevalue-0284-deterministic-fixturevalue-0285-deterministic-fixturevalue-0286-deterministic-fixture @`value-0288-deterministic-fixturevalue-0289-deterministic-fixturevalue-0290-deterministic-fixturevalue-0291-deterministic-fixturevalue-0292-deterministic-fixturevalue-0293-deterministic-fixturevalue-0295-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0296-deterministic-fixturevalue-0297-deterministic-fixturevalue-0298-deterministic-fixturevalue-0299-deterministic-fixturevalue-0300-deterministic-fixturevalue-0302-deterministic-fixturevalue-0303-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0304-deterministic-fixturevalue-0305-deterministic-fixturevalue-0306-deterministic-fixturevalue-0307-deterministic-fixturevalue-0309-deterministic-fixturevalue-0310-deterministic-fixturevalue-0311-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0312-deterministic-fixturevalue-0313-deterministic-fixturevalue-0314-deterministic-fixturevalue-0316-deterministic-fixturevalue-0317-deterministic-fixturevalue-0318-deterministic-fixturevalue-0319-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0320-deterministic-fixturevalue-0321-deterministic-fixturevalue-0323-deterministic-fixturevalue-0324-deterministic-fixturevalue-0325-deterministic-fixturevalue-0326-deterministic-fixturevalue-0327-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0328-deterministic-fixturevalue-0330-deterministic-fixturevalue-0331-deterministic-fixturevalue-0332-deterministic-fixturevalue-0333-deterministic-fixturevalue-0334-deterministic-fixturevalue-0335-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0337-deterministic-fixturevalue-0338-deterministic-fixturevalue-0339-deterministic-fixturevalue-0340-deterministic-fixturevalue-0341-deterministic-fixturevalue-0342-deterministic-fixture @`value-0344-deterministic-fixturevalue-0345-deterministic-fixturevalue-0346-deterministic-fixturevalue-0347-deterministic-fixturevalue-0348-deterministic-fixturevalue-0349-deterministic-fixturevalue-0351-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0352-deterministic-fixturevalue-0353-deterministic-fixturevalue-0354-deterministic-fixturevalue-0355-deterministic-fixturevalue-0356-deterministic-fixturevalue-0358-deterministic-fixturevalue-0359-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0360-deterministic-fixturevalue-0361-deterministic-fixturevalue-0362-deterministic-fixturevalue-0363-deterministic-fixturevalue-0365-deterministic-fixturevalue-0366-deterministic-fixturevalue-0367-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0368-deterministic-fixturevalue-0369-deterministic-fixturevalue-0370-deterministic-fixturevalue-0372-deterministic-fixturevalue-0373-deterministic-fixturevalue-0374-deterministic-fixturevalue-0375-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0376-deterministic-fixturevalue-0377-deterministic-fixturevalue-0379-deterministic-fixturevalue-0380-deterministic-fixturevalue-0381-deterministic-fixturevalue-0382-deterministic-fixturevalue-0383-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0384-deterministic-fixturevalue-0386-deterministic-fixturevalue-0387-deterministic-fixturevalue-0388-deterministic-fixturevalue-0389-deterministic-fixturevalue-0390-deterministic-fixturevalue-0391-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0393-deterministic-fixturevalue-0394-deterministic-fixturevalue-0395-deterministic-fixturevalue-0396-deterministic-fixturevalue-0397-deterministic-fixturevalue-0398-deterministic-fixture @`value-0400-deterministic-fixturevalue-0401-deterministic-fixturevalue-0402-deterministic-fixturevalue-0403-deterministic-fixturevalue-0404-deterministic-fixturevalue-0405-deterministic-fixturevalue-0407-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0408-deterministic-fixturevalue-0409-deterministic-fixturevalue-0410-deterministic-fixturevalue-0411-deterministic-fixturevalue-0412-deterministic-fixturevalue-0414-deterministic-fixturevalue-0415-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0416-deterministic-fixturevalue-0417-deterministic-fixturevalue-0418-deterministic-fixturevalue-0419-deterministic-fixturevalue-0421-deterministic-fixturevalue-0422-deterministic-fixturevalue-0423-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0424-deterministic-fixturevalue-0425-deterministic-fixturevalue-0426-deterministic-fixturevalue-0428-deterministic-fixturevalue-0429-deterministic-fixturevalue-0430-deterministic-fixturevalue-0431-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0432-deterministic-fixturevalue-0433-deterministic-fixturevalue-0435-deterministic-fixturevalue-0436-deterministic-fixturevalue-0437-deterministic-fixturevalue-0438-deterministic-fixturevalue-0439-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0440-deterministic-fixturevalue-0442-deterministic-fixturevalue-0443-deterministic-fixturevalue-0444-deterministic-fixturevalue-0445-deterministic-fixturevalue-0446-deterministic-fixturevalue-0447-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0449-deterministic-fixturevalue-0450-deterministic-fixturevalue-0451-deterministic-fixturevalue-0452-deterministic-fixturevalue-0453-deterministic-fixturevalue-0454-deterministic-fixture @`value-0456-deterministic-fixturevalue-0457-deterministic-fixturevalue-0458-deterministic-fixturevalue-0459-deterministic-fixturevalue-0460-deterministic-fixturevalue-0461-deterministic-fixturevalue-0463-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0464-deterministic-fixturevalue-0465-deterministic-fixturevalue-0466-deterministic-fixturevalue-0467-deterministic-fixturevalue-0468-deterministic-fixturevalue-0470-deterministic-fixturevalue-0471-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0472-deterministic-fixturevalue-0473-deterministic-fixturevalue-0474-deterministic-fixturevalue-0475-deterministic-fixturevalue-0477-deterministic-fixturevalue-0478-deterministic-fixturevalue-0479-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0480-deterministic-fixturevalue-0481-deterministic-fixturevalue-0482-deterministic-fixturevalue-0484-deterministic-fixturevalue-0485-deterministic-fixturevalue-0486-deterministic-fixturevalue-0487-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0488-deterministic-fixturevalue-0489-deterministic-fixturevalue-0491-deterministic-fixturevalue-0492-deterministic-fixturevalue-0493-deterministic-fixturevalue-0494-deterministic-fixturevalue-0495-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0496-deterministic-fixturevalue-0498-deterministic-fixturevalue-0499-deterministic-fixturevalue-0500-deterministic-fixturevalue-0501-deterministic-fixturevalue-0502-deterministic-fixturevalue-0503-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0505-deterministic-fixturevalue-0506-deterministic-fixturevalue-0507-deterministic-fixturevalue-0508-deterministic-fixturevalue-0509-deterministic-fixturevalue-0510-deterministic-fixture @`value-0512-deterministic-fixturevalue-0513-deterministic-fixturevalue-0514-deterministic-fixturevalue-0515-deterministic-fixturevalue-0516-deterministic-fixturevalue-0517-deterministic-fixturevalue-0519-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0520-deterministic-fixturevalue-0521-deterministic-fixturevalue-0522-deterministic-fixturevalue-0523-deterministic-fixturevalue-0524-deterministic-fixturevalue-0526-deterministic-fixturevalue-0527-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0528-deterministic-fixturevalue-0529-deterministic-fixturevalue-0530-deterministic-fixturevalue-0531-deterministic-fixturevalue-0533-deterministic-fixturevalue-0534-deterministic-fixturevalue-0535-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0536-deterministic-fixturevalue-0537-deterministic-fixturevalue-0538-deterministic-fixturevalue-0540-deterministic-fixturevalue-0541-deterministic-fixturevalue-0542-deterministic-fixturevalue-0543-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0544-deterministic-fixturevalue-0545-deterministic-fixturevalue-0547-deterministic-fixturevalue-0548-deterministic-fixturevalue-0549-deterministic-fixturevalue-0550-deterministic-fixturevalue-0551-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0552-deterministic-fixturevalue-0554-deterministic-fixturevalue-0555-deterministic-fixturevalue-0556-deterministic-fixturevalue-0557-deterministic-fixturevalue-0558-deterministic-fixturevalue-0559-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0561-deterministic-fixturevalue-0562-deterministic-fixturevalue-0563-deterministic-fixturevalue-0564-deterministic-fixturevalue-0565-deterministic-fixturevalue-0566-deterministic-fixture @`value-0568-deterministic-fixturevalue-0569-deterministic-fixturevalue-0570-deterministic-fixturevalue-0571-deterministic-fixturevalue-0572-deterministic-fixturevalue-0573-deterministic-fixturevalue-0575-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0576-deterministic-fixturevalue-0577-deterministic-fixturevalue-0578-deterministic-fixturevalue-0579-deterministic-fixturevalue-0580-deterministic-fixturevalue-0582-deterministic-fixturevalue-0583-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0584-deterministic-fixturevalue-0585-deterministic-fixturevalue-0586-deterministic-fixturevalue-0587-deterministic-fixturevalue-0589-deterministic-fixturevalue-0590-deterministic-fixturevalue-0591-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0592-deterministic-fixturevalue-0593-deterministic-fixturevalue-0594-deterministic-fixturevalue-0596-deterministic-fixturevalue-0597-deterministic-fixturevalue-0598-deterministic-fixturevalue-0599-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0600-deterministic-fixturevalue-0601-deterministic-fixturevalue-0603-deterministic-fixturevalue-0604-deterministic-fixturevalue-0605-deterministic-fixturevalue-0606-deterministic-fixturevalue-0607-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0608-deterministic-fixturevalue-0610-deterministic-fixturevalue-0611-deterministic-fixturevalue-0612-deterministic-fixturevalue-0613-deterministic-fixturevalue-0614-deterministic-fixturevalue-0615-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0617-deterministic-fixturevalue-0618-deterministic-fixturevalue-0619-deterministic-fixturevalue-0620-deterministic-fixturevalue-0621-deterministic-fixturevalue-0622-deterministic-fixture @`value-0624-deterministic-fixturevalue-0625-deterministic-fixturevalue-0626-deterministic-fixturevalue-0627-deterministic-fixturevalue-0628-deterministic-fixturevalue-0629-deterministic-fixturevalue-0631-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0632-deterministic-fixturevalue-0633-deterministic-fixturevalue-0634-deterministic-fixturevalue-0635-deterministic-fixturevalue-0636-deterministic-fixturevalue-0638-deterministic-fixturevalue-0639-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0640-deterministic-fixturevalue-0641-deterministic-fixturevalue-0642-deterministic-fixturevalue-0643-deterministic-fixturevalue-0645-deterministic-fixturevalue-0646-deterministic-fixturevalue-0647-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0648-deterministic-fixturevalue-0649-deterministic-fixturevalue-0650-deterministic-fixturevalue-0652-deterministic-fixturevalue-0653-deterministic-fixturevalue-0654-deterministic-fixturevalue-0655-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0656-deterministic-fixturevalue-0657-deterministic-fixturevalue-0659-deterministic-fixturevalue-0660-deterministic-fixturevalue-0661-deterministic-fixturevalue-0662-deterministic-fixturevalue-0663-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0664-deterministic-fixturevalue-0666-deterministic-fixturevalue-0667-deterministic-fixturevalue-0668-deterministic-fixturevalue-0669-deterministic-fixturevalue-0670-deterministic-fixturevalue-0671-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0673-deterministic-fixturevalue-0674-deterministic-fixturevalue-0675-deterministic-fixturevalue-0676-deterministic-fixturevalue-0677-deterministic-fixturevalue-0678-deterministic-fixture @`value-0680-deterministic-fixturevalue-0681-deterministic-fixturevalue-0682-deterministic-fixturevalue-0683-deterministic-fixturevalue-0684-deterministic-fixturevalue-0685-deterministic-fixturevalue-0687-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0688-deterministic-fixturevalue-0689-deterministic-fixturevalue-0690-deterministic-fixturevalue-0691-deterministic-fixturevalue-0692-deterministic-fixturevalue-0694-deterministic-fixturevalue-0695-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0696-deterministic-fixturevalue-0697-deterministic-fixturevalue-0698-deterministic-fixturevalue-0699-deterministic-fixturevalue-0701-deterministic-fixturevalue-0702-deterministic-fixturevalue-0703-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0704-deterministic-fixturevalue-0705-deterministic-fixturevalue-0706-deterministic-fixturevalue-0708-deterministic-fixturevalue-0709-deterministic-fixturevalue-0710-deterministic-fixturevalue-0711-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0712-deterministic-fixturevalue-0713-deterministic-fixturevalue-0715-deterministic-fixturevalue-0716-deterministic-fixturevalue-0717-deterministic-fixturevalue-0718-deterministic-fixturevalue-0719-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0720-deterministic-fixturevalue-0722-deterministic-fixturevalue-0723-deterministic-fixturevalue-0724-deterministic-fixturevalue-0725-deterministic-fixturevalue-0726-deterministic-fixturevalue-0727-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0729-deterministic-fixturevalue-0730-deterministic-fixturevalue-0731-deterministic-fixturevalue-0732-deterministic-fixturevalue-0733-deterministic-fixturevalue-0734-deterministic-fixture @`value-0736-deterministic-fixturevalue-0737-deterministic-fixturevalue-0738-deterministic-fixturevalue-0739-deterministic-fixturevalue-0740-deterministic-fixturevalue-0741-deterministic-fixturevalue-0743-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0744-deterministic-fixturevalue-0745-deterministic-fixturevalue-0746-deterministic-fixturevalue-0747-deterministic-fixturevalue-0748-deterministic-fixturevalue-0750-deterministic-fixturevalue-0751-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0752-deterministic-fixturevalue-0753-deterministic-fixturevalue-0754-deterministic-fixturevalue-0755-deterministic-fixturevalue-0757-deterministic-fixturevalue-0758-deterministic-fixturevalue-0759-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0760-deterministic-fixturevalue-0761-deterministic-fixturevalue-0762-deterministic-fixturevalue-0764-deterministic-fixturevalue-0765-deterministic-fixturevalue-0766-deterministic-fixturevalue-0767-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0768-deterministic-fixturevalue-0769-deterministic-fixturevalue-0771-deterministic-fixturevalue-0772-deterministic-fixturevalue-0773-deterministic-fixturevalue-0774-deterministic-fixturevalue-0775-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0776-deterministic-fixturevalue-0778-deterministic-fixturevalue-0779-deterministic-fixturevalue-0780-deterministic-fixturevalue-0781-deterministic-fixturevalue-0782-deterministic-fixturevalue-0783-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0785-deterministic-fixturevalue-0786-deterministic-fixturevalue-0787-deterministic-fixturevalue-0788-deterministic-fixturevalue-0789-deterministic-fixturevalue-0790-deterministic-fixture @`value-0792-deterministic-fixturevalue-0793-deterministic-fixturevalue-0794-deterministic-fixturevalue-0795-deterministic-fixturevalue-0796-deterministic-fixturevalue-0797-deterministic-fixturevalue-0799-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0800-deterministic-fixturevalue-0801-deterministic-fixturevalue-0802-deterministic-fixturevalue-0803-deterministic-fixturevalue-0804-deterministic-fixturevalue-0806-deterministic-fixturevalue-0807-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0808-deterministic-fixturevalue-0809-deterministic-fixturevalue-0810-deterministic-fixturevalue-0811-deterministic-fixturevalue-0813-deterministic-fixturevalue-0814-deterministic-fixturevalue-0815-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0816-deterministic-fixturevalue-0817-deterministic-fixturevalue-0818-deterministic-fixturevalue-0820-deterministic-fixturevalue-0821-deterministic-fixturevalue-0822-deterministic-fixturevalue-0823-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0824-deterministic-fixturevalue-0825-deterministic-fixturevalue-0827-deterministic-fixturevalue-0828-deterministic-fixturevalue-0829-deterministic-fixturevalue-0830-deterministic-fixturevalue-0831-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0832-deterministic-fixturevalue-0834-deterministic-fixturevalue-0835-deterministic-fixturevalue-0836-deterministic-fixturevalue-0837-deterministic-fixturevalue-0838-deterministic-fixturevalue-0839-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0841-deterministic-fixturevalue-0842-deterministic-fixturevalue-0843-deterministic-fixturevalue-0844-deterministic-fixturevalue-0845-deterministic-fixturevalue-0846-deterministic-fixture @`value-0848-deterministic-fixturevalue-0849-deterministic-fixturevalue-0850-deterministic-fixturevalue-0851-deterministic-fixturevalue-0852-deterministic-fixturevalue-0853-deterministic-fixturevalue-0855-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0856-deterministic-fixturevalue-0857-deterministic-fixturevalue-0858-deterministic-fixturevalue-0859-deterministic-fixturevalue-0860-deterministic-fixturevalue-0862-deterministic-fixturevalue-0863-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0864-deterministic-fixturevalue-0865-deterministic-fixturevalue-0866-deterministic-fixturevalue-0867-deterministic-fixturevalue-0869-deterministic-fixturevalue-0870-deterministic-fixturevalue-0871-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0872-deterministic-fixturevalue-0873-deterministic-fixturevalue-0874-deterministic-fixturevalue-0876-deterministic-fixturevalue-0877-deterministic-fixturevalue-0878-deterministic-fixturevalue-0879-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0880-deterministic-fixturevalue-0881-deterministic-fixturevalue-0883-deterministic-fixturevalue-0884-deterministic-fixturevalue-0885-deterministic-fixturevalue-0886-deterministic-fixturevalue-0887-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0888-deterministic-fixturevalue-0890-deterministic-fixturevalue-0891-deterministic-fixturevalue-0892-deterministic-fixturevalue-0893-deterministic-fixturevalue-0894-deterministic-fixturevalue-0895-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0897-deterministic-fixturevalue-0898-deterministic-fixturevalue-0899-deterministic-fixturevalue-0900-deterministic-fixturevalue-0901-deterministic-fixturevalue-0902-deterministic-fixture @`value-0904-deterministic-fixturevalue-0905-deterministic-fixturevalue-0906-deterministic-fixturevalue-0907-deterministic-fixturevalue-0908-deterministic-fixturevalue-0909-deterministic-fixturevalue-0911-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0912-deterministic-fixturevalue-0913-deterministic-fixturevalue-0914-deterministic-fixturevalue-0915-deterministic-fixturevalue-0916-deterministic-fixturevalue-0918-deterministic-fixturevalue-0919-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0920-deterministic-fixturevalue-0921-deterministic-fixturevalue-0922-deterministic-fixturevalue-0923-deterministic-fixturevalue-0925-deterministic-fixturevalue-0926-deterministic-fixturevalue-0927-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0928-deterministic-fixturevalue-0929-deterministic-fixturevalue-0930-deterministic-fixturevalue-0932-deterministic-fixturevalue-0933-deterministic-fixturevalue-0934-deterministic-fixturevalue-0935-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0936-deterministic-fixturevalue-0937-deterministic-fixturevalue-0939-deterministic-fixturevalue-0940-deterministic-fixturevalue-0941-deterministic-fixturevalue-0942-deterministic-fixturevalue-0943-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0944-deterministic-fixturevalue-0946-deterministic-fixturevalue-0947-deterministic-fixturevalue-0948-deterministic-fixturevalue-0949-deterministic-fixturevalue-0950-deterministic-fixturevalue-0951-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0953-deterministic-fixturevalue-0954-deterministic-fixturevalue-0955-deterministic-fixturevalue-0956-deterministic-fixturevalue-0957-deterministic-fixturevalue-0958-deterministic-fixture @`value-0960-deterministic-fixturevalue-0961-deterministic-fixturevalue-0962-deterministic-fixturevalue-0963-deterministic-fixturevalue-0964-deterministic-fixturevalue-0965-deterministic-fixturevalue-0967-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-0968-deterministic-fixturevalue-0969-deterministic-fixturevalue-0970-deterministic-fixturevalue-0971-deterministic-fixturevalue-0972-deterministic-fixturevalue-0974-deterministic-fixturevalue-0975-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-0976-deterministic-fixturevalue-0977-deterministic-fixturevalue-0978-deterministic-fixturevalue-0979-deterministic-fixturevalue-0981-deterministic-fixturevalue-0982-deterministic-fixturevalue-0983-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-0984-deterministic-fixturevalue-0985-deterministic-fixturevalue-0986-deterministic-fixturevalue-0988-deterministic-fixturevalue-0989-deterministic-fixturevalue-0990-deterministic-fixturevalue-0991-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-0992-deterministic-fixturevalue-0993-deterministic-fixturevalue-0995-deterministic-fixturevalue-0996-deterministic-fixturevalue-0997-deterministic-fixturevalue-0998-deterministic-fixturevalue-0999-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1000-deterministic-fixturevalue-1002-deterministic-fixturevalue-1003-deterministic-fixturevalue-1004-deterministic-fixturevalue-1005-deterministic-fixturevalue-1006-deterministic-fixturevalue-1007-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1009-deterministic-fixturevalue-1010-deterministic-fixturevalue-1011-deterministic-fixturevalue-1012-deterministic-fixturevalue-1013-deterministic-fixturevalue-1014-deterministic-fixture @`value-1016-deterministic-fixturevalue-1017-deterministic-fixturevalue-1018-deterministic-fixturevalue-1019-deterministic-fixturevalue-1020-deterministic-fixturevalue-1021-deterministic-fixturevalue-1023-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +  !$'*-0369<# ?BEHKNQTWZA ]`cfilorux_ {~}    #&), /258;>ADGJ1 MPSVY\_behO knqtwz}m      +  "%(+.147:! =@CFILORUX? [^adgjmpsv] y|{       !$'*-0369<?BEH/KNQTWZ]`cfMilorux{~k  #&),/258;>ADGJMPSV=Y\_behknqt[wz}y + "%(+.147:=@CF-ILORUX[^adKgjmpsvy|i  !$'*-0369<?BEHKNQT;WZ]`cfilorYux{~w  #& ),/258;>AD+GJMPSVY\_bIehknqtwz}g + "%(+.147:=@CFILOR9UX[^adgjmpWsvy|u          ! $ ' * - 0 3 6 9 < ? B )E H K N Q T W Z ] ` Gc f i l o r u x { ~ e      + + + + + + + + + + +# +& +) +, +/ +2 +5 +8 +; +> +A +D +G +J +M +P +7S +V +Y +\ +_ +b +e +h +k +n +Uq +t +w +z +} + + + + + +s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH     + $ '*-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH 0"3$6&9<*?.E0HK4N6Q8T:WZ>HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH] @`"Df#i$Hl%Jo&Lr'Nu(x)R{*T~+V-.\/^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH0`1b23f4h5j6l8p9r:t;v<=z>|?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH~@ACDEFGHIJKLNOHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHPQRSTUVWY Z[\]^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH_` a#b&d,e/f2g5h8i;j>kAlDmGoHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHMpPqSrVsYt\u_vbwexhzn{q|t}w~HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHz} + HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH $&(*.028:HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH<>BDFHLNPRVXZHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\ + bdfjl"n%p(+t.x4z7HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH:~=@CFILOUX[^adHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgjmpvy|HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH   +   + ! HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$ '*- 0"3$6(<*?,B.EH2K4N6QHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH8TW>] @`!Bc"f#Fi$Hl%Jo&Lr'u(Px*T~+V,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH-Z.\/^0`12d3f5j67n8p9r:t;HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH<x=z>|@ABCDEFGHIKHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHLMNOPQRSTVWXY Z[HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\]^_a#b&c)d,e/f2g5h8i;jHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH>lDmGnJoMpPqSrVsYt\u_wexhykznHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH{q|t}w~z} +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"$&(,02HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH68:<@BFJLNPHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHTVX +^ `bdhjl"n%r+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHt.v1x47|:~=@CFLORUXHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH[^adgmpsvy|HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH   HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  + ! $ '*-"36&9(<*?,BEHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH0H2K4NT:W<Z>] @`!c"Df#Fi$Hl%Jo'Nu(PHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHx)R{*T~+,X-Z.\/^02d3f4h56l7nHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH8p9r:;v=z>|?@ABCDEFHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIJKLMNOPQSTUVWHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHXY Z[\^_` a#b&c)d,e/f2HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHg5i;j>kAlDmGnJoMpPqSrVt\u_vbwHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHexhykzn{q|t}w} HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH "$&*,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.0468:@BDHJHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHLNRVX\ +^ `bfhlHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"%p(r+t.v14z7|:~=CFILOHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHRUX[^dgjmpsvy|HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +      + ! $ * -  0 3 $6 &9 (HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH< *? B .E 2K 4N Q 8T :W <Z >] ` !Bc "Df $Hl HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH%o &Lr 'Nu (Px )R{ *~ +V ,X -Z / 0` 1b 2d 3f 4HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH 5j 6l 7n 8p :t ;v <x =z > ?~ @ A B CHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH E F G H I J K L M N P Q R S HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHT U V +W +X +Y +[ +\ +] +^ +_ +` +a# +b& +cHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH) +d, +f2 +g5 +h8 +i; +j> +kA +lD +mG +nJ +oM +qS +rV +sHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHY +t\ +u_ +vb +we +xh +yk +zn +|t +}w +~z +} + + + +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + + + + + + + + + + + + +  +$ +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +( +* +, +. + +2 +4 +6 + +< +> +@ +B +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +F +H +J +L +P +R +T +V  Z \ + ^ `  HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHf h j " n% p( r+ t. 1 x4 |: ~= @ C HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHF I L O R U [ ^ a d g j m p HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHs v |             HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH              HHHHHHHHHHHHHHHHHHHH?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH             HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@blob-1024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  +    !"#$%&'()*+,-./0123456789:;<HHHHHHHHHHHH=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyHHHHHHHHHHHHz{|}~HHHHHHHHHHHHHHHHHHHHHHHH  +    !"#$%&'()*+,-./0HHHHHHHHHHHH123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmHHHHHHHHHHHHnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH  +    !"#$HHHHHHHHHHHH%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`aHHHHHHHHHHHHbcdefghijklmnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH  +   HHHHHHHHHHHH !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUHHHHHHHHHHHHVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH @`value-1024-deterministic-fixturevalue-1025-deterministic-fixturevalue-1026-deterministic-fixturevalue-1027-deterministic-fixturevalue-1028-deterministic-fixturevalue-1030-deterministic-fixturevalue-1031-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1032-deterministic-fixturevalue-1033-deterministic-fixturevalue-1034-deterministic-fixturevalue-1035-deterministic-fixturevalue-1037-deterministic-fixturevalue-1038-deterministic-fixturevalue-1039-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1040-deterministic-fixturevalue-1041-deterministic-fixturevalue-1042-deterministic-fixturevalue-1044-deterministic-fixturevalue-1045-deterministic-fixturevalue-1046-deterministic-fixturevalue-1047-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1048-deterministic-fixturevalue-1049-deterministic-fixturevalue-1051-deterministic-fixturevalue-1052-deterministic-fixturevalue-1053-deterministic-fixturevalue-1054-deterministic-fixturevalue-1055-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1056-deterministic-fixturevalue-1058-deterministic-fixturevalue-1059-deterministic-fixturevalue-1060-deterministic-fixturevalue-1061-deterministic-fixturevalue-1062-deterministic-fixturevalue-1063-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1065-deterministic-fixturevalue-1066-deterministic-fixturevalue-1067-deterministic-fixturevalue-1068-deterministic-fixturevalue-1069-deterministic-fixturevalue-1070-deterministic-fixture @`value-1072-deterministic-fixturevalue-1073-deterministic-fixturevalue-1074-deterministic-fixturevalue-1075-deterministic-fixturevalue-1076-deterministic-fixturevalue-1077-deterministic-fixturevalue-1079-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1080-deterministic-fixturevalue-1081-deterministic-fixturevalue-1082-deterministic-fixturevalue-1083-deterministic-fixturevalue-1084-deterministic-fixturevalue-1086-deterministic-fixturevalue-1087-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1088-deterministic-fixturevalue-1089-deterministic-fixturevalue-1090-deterministic-fixturevalue-1091-deterministic-fixturevalue-1093-deterministic-fixturevalue-1094-deterministic-fixturevalue-1095-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1096-deterministic-fixturevalue-1097-deterministic-fixturevalue-1098-deterministic-fixturevalue-1100-deterministic-fixturevalue-1101-deterministic-fixturevalue-1102-deterministic-fixturevalue-1103-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1104-deterministic-fixturevalue-1105-deterministic-fixturevalue-1107-deterministic-fixturevalue-1108-deterministic-fixturevalue-1109-deterministic-fixturevalue-1110-deterministic-fixturevalue-1111-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1112-deterministic-fixturevalue-1114-deterministic-fixturevalue-1115-deterministic-fixturevalue-1116-deterministic-fixturevalue-1117-deterministic-fixturevalue-1118-deterministic-fixturevalue-1119-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1121-deterministic-fixturevalue-1122-deterministic-fixturevalue-1123-deterministic-fixturevalue-1124-deterministic-fixturevalue-1125-deterministic-fixturevalue-1126-deterministic-fixture @`value-1128-deterministic-fixturevalue-1129-deterministic-fixturevalue-1130-deterministic-fixturevalue-1131-deterministic-fixturevalue-1132-deterministic-fixturevalue-1133-deterministic-fixturevalue-1135-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1136-deterministic-fixturevalue-1137-deterministic-fixturevalue-1138-deterministic-fixturevalue-1139-deterministic-fixturevalue-1140-deterministic-fixturevalue-1142-deterministic-fixturevalue-1143-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1144-deterministic-fixturevalue-1145-deterministic-fixturevalue-1146-deterministic-fixturevalue-1147-deterministic-fixturevalue-1149-deterministic-fixturevalue-1150-deterministic-fixturevalue-1151-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1152-deterministic-fixturevalue-1153-deterministic-fixturevalue-1154-deterministic-fixturevalue-1156-deterministic-fixturevalue-1157-deterministic-fixturevalue-1158-deterministic-fixturevalue-1159-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1160-deterministic-fixturevalue-1161-deterministic-fixturevalue-1163-deterministic-fixturevalue-1164-deterministic-fixturevalue-1165-deterministic-fixturevalue-1166-deterministic-fixturevalue-1167-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1168-deterministic-fixturevalue-1170-deterministic-fixturevalue-1171-deterministic-fixturevalue-1172-deterministic-fixturevalue-1173-deterministic-fixturevalue-1174-deterministic-fixturevalue-1175-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1177-deterministic-fixturevalue-1178-deterministic-fixturevalue-1179-deterministic-fixturevalue-1180-deterministic-fixturevalue-1181-deterministic-fixturevalue-1182-deterministic-fixture @`value-1184-deterministic-fixturevalue-1185-deterministic-fixturevalue-1186-deterministic-fixturevalue-1187-deterministic-fixturevalue-1188-deterministic-fixturevalue-1189-deterministic-fixturevalue-1191-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1192-deterministic-fixturevalue-1193-deterministic-fixturevalue-1194-deterministic-fixturevalue-1195-deterministic-fixturevalue-1196-deterministic-fixturevalue-1198-deterministic-fixturevalue-1199-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1200-deterministic-fixturevalue-1201-deterministic-fixturevalue-1202-deterministic-fixturevalue-1203-deterministic-fixturevalue-1205-deterministic-fixturevalue-1206-deterministic-fixturevalue-1207-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1208-deterministic-fixturevalue-1209-deterministic-fixturevalue-1210-deterministic-fixturevalue-1212-deterministic-fixturevalue-1213-deterministic-fixturevalue-1214-deterministic-fixturevalue-1215-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1216-deterministic-fixturevalue-1217-deterministic-fixturevalue-1219-deterministic-fixturevalue-1220-deterministic-fixturevalue-1221-deterministic-fixturevalue-1222-deterministic-fixturevalue-1223-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1224-deterministic-fixturevalue-1226-deterministic-fixturevalue-1227-deterministic-fixturevalue-1228-deterministic-fixturevalue-1229-deterministic-fixturevalue-1230-deterministic-fixturevalue-1231-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1233-deterministic-fixturevalue-1234-deterministic-fixturevalue-1235-deterministic-fixturevalue-1236-deterministic-fixturevalue-1237-deterministic-fixturevalue-1238-deterministic-fixture @`value-1240-deterministic-fixturevalue-1241-deterministic-fixturevalue-1242-deterministic-fixturevalue-1243-deterministic-fixturevalue-1244-deterministic-fixturevalue-1245-deterministic-fixturevalue-1247-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1248-deterministic-fixturevalue-1249-deterministic-fixturevalue-1250-deterministic-fixturevalue-1251-deterministic-fixturevalue-1252-deterministic-fixturevalue-1254-deterministic-fixturevalue-1255-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1256-deterministic-fixturevalue-1257-deterministic-fixturevalue-1258-deterministic-fixturevalue-1259-deterministic-fixturevalue-1261-deterministic-fixturevalue-1262-deterministic-fixturevalue-1263-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1264-deterministic-fixturevalue-1265-deterministic-fixturevalue-1266-deterministic-fixturevalue-1268-deterministic-fixturevalue-1269-deterministic-fixturevalue-1270-deterministic-fixturevalue-1271-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1272-deterministic-fixturevalue-1273-deterministic-fixturevalue-1275-deterministic-fixturevalue-1276-deterministic-fixturevalue-1277-deterministic-fixturevalue-1278-deterministic-fixturevalue-1279-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1280-deterministic-fixturevalue-1282-deterministic-fixturevalue-1283-deterministic-fixturevalue-1284-deterministic-fixturevalue-1285-deterministic-fixturevalue-1286-deterministic-fixturevalue-1287-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1289-deterministic-fixturevalue-1290-deterministic-fixturevalue-1291-deterministic-fixturevalue-1292-deterministic-fixturevalue-1293-deterministic-fixturevalue-1294-deterministic-fixture @`value-1296-deterministic-fixturevalue-1297-deterministic-fixturevalue-1298-deterministic-fixturevalue-1299-deterministic-fixturevalue-1300-deterministic-fixturevalue-1301-deterministic-fixturevalue-1303-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1304-deterministic-fixturevalue-1305-deterministic-fixturevalue-1306-deterministic-fixturevalue-1307-deterministic-fixturevalue-1308-deterministic-fixturevalue-1310-deterministic-fixturevalue-1311-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1312-deterministic-fixturevalue-1313-deterministic-fixturevalue-1314-deterministic-fixturevalue-1315-deterministic-fixturevalue-1317-deterministic-fixturevalue-1318-deterministic-fixturevalue-1319-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1320-deterministic-fixturevalue-1321-deterministic-fixturevalue-1322-deterministic-fixturevalue-1324-deterministic-fixturevalue-1325-deterministic-fixturevalue-1326-deterministic-fixturevalue-1327-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1328-deterministic-fixturevalue-1329-deterministic-fixturevalue-1331-deterministic-fixturevalue-1332-deterministic-fixturevalue-1333-deterministic-fixturevalue-1334-deterministic-fixturevalue-1335-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1336-deterministic-fixturevalue-1338-deterministic-fixturevalue-1339-deterministic-fixturevalue-1340-deterministic-fixturevalue-1341-deterministic-fixturevalue-1342-deterministic-fixturevalue-1343-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1345-deterministic-fixturevalue-1346-deterministic-fixturevalue-1347-deterministic-fixturevalue-1348-deterministic-fixturevalue-1349-deterministic-fixturevalue-1350-deterministic-fixture @`value-1352-deterministic-fixturevalue-1353-deterministic-fixturevalue-1354-deterministic-fixturevalue-1355-deterministic-fixturevalue-1356-deterministic-fixturevalue-1357-deterministic-fixturevalue-1359-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1360-deterministic-fixturevalue-1361-deterministic-fixturevalue-1362-deterministic-fixturevalue-1363-deterministic-fixturevalue-1364-deterministic-fixturevalue-1366-deterministic-fixturevalue-1367-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1368-deterministic-fixturevalue-1369-deterministic-fixturevalue-1370-deterministic-fixturevalue-1371-deterministic-fixturevalue-1373-deterministic-fixturevalue-1374-deterministic-fixturevalue-1375-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1376-deterministic-fixturevalue-1377-deterministic-fixturevalue-1378-deterministic-fixturevalue-1380-deterministic-fixturevalue-1381-deterministic-fixturevalue-1382-deterministic-fixturevalue-1383-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1384-deterministic-fixturevalue-1385-deterministic-fixturevalue-1387-deterministic-fixturevalue-1388-deterministic-fixturevalue-1389-deterministic-fixturevalue-1390-deterministic-fixturevalue-1391-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1392-deterministic-fixturevalue-1394-deterministic-fixturevalue-1395-deterministic-fixturevalue-1396-deterministic-fixturevalue-1397-deterministic-fixturevalue-1398-deterministic-fixturevalue-1399-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1401-deterministic-fixturevalue-1402-deterministic-fixturevalue-1403-deterministic-fixturevalue-1404-deterministic-fixturevalue-1405-deterministic-fixturevalue-1406-deterministic-fixture @`value-1408-deterministic-fixturevalue-1409-deterministic-fixturevalue-1410-deterministic-fixturevalue-1411-deterministic-fixturevalue-1412-deterministic-fixturevalue-1413-deterministic-fixturevalue-1415-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1416-deterministic-fixturevalue-1417-deterministic-fixturevalue-1418-deterministic-fixturevalue-1419-deterministic-fixturevalue-1420-deterministic-fixturevalue-1422-deterministic-fixturevalue-1423-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1424-deterministic-fixturevalue-1425-deterministic-fixturevalue-1426-deterministic-fixturevalue-1427-deterministic-fixturevalue-1429-deterministic-fixturevalue-1430-deterministic-fixturevalue-1431-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1432-deterministic-fixturevalue-1433-deterministic-fixturevalue-1434-deterministic-fixturevalue-1436-deterministic-fixturevalue-1437-deterministic-fixturevalue-1438-deterministic-fixturevalue-1439-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1440-deterministic-fixturevalue-1441-deterministic-fixturevalue-1443-deterministic-fixturevalue-1444-deterministic-fixturevalue-1445-deterministic-fixturevalue-1446-deterministic-fixturevalue-1447-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1448-deterministic-fixturevalue-1450-deterministic-fixturevalue-1451-deterministic-fixturevalue-1452-deterministic-fixturevalue-1453-deterministic-fixturevalue-1454-deterministic-fixturevalue-1455-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1457-deterministic-fixturevalue-1458-deterministic-fixturevalue-1459-deterministic-fixturevalue-1460-deterministic-fixturevalue-1461-deterministic-fixturevalue-1462-deterministic-fixture @`value-1464-deterministic-fixturevalue-1465-deterministic-fixturevalue-1466-deterministic-fixturevalue-1467-deterministic-fixturevalue-1468-deterministic-fixturevalue-1469-deterministic-fixturevalue-1471-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1472-deterministic-fixturevalue-1473-deterministic-fixturevalue-1474-deterministic-fixturevalue-1475-deterministic-fixturevalue-1476-deterministic-fixturevalue-1478-deterministic-fixturevalue-1479-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1480-deterministic-fixturevalue-1481-deterministic-fixturevalue-1482-deterministic-fixturevalue-1483-deterministic-fixturevalue-1485-deterministic-fixturevalue-1486-deterministic-fixturevalue-1487-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1488-deterministic-fixturevalue-1489-deterministic-fixturevalue-1490-deterministic-fixturevalue-1492-deterministic-fixturevalue-1493-deterministic-fixturevalue-1494-deterministic-fixturevalue-1495-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1496-deterministic-fixturevalue-1497-deterministic-fixturevalue-1499-deterministic-fixturevalue-1500-deterministic-fixturevalue-1501-deterministic-fixturevalue-1502-deterministic-fixturevalue-1503-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1504-deterministic-fixturevalue-1506-deterministic-fixturevalue-1507-deterministic-fixturevalue-1508-deterministic-fixturevalue-1509-deterministic-fixturevalue-1510-deterministic-fixturevalue-1511-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1513-deterministic-fixturevalue-1514-deterministic-fixturevalue-1515-deterministic-fixturevalue-1516-deterministic-fixturevalue-1517-deterministic-fixturevalue-1518-deterministic-fixture @`value-1520-deterministic-fixturevalue-1521-deterministic-fixturevalue-1522-deterministic-fixturevalue-1523-deterministic-fixturevalue-1524-deterministic-fixturevalue-1525-deterministic-fixturevalue-1527-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1528-deterministic-fixturevalue-1529-deterministic-fixturevalue-1530-deterministic-fixturevalue-1531-deterministic-fixturevalue-1532-deterministic-fixturevalue-1534-deterministic-fixturevalue-1535-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1536-deterministic-fixturevalue-1537-deterministic-fixturevalue-1538-deterministic-fixturevalue-1539-deterministic-fixturevalue-1541-deterministic-fixturevalue-1542-deterministic-fixturevalue-1543-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1544-deterministic-fixturevalue-1545-deterministic-fixturevalue-1546-deterministic-fixturevalue-1548-deterministic-fixturevalue-1549-deterministic-fixturevalue-1550-deterministic-fixturevalue-1551-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1552-deterministic-fixturevalue-1553-deterministic-fixturevalue-1555-deterministic-fixturevalue-1556-deterministic-fixturevalue-1557-deterministic-fixturevalue-1558-deterministic-fixturevalue-1559-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1560-deterministic-fixturevalue-1562-deterministic-fixturevalue-1563-deterministic-fixturevalue-1564-deterministic-fixturevalue-1565-deterministic-fixturevalue-1566-deterministic-fixturevalue-1567-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1569-deterministic-fixturevalue-1570-deterministic-fixturevalue-1571-deterministic-fixturevalue-1572-deterministic-fixturevalue-1573-deterministic-fixturevalue-1574-deterministic-fixture @`value-1576-deterministic-fixturevalue-1577-deterministic-fixturevalue-1578-deterministic-fixturevalue-1579-deterministic-fixturevalue-1580-deterministic-fixturevalue-1581-deterministic-fixturevalue-1583-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1584-deterministic-fixturevalue-1585-deterministic-fixturevalue-1586-deterministic-fixturevalue-1587-deterministic-fixturevalue-1588-deterministic-fixturevalue-1590-deterministic-fixturevalue-1591-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1592-deterministic-fixturevalue-1593-deterministic-fixturevalue-1594-deterministic-fixturevalue-1595-deterministic-fixturevalue-1597-deterministic-fixturevalue-1598-deterministic-fixturevalue-1599-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1600-deterministic-fixturevalue-1601-deterministic-fixturevalue-1602-deterministic-fixturevalue-1604-deterministic-fixturevalue-1605-deterministic-fixturevalue-1606-deterministic-fixturevalue-1607-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1608-deterministic-fixturevalue-1609-deterministic-fixturevalue-1611-deterministic-fixturevalue-1612-deterministic-fixturevalue-1613-deterministic-fixturevalue-1614-deterministic-fixturevalue-1615-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1616-deterministic-fixturevalue-1618-deterministic-fixturevalue-1619-deterministic-fixturevalue-1620-deterministic-fixturevalue-1621-deterministic-fixturevalue-1622-deterministic-fixturevalue-1623-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1625-deterministic-fixturevalue-1626-deterministic-fixturevalue-1627-deterministic-fixturevalue-1628-deterministic-fixturevalue-1629-deterministic-fixturevalue-1630-deterministic-fixture @`value-1632-deterministic-fixturevalue-1633-deterministic-fixturevalue-1634-deterministic-fixturevalue-1635-deterministic-fixturevalue-1636-deterministic-fixturevalue-1637-deterministic-fixturevalue-1639-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1640-deterministic-fixturevalue-1641-deterministic-fixturevalue-1642-deterministic-fixturevalue-1643-deterministic-fixturevalue-1644-deterministic-fixturevalue-1646-deterministic-fixturevalue-1647-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1648-deterministic-fixturevalue-1649-deterministic-fixturevalue-1650-deterministic-fixturevalue-1651-deterministic-fixturevalue-1653-deterministic-fixturevalue-1654-deterministic-fixturevalue-1655-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1656-deterministic-fixturevalue-1657-deterministic-fixturevalue-1658-deterministic-fixturevalue-1660-deterministic-fixturevalue-1661-deterministic-fixturevalue-1662-deterministic-fixturevalue-1663-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1664-deterministic-fixturevalue-1665-deterministic-fixturevalue-1667-deterministic-fixturevalue-1668-deterministic-fixturevalue-1669-deterministic-fixturevalue-1670-deterministic-fixturevalue-1671-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1672-deterministic-fixturevalue-1674-deterministic-fixturevalue-1675-deterministic-fixturevalue-1676-deterministic-fixturevalue-1677-deterministic-fixturevalue-1678-deterministic-fixturevalue-1679-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1681-deterministic-fixturevalue-1682-deterministic-fixturevalue-1683-deterministic-fixturevalue-1684-deterministic-fixturevalue-1685-deterministic-fixturevalue-1686-deterministic-fixture @`value-1688-deterministic-fixturevalue-1689-deterministic-fixturevalue-1690-deterministic-fixturevalue-1691-deterministic-fixturevalue-1692-deterministic-fixturevalue-1693-deterministic-fixturevalue-1695-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1696-deterministic-fixturevalue-1697-deterministic-fixturevalue-1698-deterministic-fixturevalue-1699-deterministic-fixturevalue-1700-deterministic-fixturevalue-1702-deterministic-fixturevalue-1703-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1704-deterministic-fixturevalue-1705-deterministic-fixturevalue-1706-deterministic-fixturevalue-1707-deterministic-fixturevalue-1709-deterministic-fixturevalue-1710-deterministic-fixturevalue-1711-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1712-deterministic-fixturevalue-1713-deterministic-fixturevalue-1714-deterministic-fixturevalue-1716-deterministic-fixturevalue-1717-deterministic-fixturevalue-1718-deterministic-fixturevalue-1719-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1720-deterministic-fixturevalue-1721-deterministic-fixturevalue-1723-deterministic-fixturevalue-1724-deterministic-fixturevalue-1725-deterministic-fixturevalue-1726-deterministic-fixturevalue-1727-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1728-deterministic-fixturevalue-1730-deterministic-fixturevalue-1731-deterministic-fixturevalue-1732-deterministic-fixturevalue-1733-deterministic-fixturevalue-1734-deterministic-fixturevalue-1735-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1737-deterministic-fixturevalue-1738-deterministic-fixturevalue-1739-deterministic-fixturevalue-1740-deterministic-fixturevalue-1741-deterministic-fixturevalue-1742-deterministic-fixture @`value-1744-deterministic-fixturevalue-1745-deterministic-fixturevalue-1746-deterministic-fixturevalue-1747-deterministic-fixturevalue-1748-deterministic-fixturevalue-1749-deterministic-fixturevalue-1751-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1752-deterministic-fixturevalue-1753-deterministic-fixturevalue-1754-deterministic-fixturevalue-1755-deterministic-fixturevalue-1756-deterministic-fixturevalue-1758-deterministic-fixturevalue-1759-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1760-deterministic-fixturevalue-1761-deterministic-fixturevalue-1762-deterministic-fixturevalue-1763-deterministic-fixturevalue-1765-deterministic-fixturevalue-1766-deterministic-fixturevalue-1767-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1768-deterministic-fixturevalue-1769-deterministic-fixturevalue-1770-deterministic-fixturevalue-1772-deterministic-fixturevalue-1773-deterministic-fixturevalue-1774-deterministic-fixturevalue-1775-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1776-deterministic-fixturevalue-1777-deterministic-fixturevalue-1779-deterministic-fixturevalue-1780-deterministic-fixturevalue-1781-deterministic-fixturevalue-1782-deterministic-fixturevalue-1783-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1784-deterministic-fixturevalue-1786-deterministic-fixturevalue-1787-deterministic-fixturevalue-1788-deterministic-fixturevalue-1789-deterministic-fixturevalue-1790-deterministic-fixturevalue-1791-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1793-deterministic-fixturevalue-1794-deterministic-fixturevalue-1795-deterministic-fixturevalue-1796-deterministic-fixturevalue-1797-deterministic-fixturevalue-1798-deterministic-fixture @`value-1800-deterministic-fixturevalue-1801-deterministic-fixturevalue-1802-deterministic-fixturevalue-1803-deterministic-fixturevalue-1804-deterministic-fixturevalue-1805-deterministic-fixturevalue-1807-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1808-deterministic-fixturevalue-1809-deterministic-fixturevalue-1810-deterministic-fixturevalue-1811-deterministic-fixturevalue-1812-deterministic-fixturevalue-1814-deterministic-fixturevalue-1815-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1816-deterministic-fixturevalue-1817-deterministic-fixturevalue-1818-deterministic-fixturevalue-1819-deterministic-fixturevalue-1821-deterministic-fixturevalue-1822-deterministic-fixturevalue-1823-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1824-deterministic-fixturevalue-1825-deterministic-fixturevalue-1826-deterministic-fixturevalue-1828-deterministic-fixturevalue-1829-deterministic-fixturevalue-1830-deterministic-fixturevalue-1831-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1832-deterministic-fixturevalue-1833-deterministic-fixturevalue-1835-deterministic-fixturevalue-1836-deterministic-fixturevalue-1837-deterministic-fixturevalue-1838-deterministic-fixturevalue-1839-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1840-deterministic-fixturevalue-1842-deterministic-fixturevalue-1843-deterministic-fixturevalue-1844-deterministic-fixturevalue-1845-deterministic-fixturevalue-1846-deterministic-fixturevalue-1847-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1849-deterministic-fixturevalue-1850-deterministic-fixturevalue-1851-deterministic-fixturevalue-1852-deterministic-fixturevalue-1853-deterministic-fixturevalue-1854-deterministic-fixture @`value-1856-deterministic-fixturevalue-1857-deterministic-fixturevalue-1858-deterministic-fixturevalue-1859-deterministic-fixturevalue-1860-deterministic-fixturevalue-1861-deterministic-fixturevalue-1863-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1864-deterministic-fixturevalue-1865-deterministic-fixturevalue-1866-deterministic-fixturevalue-1867-deterministic-fixturevalue-1868-deterministic-fixturevalue-1870-deterministic-fixturevalue-1871-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1872-deterministic-fixturevalue-1873-deterministic-fixturevalue-1874-deterministic-fixturevalue-1875-deterministic-fixturevalue-1877-deterministic-fixturevalue-1878-deterministic-fixturevalue-1879-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1880-deterministic-fixturevalue-1881-deterministic-fixturevalue-1882-deterministic-fixturevalue-1884-deterministic-fixturevalue-1885-deterministic-fixturevalue-1886-deterministic-fixturevalue-1887-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1888-deterministic-fixturevalue-1889-deterministic-fixturevalue-1891-deterministic-fixturevalue-1892-deterministic-fixturevalue-1893-deterministic-fixturevalue-1894-deterministic-fixturevalue-1895-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1896-deterministic-fixturevalue-1898-deterministic-fixturevalue-1899-deterministic-fixturevalue-1900-deterministic-fixturevalue-1901-deterministic-fixturevalue-1902-deterministic-fixturevalue-1903-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1905-deterministic-fixturevalue-1906-deterministic-fixturevalue-1907-deterministic-fixturevalue-1908-deterministic-fixturevalue-1909-deterministic-fixturevalue-1910-deterministic-fixture @`value-1912-deterministic-fixturevalue-1913-deterministic-fixturevalue-1914-deterministic-fixturevalue-1915-deterministic-fixturevalue-1916-deterministic-fixturevalue-1917-deterministic-fixturevalue-1919-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1920-deterministic-fixturevalue-1921-deterministic-fixturevalue-1922-deterministic-fixturevalue-1923-deterministic-fixturevalue-1924-deterministic-fixturevalue-1926-deterministic-fixturevalue-1927-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1928-deterministic-fixturevalue-1929-deterministic-fixturevalue-1930-deterministic-fixturevalue-1931-deterministic-fixturevalue-1933-deterministic-fixturevalue-1934-deterministic-fixturevalue-1935-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1936-deterministic-fixturevalue-1937-deterministic-fixturevalue-1938-deterministic-fixturevalue-1940-deterministic-fixturevalue-1941-deterministic-fixturevalue-1942-deterministic-fixturevalue-1943-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-1944-deterministic-fixturevalue-1945-deterministic-fixturevalue-1947-deterministic-fixturevalue-1948-deterministic-fixturevalue-1949-deterministic-fixturevalue-1950-deterministic-fixturevalue-1951-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1952-deterministic-fixturevalue-1954-deterministic-fixturevalue-1955-deterministic-fixturevalue-1956-deterministic-fixturevalue-1957-deterministic-fixturevalue-1958-deterministic-fixturevalue-1959-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1961-deterministic-fixturevalue-1962-deterministic-fixturevalue-1963-deterministic-fixturevalue-1964-deterministic-fixturevalue-1965-deterministic-fixturevalue-1966-deterministic-fixture @`value-1968-deterministic-fixturevalue-1969-deterministic-fixturevalue-1970-deterministic-fixturevalue-1971-deterministic-fixturevalue-1972-deterministic-fixturevalue-1973-deterministic-fixturevalue-1975-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-1976-deterministic-fixturevalue-1977-deterministic-fixturevalue-1978-deterministic-fixturevalue-1979-deterministic-fixturevalue-1980-deterministic-fixturevalue-1982-deterministic-fixturevalue-1983-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-1984-deterministic-fixturevalue-1985-deterministic-fixturevalue-1986-deterministic-fixturevalue-1987-deterministic-fixturevalue-1989-deterministic-fixturevalue-1990-deterministic-fixturevalue-1991-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-1992-deterministic-fixturevalue-1993-deterministic-fixturevalue-1994-deterministic-fixturevalue-1996-deterministic-fixturevalue-1997-deterministic-fixturevalue-1998-deterministic-fixturevalue-1999-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2000-deterministic-fixturevalue-2001-deterministic-fixturevalue-2003-deterministic-fixturevalue-2004-deterministic-fixturevalue-2005-deterministic-fixturevalue-2006-deterministic-fixturevalue-2007-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2008-deterministic-fixturevalue-2010-deterministic-fixturevalue-2011-deterministic-fixturevalue-2012-deterministic-fixturevalue-2013-deterministic-fixturevalue-2014-deterministic-fixturevalue-2015-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2017-deterministic-fixturevalue-2018-deterministic-fixturevalue-2019-deterministic-fixturevalue-2020-deterministic-fixturevalue-2021-deterministic-fixturevalue-2022-deterministic-fixture @`value-2024-deterministic-fixturevalue-2025-deterministic-fixturevalue-2026-deterministic-fixturevalue-2027-deterministic-fixturevalue-2028-deterministic-fixturevalue-2029-deterministic-fixturevalue-2031-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2032-deterministic-fixturevalue-2033-deterministic-fixturevalue-2034-deterministic-fixturevalue-2035-deterministic-fixturevalue-2036-deterministic-fixturevalue-2038-deterministic-fixturevalue-2039-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2040-deterministic-fixturevalue-2041-deterministic-fixturevalue-2042-deterministic-fixturevalue-2043-deterministic-fixturevalue-2045-deterministic-fixturevalue-2046-deterministic-fixturevalue-2047-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  !$'*-0369<& ?BEHKNQTWZD ]`cfiloruxb {~    #&), /258;>ADGJ4 MPSVY\_behR knqtwz}p      +  "%(+.147:$ =@CFILORUXB [^adgjmpsv` y|~       !$'*-0369<?BEH2KNQTWZ]`cfPilorux{~n  #&),/258";>ADGJMPSV@Y\_behknqt^wz}| + "%(+.147:=@CF0ILORUX[^adNgjmpsvy|l  !$'*-036 9<?BEHKNQT>WZ]`cfilor\ux{~z  #&),/258;>AD.GJMPSVY\_bLehknqtwz}j + "%(+.147:=@CFILOR<UX[^adgjmpZsvy|x          ! $ ' * - 0 3 6 9 < ? B ,E H K N Q T W Z ] ` Jc f i l o r u x { ~ h      + + + + + + + + + + +# +& +) +, +/ +2 +5 +8 +; +> +A +D +G +J +M +P +:S +V +Y +\ +_ +b +e +h +k +n +Xq +t +w +z +} + + + + + +v + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH      +     ! $ ' * HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH- 0 "3 $6 &9 (< ,B .E 0H 2K N 6Q 8T :W <HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHZ ] !Bc "Df #Fi $l %Jo &Lr 'Nu (Px ){ *T~ ,X -Z . HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH/^ 0` 1b 2d 3 4h 5j 7n 8 9r :t ;v <x = >HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH| ?~ @ B C D E F G H I J K M NHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH O P Q R S T U V X Y Z [ \ ] HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^ _ ` a# c) d, e/ f2 g5 h8 i; j> kA lD nHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJ oM pP qS rV sY t\ u_ vb we yk zn {q |t }HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHw ~z }      +        HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH   "  & ( * ,  0 4 6  HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH: < > @  D F J  N P R T X HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHZ \ +b d f h l "n %p (r +v 1x 4HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHz 7| := @ C F IL R U X[ ^ aHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH d gj m s vy |      HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH           HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH            HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH    + + +  +  + +  + +  +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!  +$  +' +*- +0" +3& +9<* +?, +B. +E0 +HK4 +NHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH6 +Q8 +TZ> +] @ +`!B +c"D +f#i$H +l%J +o&L +r'N +u)R +{*T +~+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHV +,X +-.\ +/^ +0` +1b +24h +5j +6l +78p +9r +:t +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH;v +<=z +?~ +@ +AB +C +D +E +FG +H +J +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHKL +M +N +O +PQ +R +S +UV +W +X +Y + ZHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH[ +\ +] +^ +` + a +#b +&c +)d,e +/f +2g +5h +8iHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH;k +Al +Dm +GnJo +Mp +Pq +Sr +VsYt +\v +bw +exhy +kHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHz +n{ +q| +t}w~ +z +}    +   HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH      $ & ( * . 0 2 HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH4 8 : < > D F H L N P HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHR V Z \ + ` b d f j l "p (HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH+t .v 1x 4z 7:~ = @ CI L O R UHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHX [ ^ a d j m p sv y |  HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH           HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH            HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH          +   HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH   +   !  $  '* 0" 3$ 69( <* ?, BHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH. EH2 K6 Q8 TW< Z> ] @ `!B c"f#F i$H l&L r'HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHu(P x)R {*T ~+V ,-Z .\ /^ 12d 3f 4h 5j 6HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH7n 8p 9r :t <x =z >| ?~ @A B C D EGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH H I JK L M N OP R S TU V HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHW X Y Z [ ] ^_ ` a #b &c)d ,e /HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHf 2h8i ;j >k Al DmGn Jo Mp Pq Ss Yt \u _vHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH bwex hy kz n{ q|t~ z }     + HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH       " $ ( HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH, . 0 2 6 8 : @ B D F HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJ L N P T V X Z  +^ ` b d j HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHl "n %(r +t .v 1x 47| : @ CF I LHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH O RU X [ ad g j m ps v yHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH |            HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH           HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH         HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  +   + ! '*- 0"36&HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH9(<*?,B0H2K4N6QT:W<Z>] @`!c#FiHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$Hl%Jo&r'Nu(Px)R{*T~+,X.\/^01b2d3HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHf4h56l7n9r:;v<x=z>|?@ABHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDEFGHIJKLMOPQRHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHSTUVWXZ[\]^_` a#bHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH&c)e/f2g5h8i;j>kAlDmGnJpPqSrHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHVsYt\u_vbwexhyk{q|t}w~z}HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$&*,.048:>@BHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDHJNRTVX\ +^ `HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHfhjl"%p(r+t.v1z7|:~=@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHCFILORX[^adgjmHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHpsy|HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[blob-2048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  +    !"#$%&'()*+,-./0123456789:;<HHHHHHHHHHHH=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyHHHHHHHHHHHHz{|}~HHHHHHHHHHHHHHHHHHHHHHHH         +                   ! " # $ % & ' ( ) * + , - . / 0 HHHHHHHHHHHH1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m HHHHHHHHHHHHn o p q r s t u v w x y z { | } ~  HHHHHHHHHHHH HHHHHHHHHHHH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +! +" +# +$ +HHHHHHHHHHHH% +& +' +( +) +* ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +\ +] +^ +_ +` +a +HHHHHHHHHHHHb +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +{ +| +} +~ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HHHHHHHHHHHH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HHHHHHHHHHHH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +         +            HHHHHHHHHHHH       ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U HHHHHHHHHHHHV W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  HHHHHHHHHHHH HHHHHHHHHHHH @`Avalue-2048-deterministic-fixturevalue-2049-deterministic-fixturevalue-2050-deterministic-fixturevalue-2052-deterministic-fixturevalue-2053-deterministic-fixturevalue-2054-deterministic-fixturevalue-2055-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2056-deterministic-fixturevalue-2057-deterministic-fixturevalue-2059-deterministic-fixturevalue-2060-deterministic-fixturevalue-2061-deterministic-fixturevalue-2062-deterministic-fixturevalue-2063-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2064-deterministic-fixturevalue-2066-deterministic-fixturevalue-2067-deterministic-fixturevalue-2068-deterministic-fixturevalue-2069-deterministic-fixturevalue-2070-deterministic-fixturevalue-2071-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2073-deterministic-fixturevalue-2074-deterministic-fixturevalue-2075-deterministic-fixturevalue-2076-deterministic-fixturevalue-2077-deterministic-fixturevalue-2078-deterministic-fixture @`value-2080-deterministic-fixturevalue-2081-deterministic-fixturevalue-2082-deterministic-fixturevalue-2083-deterministic-fixturevalue-2084-deterministic-fixturevalue-2085-deterministic-fixturevalue-2087-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2088-deterministic-fixturevalue-2089-deterministic-fixturevalue-2090-deterministic-fixturevalue-2091-deterministic-fixturevalue-2092-deterministic-fixturevalue-2094-deterministic-fixturevalue-2095-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2096-deterministic-fixturevalue-2097-deterministic-fixturevalue-2098-deterministic-fixturevalue-2099-deterministic-fixturevalue-2101-deterministic-fixturevalue-2102-deterministic-fixturevalue-2103-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2104-deterministic-fixturevalue-2105-deterministic-fixturevalue-2106-deterministic-fixturevalue-2108-deterministic-fixturevalue-2109-deterministic-fixturevalue-2110-deterministic-fixturevalue-2111-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2112-deterministic-fixturevalue-2113-deterministic-fixturevalue-2115-deterministic-fixturevalue-2116-deterministic-fixturevalue-2117-deterministic-fixturevalue-2118-deterministic-fixturevalue-2119-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2120-deterministic-fixturevalue-2122-deterministic-fixturevalue-2123-deterministic-fixturevalue-2124-deterministic-fixturevalue-2125-deterministic-fixturevalue-2126-deterministic-fixturevalue-2127-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2129-deterministic-fixturevalue-2130-deterministic-fixturevalue-2131-deterministic-fixturevalue-2132-deterministic-fixturevalue-2133-deterministic-fixturevalue-2134-deterministic-fixture @`value-2136-deterministic-fixturevalue-2137-deterministic-fixturevalue-2138-deterministic-fixturevalue-2139-deterministic-fixturevalue-2140-deterministic-fixturevalue-2141-deterministic-fixturevalue-2143-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2144-deterministic-fixturevalue-2145-deterministic-fixturevalue-2146-deterministic-fixturevalue-2147-deterministic-fixturevalue-2148-deterministic-fixturevalue-2150-deterministic-fixturevalue-2151-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2152-deterministic-fixturevalue-2153-deterministic-fixturevalue-2154-deterministic-fixturevalue-2155-deterministic-fixturevalue-2157-deterministic-fixturevalue-2158-deterministic-fixturevalue-2159-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2160-deterministic-fixturevalue-2161-deterministic-fixturevalue-2162-deterministic-fixturevalue-2164-deterministic-fixturevalue-2165-deterministic-fixturevalue-2166-deterministic-fixturevalue-2167-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2168-deterministic-fixturevalue-2169-deterministic-fixturevalue-2171-deterministic-fixturevalue-2172-deterministic-fixturevalue-2173-deterministic-fixturevalue-2174-deterministic-fixturevalue-2175-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2176-deterministic-fixturevalue-2178-deterministic-fixturevalue-2179-deterministic-fixturevalue-2180-deterministic-fixturevalue-2181-deterministic-fixturevalue-2182-deterministic-fixturevalue-2183-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2185-deterministic-fixturevalue-2186-deterministic-fixturevalue-2187-deterministic-fixturevalue-2188-deterministic-fixturevalue-2189-deterministic-fixturevalue-2190-deterministic-fixture @`value-2192-deterministic-fixturevalue-2193-deterministic-fixturevalue-2194-deterministic-fixturevalue-2195-deterministic-fixturevalue-2196-deterministic-fixturevalue-2197-deterministic-fixturevalue-2199-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2200-deterministic-fixturevalue-2201-deterministic-fixturevalue-2202-deterministic-fixturevalue-2203-deterministic-fixturevalue-2204-deterministic-fixturevalue-2206-deterministic-fixturevalue-2207-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2208-deterministic-fixturevalue-2209-deterministic-fixturevalue-2210-deterministic-fixturevalue-2211-deterministic-fixturevalue-2213-deterministic-fixturevalue-2214-deterministic-fixturevalue-2215-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2216-deterministic-fixturevalue-2217-deterministic-fixturevalue-2218-deterministic-fixturevalue-2220-deterministic-fixturevalue-2221-deterministic-fixturevalue-2222-deterministic-fixturevalue-2223-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2224-deterministic-fixturevalue-2225-deterministic-fixturevalue-2227-deterministic-fixturevalue-2228-deterministic-fixturevalue-2229-deterministic-fixturevalue-2230-deterministic-fixturevalue-2231-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2232-deterministic-fixturevalue-2234-deterministic-fixturevalue-2235-deterministic-fixturevalue-2236-deterministic-fixturevalue-2237-deterministic-fixturevalue-2238-deterministic-fixturevalue-2239-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2241-deterministic-fixturevalue-2242-deterministic-fixturevalue-2243-deterministic-fixturevalue-2244-deterministic-fixturevalue-2245-deterministic-fixturevalue-2246-deterministic-fixture @`value-2248-deterministic-fixturevalue-2249-deterministic-fixturevalue-2250-deterministic-fixturevalue-2251-deterministic-fixturevalue-2252-deterministic-fixturevalue-2253-deterministic-fixturevalue-2255-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2256-deterministic-fixturevalue-2257-deterministic-fixturevalue-2258-deterministic-fixturevalue-2259-deterministic-fixturevalue-2260-deterministic-fixturevalue-2262-deterministic-fixturevalue-2263-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2264-deterministic-fixturevalue-2265-deterministic-fixturevalue-2266-deterministic-fixturevalue-2267-deterministic-fixturevalue-2269-deterministic-fixturevalue-2270-deterministic-fixturevalue-2271-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2272-deterministic-fixturevalue-2273-deterministic-fixturevalue-2274-deterministic-fixturevalue-2276-deterministic-fixturevalue-2277-deterministic-fixturevalue-2278-deterministic-fixturevalue-2279-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2280-deterministic-fixturevalue-2281-deterministic-fixturevalue-2283-deterministic-fixturevalue-2284-deterministic-fixturevalue-2285-deterministic-fixturevalue-2286-deterministic-fixturevalue-2287-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2288-deterministic-fixturevalue-2290-deterministic-fixturevalue-2291-deterministic-fixturevalue-2292-deterministic-fixturevalue-2293-deterministic-fixturevalue-2294-deterministic-fixturevalue-2295-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2297-deterministic-fixturevalue-2298-deterministic-fixturevalue-2299-deterministic-fixturevalue-2300-deterministic-fixturevalue-2301-deterministic-fixturevalue-2302-deterministic-fixture @`value-2304-deterministic-fixturevalue-2305-deterministic-fixturevalue-2306-deterministic-fixturevalue-2307-deterministic-fixturevalue-2308-deterministic-fixturevalue-2309-deterministic-fixturevalue-2311-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2312-deterministic-fixturevalue-2313-deterministic-fixturevalue-2314-deterministic-fixturevalue-2315-deterministic-fixturevalue-2316-deterministic-fixturevalue-2318-deterministic-fixturevalue-2319-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2320-deterministic-fixturevalue-2321-deterministic-fixturevalue-2322-deterministic-fixturevalue-2323-deterministic-fixturevalue-2325-deterministic-fixturevalue-2326-deterministic-fixturevalue-2327-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2328-deterministic-fixturevalue-2329-deterministic-fixturevalue-2330-deterministic-fixturevalue-2332-deterministic-fixturevalue-2333-deterministic-fixturevalue-2334-deterministic-fixturevalue-2335-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2336-deterministic-fixturevalue-2337-deterministic-fixturevalue-2339-deterministic-fixturevalue-2340-deterministic-fixturevalue-2341-deterministic-fixturevalue-2342-deterministic-fixturevalue-2343-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2344-deterministic-fixturevalue-2346-deterministic-fixturevalue-2347-deterministic-fixturevalue-2348-deterministic-fixturevalue-2349-deterministic-fixturevalue-2350-deterministic-fixturevalue-2351-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2353-deterministic-fixturevalue-2354-deterministic-fixturevalue-2355-deterministic-fixturevalue-2356-deterministic-fixturevalue-2357-deterministic-fixturevalue-2358-deterministic-fixture @`value-2360-deterministic-fixturevalue-2361-deterministic-fixturevalue-2362-deterministic-fixturevalue-2363-deterministic-fixturevalue-2364-deterministic-fixturevalue-2365-deterministic-fixturevalue-2367-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2368-deterministic-fixturevalue-2369-deterministic-fixturevalue-2370-deterministic-fixturevalue-2371-deterministic-fixturevalue-2372-deterministic-fixturevalue-2374-deterministic-fixturevalue-2375-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2376-deterministic-fixturevalue-2377-deterministic-fixturevalue-2378-deterministic-fixturevalue-2379-deterministic-fixturevalue-2381-deterministic-fixturevalue-2382-deterministic-fixturevalue-2383-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2384-deterministic-fixturevalue-2385-deterministic-fixturevalue-2386-deterministic-fixturevalue-2388-deterministic-fixturevalue-2389-deterministic-fixturevalue-2390-deterministic-fixturevalue-2391-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2392-deterministic-fixturevalue-2393-deterministic-fixturevalue-2395-deterministic-fixturevalue-2396-deterministic-fixturevalue-2397-deterministic-fixturevalue-2398-deterministic-fixturevalue-2399-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2400-deterministic-fixturevalue-2402-deterministic-fixturevalue-2403-deterministic-fixturevalue-2404-deterministic-fixturevalue-2405-deterministic-fixturevalue-2406-deterministic-fixturevalue-2407-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2409-deterministic-fixturevalue-2410-deterministic-fixturevalue-2411-deterministic-fixturevalue-2412-deterministic-fixturevalue-2413-deterministic-fixturevalue-2414-deterministic-fixture @`value-2416-deterministic-fixturevalue-2417-deterministic-fixturevalue-2418-deterministic-fixturevalue-2419-deterministic-fixturevalue-2420-deterministic-fixturevalue-2421-deterministic-fixturevalue-2423-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2424-deterministic-fixturevalue-2425-deterministic-fixturevalue-2426-deterministic-fixturevalue-2427-deterministic-fixturevalue-2428-deterministic-fixturevalue-2430-deterministic-fixturevalue-2431-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2432-deterministic-fixturevalue-2433-deterministic-fixturevalue-2434-deterministic-fixturevalue-2435-deterministic-fixturevalue-2437-deterministic-fixturevalue-2438-deterministic-fixturevalue-2439-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2440-deterministic-fixturevalue-2441-deterministic-fixturevalue-2442-deterministic-fixturevalue-2444-deterministic-fixturevalue-2445-deterministic-fixturevalue-2446-deterministic-fixturevalue-2447-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2448-deterministic-fixturevalue-2449-deterministic-fixturevalue-2451-deterministic-fixturevalue-2452-deterministic-fixturevalue-2453-deterministic-fixturevalue-2454-deterministic-fixturevalue-2455-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2456-deterministic-fixturevalue-2458-deterministic-fixturevalue-2459-deterministic-fixturevalue-2460-deterministic-fixturevalue-2461-deterministic-fixturevalue-2462-deterministic-fixturevalue-2463-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2465-deterministic-fixturevalue-2466-deterministic-fixturevalue-2467-deterministic-fixturevalue-2468-deterministic-fixturevalue-2469-deterministic-fixturevalue-2470-deterministic-fixture @`value-2472-deterministic-fixturevalue-2473-deterministic-fixturevalue-2474-deterministic-fixturevalue-2475-deterministic-fixturevalue-2476-deterministic-fixturevalue-2477-deterministic-fixturevalue-2479-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2480-deterministic-fixturevalue-2481-deterministic-fixturevalue-2482-deterministic-fixturevalue-2483-deterministic-fixturevalue-2484-deterministic-fixturevalue-2486-deterministic-fixturevalue-2487-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2488-deterministic-fixturevalue-2489-deterministic-fixturevalue-2490-deterministic-fixturevalue-2491-deterministic-fixturevalue-2493-deterministic-fixturevalue-2494-deterministic-fixturevalue-2495-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2496-deterministic-fixturevalue-2497-deterministic-fixturevalue-2498-deterministic-fixturevalue-2500-deterministic-fixturevalue-2501-deterministic-fixturevalue-2502-deterministic-fixturevalue-2503-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2504-deterministic-fixturevalue-2505-deterministic-fixturevalue-2507-deterministic-fixturevalue-2508-deterministic-fixturevalue-2509-deterministic-fixturevalue-2510-deterministic-fixturevalue-2511-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2512-deterministic-fixturevalue-2514-deterministic-fixturevalue-2515-deterministic-fixturevalue-2516-deterministic-fixturevalue-2517-deterministic-fixturevalue-2518-deterministic-fixturevalue-2519-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2521-deterministic-fixturevalue-2522-deterministic-fixturevalue-2523-deterministic-fixturevalue-2524-deterministic-fixturevalue-2525-deterministic-fixturevalue-2526-deterministic-fixture @`value-2528-deterministic-fixturevalue-2529-deterministic-fixturevalue-2530-deterministic-fixturevalue-2531-deterministic-fixturevalue-2532-deterministic-fixturevalue-2533-deterministic-fixturevalue-2535-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2536-deterministic-fixturevalue-2537-deterministic-fixturevalue-2538-deterministic-fixturevalue-2539-deterministic-fixturevalue-2540-deterministic-fixturevalue-2542-deterministic-fixturevalue-2543-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2544-deterministic-fixturevalue-2545-deterministic-fixturevalue-2546-deterministic-fixturevalue-2547-deterministic-fixturevalue-2549-deterministic-fixturevalue-2550-deterministic-fixturevalue-2551-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2552-deterministic-fixturevalue-2553-deterministic-fixturevalue-2554-deterministic-fixturevalue-2556-deterministic-fixturevalue-2557-deterministic-fixturevalue-2558-deterministic-fixturevalue-2559-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2560-deterministic-fixturevalue-2561-deterministic-fixturevalue-2563-deterministic-fixturevalue-2564-deterministic-fixturevalue-2565-deterministic-fixturevalue-2566-deterministic-fixturevalue-2567-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2568-deterministic-fixturevalue-2570-deterministic-fixturevalue-2571-deterministic-fixturevalue-2572-deterministic-fixturevalue-2573-deterministic-fixturevalue-2574-deterministic-fixturevalue-2575-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2577-deterministic-fixturevalue-2578-deterministic-fixturevalue-2579-deterministic-fixturevalue-2580-deterministic-fixturevalue-2581-deterministic-fixturevalue-2582-deterministic-fixture @`value-2584-deterministic-fixturevalue-2585-deterministic-fixturevalue-2586-deterministic-fixturevalue-2587-deterministic-fixturevalue-2588-deterministic-fixturevalue-2589-deterministic-fixturevalue-2591-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2592-deterministic-fixturevalue-2593-deterministic-fixturevalue-2594-deterministic-fixturevalue-2595-deterministic-fixturevalue-2596-deterministic-fixturevalue-2598-deterministic-fixturevalue-2599-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2600-deterministic-fixturevalue-2601-deterministic-fixturevalue-2602-deterministic-fixturevalue-2603-deterministic-fixturevalue-2605-deterministic-fixturevalue-2606-deterministic-fixturevalue-2607-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2608-deterministic-fixturevalue-2609-deterministic-fixturevalue-2610-deterministic-fixturevalue-2612-deterministic-fixturevalue-2613-deterministic-fixturevalue-2614-deterministic-fixturevalue-2615-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2616-deterministic-fixturevalue-2617-deterministic-fixturevalue-2619-deterministic-fixturevalue-2620-deterministic-fixturevalue-2621-deterministic-fixturevalue-2622-deterministic-fixturevalue-2623-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2624-deterministic-fixturevalue-2626-deterministic-fixturevalue-2627-deterministic-fixturevalue-2628-deterministic-fixturevalue-2629-deterministic-fixturevalue-2630-deterministic-fixturevalue-2631-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2633-deterministic-fixturevalue-2634-deterministic-fixturevalue-2635-deterministic-fixturevalue-2636-deterministic-fixturevalue-2637-deterministic-fixturevalue-2638-deterministic-fixture @`value-2640-deterministic-fixturevalue-2641-deterministic-fixturevalue-2642-deterministic-fixturevalue-2643-deterministic-fixturevalue-2644-deterministic-fixturevalue-2645-deterministic-fixturevalue-2647-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2648-deterministic-fixturevalue-2649-deterministic-fixturevalue-2650-deterministic-fixturevalue-2651-deterministic-fixturevalue-2652-deterministic-fixturevalue-2654-deterministic-fixturevalue-2655-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2656-deterministic-fixturevalue-2657-deterministic-fixturevalue-2658-deterministic-fixturevalue-2659-deterministic-fixturevalue-2661-deterministic-fixturevalue-2662-deterministic-fixturevalue-2663-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2664-deterministic-fixturevalue-2665-deterministic-fixturevalue-2666-deterministic-fixturevalue-2668-deterministic-fixturevalue-2669-deterministic-fixturevalue-2670-deterministic-fixturevalue-2671-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2672-deterministic-fixturevalue-2673-deterministic-fixturevalue-2675-deterministic-fixturevalue-2676-deterministic-fixturevalue-2677-deterministic-fixturevalue-2678-deterministic-fixturevalue-2679-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2680-deterministic-fixturevalue-2682-deterministic-fixturevalue-2683-deterministic-fixturevalue-2684-deterministic-fixturevalue-2685-deterministic-fixturevalue-2686-deterministic-fixturevalue-2687-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2689-deterministic-fixturevalue-2690-deterministic-fixturevalue-2691-deterministic-fixturevalue-2692-deterministic-fixturevalue-2693-deterministic-fixturevalue-2694-deterministic-fixture @`value-2696-deterministic-fixturevalue-2697-deterministic-fixturevalue-2698-deterministic-fixturevalue-2699-deterministic-fixturevalue-2700-deterministic-fixturevalue-2701-deterministic-fixturevalue-2703-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2704-deterministic-fixturevalue-2705-deterministic-fixturevalue-2706-deterministic-fixturevalue-2707-deterministic-fixturevalue-2708-deterministic-fixturevalue-2710-deterministic-fixturevalue-2711-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2712-deterministic-fixturevalue-2713-deterministic-fixturevalue-2714-deterministic-fixturevalue-2715-deterministic-fixturevalue-2717-deterministic-fixturevalue-2718-deterministic-fixturevalue-2719-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2720-deterministic-fixturevalue-2721-deterministic-fixturevalue-2722-deterministic-fixturevalue-2724-deterministic-fixturevalue-2725-deterministic-fixturevalue-2726-deterministic-fixturevalue-2727-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2728-deterministic-fixturevalue-2729-deterministic-fixturevalue-2731-deterministic-fixturevalue-2732-deterministic-fixturevalue-2733-deterministic-fixturevalue-2734-deterministic-fixturevalue-2735-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2736-deterministic-fixturevalue-2738-deterministic-fixturevalue-2739-deterministic-fixturevalue-2740-deterministic-fixturevalue-2741-deterministic-fixturevalue-2742-deterministic-fixturevalue-2743-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2745-deterministic-fixturevalue-2746-deterministic-fixturevalue-2747-deterministic-fixturevalue-2748-deterministic-fixturevalue-2749-deterministic-fixturevalue-2750-deterministic-fixture @`value-2752-deterministic-fixturevalue-2753-deterministic-fixturevalue-2754-deterministic-fixturevalue-2755-deterministic-fixturevalue-2756-deterministic-fixturevalue-2757-deterministic-fixturevalue-2759-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2760-deterministic-fixturevalue-2761-deterministic-fixturevalue-2762-deterministic-fixturevalue-2763-deterministic-fixturevalue-2764-deterministic-fixturevalue-2766-deterministic-fixturevalue-2767-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2768-deterministic-fixturevalue-2769-deterministic-fixturevalue-2770-deterministic-fixturevalue-2771-deterministic-fixturevalue-2773-deterministic-fixturevalue-2774-deterministic-fixturevalue-2775-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2776-deterministic-fixturevalue-2777-deterministic-fixturevalue-2778-deterministic-fixturevalue-2780-deterministic-fixturevalue-2781-deterministic-fixturevalue-2782-deterministic-fixturevalue-2783-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2784-deterministic-fixturevalue-2785-deterministic-fixturevalue-2787-deterministic-fixturevalue-2788-deterministic-fixturevalue-2789-deterministic-fixturevalue-2790-deterministic-fixturevalue-2791-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2792-deterministic-fixturevalue-2794-deterministic-fixturevalue-2795-deterministic-fixturevalue-2796-deterministic-fixturevalue-2797-deterministic-fixturevalue-2798-deterministic-fixturevalue-2799-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2801-deterministic-fixturevalue-2802-deterministic-fixturevalue-2803-deterministic-fixturevalue-2804-deterministic-fixturevalue-2805-deterministic-fixturevalue-2806-deterministic-fixture @`value-2808-deterministic-fixturevalue-2809-deterministic-fixturevalue-2810-deterministic-fixturevalue-2811-deterministic-fixturevalue-2812-deterministic-fixturevalue-2813-deterministic-fixturevalue-2815-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2816-deterministic-fixturevalue-2817-deterministic-fixturevalue-2818-deterministic-fixturevalue-2819-deterministic-fixturevalue-2820-deterministic-fixturevalue-2822-deterministic-fixturevalue-2823-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2824-deterministic-fixturevalue-2825-deterministic-fixturevalue-2826-deterministic-fixturevalue-2827-deterministic-fixturevalue-2829-deterministic-fixturevalue-2830-deterministic-fixturevalue-2831-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2832-deterministic-fixturevalue-2833-deterministic-fixturevalue-2834-deterministic-fixturevalue-2836-deterministic-fixturevalue-2837-deterministic-fixturevalue-2838-deterministic-fixturevalue-2839-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2840-deterministic-fixturevalue-2841-deterministic-fixturevalue-2843-deterministic-fixturevalue-2844-deterministic-fixturevalue-2845-deterministic-fixturevalue-2846-deterministic-fixturevalue-2847-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2848-deterministic-fixturevalue-2850-deterministic-fixturevalue-2851-deterministic-fixturevalue-2852-deterministic-fixturevalue-2853-deterministic-fixturevalue-2854-deterministic-fixturevalue-2855-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2857-deterministic-fixturevalue-2858-deterministic-fixturevalue-2859-deterministic-fixturevalue-2860-deterministic-fixturevalue-2861-deterministic-fixturevalue-2862-deterministic-fixture @`value-2864-deterministic-fixturevalue-2865-deterministic-fixturevalue-2866-deterministic-fixturevalue-2867-deterministic-fixturevalue-2868-deterministic-fixturevalue-2869-deterministic-fixturevalue-2871-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2872-deterministic-fixturevalue-2873-deterministic-fixturevalue-2874-deterministic-fixturevalue-2875-deterministic-fixturevalue-2876-deterministic-fixturevalue-2878-deterministic-fixturevalue-2879-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2880-deterministic-fixturevalue-2881-deterministic-fixturevalue-2882-deterministic-fixturevalue-2883-deterministic-fixturevalue-2885-deterministic-fixturevalue-2886-deterministic-fixturevalue-2887-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2888-deterministic-fixturevalue-2889-deterministic-fixturevalue-2890-deterministic-fixturevalue-2892-deterministic-fixturevalue-2893-deterministic-fixturevalue-2894-deterministic-fixturevalue-2895-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2896-deterministic-fixturevalue-2897-deterministic-fixturevalue-2899-deterministic-fixturevalue-2900-deterministic-fixturevalue-2901-deterministic-fixturevalue-2902-deterministic-fixturevalue-2903-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2904-deterministic-fixturevalue-2906-deterministic-fixturevalue-2907-deterministic-fixturevalue-2908-deterministic-fixturevalue-2909-deterministic-fixturevalue-2910-deterministic-fixturevalue-2911-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2913-deterministic-fixturevalue-2914-deterministic-fixturevalue-2915-deterministic-fixturevalue-2916-deterministic-fixturevalue-2917-deterministic-fixturevalue-2918-deterministic-fixture @`value-2920-deterministic-fixturevalue-2921-deterministic-fixturevalue-2922-deterministic-fixturevalue-2923-deterministic-fixturevalue-2924-deterministic-fixturevalue-2925-deterministic-fixturevalue-2927-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2928-deterministic-fixturevalue-2929-deterministic-fixturevalue-2930-deterministic-fixturevalue-2931-deterministic-fixturevalue-2932-deterministic-fixturevalue-2934-deterministic-fixturevalue-2935-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2936-deterministic-fixturevalue-2937-deterministic-fixturevalue-2938-deterministic-fixturevalue-2939-deterministic-fixturevalue-2941-deterministic-fixturevalue-2942-deterministic-fixturevalue-2943-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-2944-deterministic-fixturevalue-2945-deterministic-fixturevalue-2946-deterministic-fixturevalue-2948-deterministic-fixturevalue-2949-deterministic-fixturevalue-2950-deterministic-fixturevalue-2951-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-2952-deterministic-fixturevalue-2953-deterministic-fixturevalue-2955-deterministic-fixturevalue-2956-deterministic-fixturevalue-2957-deterministic-fixturevalue-2958-deterministic-fixturevalue-2959-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2960-deterministic-fixturevalue-2962-deterministic-fixturevalue-2963-deterministic-fixturevalue-2964-deterministic-fixturevalue-2965-deterministic-fixturevalue-2966-deterministic-fixturevalue-2967-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2969-deterministic-fixturevalue-2970-deterministic-fixturevalue-2971-deterministic-fixturevalue-2972-deterministic-fixturevalue-2973-deterministic-fixturevalue-2974-deterministic-fixture @`value-2976-deterministic-fixturevalue-2977-deterministic-fixturevalue-2978-deterministic-fixturevalue-2979-deterministic-fixturevalue-2980-deterministic-fixturevalue-2981-deterministic-fixturevalue-2983-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-2984-deterministic-fixturevalue-2985-deterministic-fixturevalue-2986-deterministic-fixturevalue-2987-deterministic-fixturevalue-2988-deterministic-fixturevalue-2990-deterministic-fixturevalue-2991-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-2992-deterministic-fixturevalue-2993-deterministic-fixturevalue-2994-deterministic-fixturevalue-2995-deterministic-fixturevalue-2997-deterministic-fixturevalue-2998-deterministic-fixturevalue-2999-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3000-deterministic-fixturevalue-3001-deterministic-fixturevalue-3002-deterministic-fixturevalue-3004-deterministic-fixturevalue-3005-deterministic-fixturevalue-3006-deterministic-fixturevalue-3007-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3008-deterministic-fixturevalue-3009-deterministic-fixturevalue-3011-deterministic-fixturevalue-3012-deterministic-fixturevalue-3013-deterministic-fixturevalue-3014-deterministic-fixturevalue-3015-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3016-deterministic-fixturevalue-3018-deterministic-fixturevalue-3019-deterministic-fixturevalue-3020-deterministic-fixturevalue-3021-deterministic-fixturevalue-3022-deterministic-fixturevalue-3023-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3025-deterministic-fixturevalue-3026-deterministic-fixturevalue-3027-deterministic-fixturevalue-3028-deterministic-fixturevalue-3029-deterministic-fixturevalue-3030-deterministic-fixture @`value-3032-deterministic-fixturevalue-3033-deterministic-fixturevalue-3034-deterministic-fixturevalue-3035-deterministic-fixturevalue-3036-deterministic-fixturevalue-3037-deterministic-fixturevalue-3039-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3040-deterministic-fixturevalue-3041-deterministic-fixturevalue-3042-deterministic-fixturevalue-3043-deterministic-fixturevalue-3044-deterministic-fixturevalue-3046-deterministic-fixturevalue-3047-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3048-deterministic-fixturevalue-3049-deterministic-fixturevalue-3050-deterministic-fixturevalue-3051-deterministic-fixturevalue-3053-deterministic-fixturevalue-3054-deterministic-fixturevalue-3055-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3056-deterministic-fixturevalue-3057-deterministic-fixturevalue-3058-deterministic-fixturevalue-3060-deterministic-fixturevalue-3061-deterministic-fixturevalue-3062-deterministic-fixturevalue-3063-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3064-deterministic-fixturevalue-3065-deterministic-fixturevalue-3067-deterministic-fixturevalue-3068-deterministic-fixturevalue-3069-deterministic-fixturevalue-3070-deterministic-fixturevalue-3071-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  !$'*-0369# <?BEHKNQTWA Z]`cfiloru_ x{~}    #&) ,/258;>ADG1 JMPSVY\_beO hknqtwz}m      +  "%(+.147! :=@CFILORU? X[^adgjmps] vy|{      !$'*-0369<?BE/HKNQTWZ]`cMfilorux{~k  #&),/258;>ADGJMPS=VY\_behknq[twz}y + "%(+.147:=@C-FILORUX[^aKdgjmpsvy|i  !$'*-0369<?BEHKNQ;TWZ]`cfiloYrux{~w  # &),/258;>A+DGJMPSVY\_Ibehknqtwz}g + "%(+.147:=@CFILO9RUX[^adgjmWpsvy|u         ! $ ' * - 0 3 6 9 < ? )B E H K N Q T W Z ] G` c f i l o r u x { e~      + + + + + + + + + + +# +& +) +, +/ +2 +5 +8 +; +> +A +D +G +J +M +7P +S +V +Y +\ +_ +b +e +h +k +Un +q +t +w +z +} + + + + +s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH   +  + ! $ '*HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH- 03$6&9*?B.E0H2K4NQ8T:W<HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHZ `!Bc"Df#Fi$Hl%o&Lr'Nu(Px)R{+V,X-Z.\HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH/0`1b2d3f46l7n8p9:t;v<x=z>HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?~ABCDEFGHIJLMNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHOPQRSTUWXY Z[\]HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^_` b&c)d,e/f2g5h8i;j>kAmGnHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJoMpPqSrVsYt\u_vbxhykzn{q|t}HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHw~z} + HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH "$(*,.2468HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH<>@BHJLPRTVHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHZ^ `dfhj"n%p(t.1x4HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHz7|:~=@CFIORUX[^aHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHdgjpsvy|HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH        +    +  HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH! $ ' * - 0 $6 &9 (< ? ,B .E 0H 2K NHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH 6Q :W <Z ] @`! Bc" Df# Fi$ l% Jo& Lr( Px) {* T~+ HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHV, X- Z. / ^0 `1 b3 4 h5 j6 l7 n8 9 r: tHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH; v< x> |? ~@ A B C D E F G I J HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHK L M N O P Q R T U V W X Y  Z HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH[ \ ] _ ` a #b &c )d ,e /f 2g 5h 8j HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH>k Al Dm Gn Jo Mp Pq Sr Vs Yu _v bw ex hy kHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHz n{ q| t} w~ z      +    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH       "  & ( ,  0 2HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH 4 6  : < >  D F H J  N PHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH R T X Z \ + ^   b d f h  n% p( HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHr+ . v1 x4 z7 |: = @ F I L O R U HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHX [ ^ a g j m p s v y |   HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH               HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH              HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH        + + + +  + +  +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + + + + +! +$ +' +- + 0 +"3 +$6 +&9 +< +*? +,B +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.E +0H +4N +6Q +8T +:W +Z +>] +@`! +Bc" +Df# +i% +Jo& +Lr' +NHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHu( +x) +R{* +T~+ +V, +X- +. +\0 +`1 +b2 +3 +f4 +h5 +j6 +lHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH7 +8 +p9 +r; +v< += +z> +|? +~@ +A +B +C +D +F +G +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +I +J +K +L +M +N +O +Q +R +S +T +U +V +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHW +X +Y + Z +\ +] +^ +_ +` + a +#b +&c +)d +,e +/HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHg +5h +8i +;j +>k +Al +Dm +Gn +Jo +Mp +Pr +Vs +Yt +\u +_v +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHbw +ex +hy +kz +n{ +q} +w~ +z +} + + + + + + +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +  + + + + + + + + +  + +& +( +*HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + +. +0 +2 +4 + +8 +< +> + +B +D +F +H +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +L +N +R + +V +X +Z +\ + + +` +b +d + +jHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +l" +n% +p( ++ +t. +v1 +x4 +z7 +~= +@ +C +F +I +L HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +O +R +U +X +^ +a +d +g +j +m +p +s +v +y +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + + + + + + + + + + + + + +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + + + + + + + + + + + + + + HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + + + + + + + + + + +  ! !  ! HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH ! +! ! ! ! ! + ! $! '! *! -! 0! "3! $6! HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH9! (]! @`!" f!# Fi!HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$ Hl!% Jo!& Lr!' u!( Px!) R{!* T~!+ V!- Z!. \!/ ^!0 `!1 !2 d!3 HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHf!4 h!5 j!6 !8 p!9 r!: t!; !< x!= z!> |!? ~!@ !A !C HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!D !E !F !G !H !I !J !K !L !N !O !P !Q !R !HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHS !T !U !V "W "Y "Z "[ "\ "] "^ "_ "`  "a #"b HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH&"d ,"e /"f 2"g 5"h 8"i ;"j >"k A"l D"m G"o M"p P"q S"r HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHV"s Y"t \"u _"v b"w e"x h"z n"{ q"| t"} w"~ z" }" " "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH " " +" " " " " " " " " " " "" HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$" &" (" " ," ." 0" 2" 6" 8" :" <" " @" BHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" D" F" " L" N" P" " T" V# X# Z# +# ^ # b#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH d# # h# j# l"# n%# (# r+# t.# x4# 7# |:# ~=# @# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHC# F# I# L# O# U# X# [# ^# a# d# g# j# m# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHp# v# y# |# # # # # # # # # # # #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH # # # # # # # # # # # # # # HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH# # # # # # # # # # # # # #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@blob-3072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH         +                   ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < HHHHHHHHHHHH= > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y HHHHHHHHHHHHz { | } ~  HHHHHHHHHHHH HHHHHHHHHHHH         +                   ! " # $ % & ' ( ) * + , - . / 0 HHHHHHHHHHHH1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m HHHHHHHHHHHHn o p q r s t u v w x y z { | } ~  HHHHHHHHHHHH HHHHHHHHHHHH   +    !"#$HHHHHHHHHHHH%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`aHHHHHHHHHHHHbcdefghijklmnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH  +   HHHHHHHHHHHH !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUHHHHHHHHHHHHVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHH @`value-3072-deterministic-fixturevalue-3074-deterministic-fixturevalue-3075-deterministic-fixturevalue-3076-deterministic-fixturevalue-3077-deterministic-fixturevalue-3078-deterministic-fixturevalue-3079-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3081-deterministic-fixturevalue-3082-deterministic-fixturevalue-3083-deterministic-fixturevalue-3084-deterministic-fixturevalue-3085-deterministic-fixturevalue-3086-deterministic-fixture @`value-3088-deterministic-fixturevalue-3089-deterministic-fixturevalue-3090-deterministic-fixturevalue-3091-deterministic-fixturevalue-3092-deterministic-fixturevalue-3093-deterministic-fixturevalue-3095-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3096-deterministic-fixturevalue-3097-deterministic-fixturevalue-3098-deterministic-fixturevalue-3099-deterministic-fixturevalue-3100-deterministic-fixturevalue-3102-deterministic-fixturevalue-3103-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3104-deterministic-fixturevalue-3105-deterministic-fixturevalue-3106-deterministic-fixturevalue-3107-deterministic-fixturevalue-3109-deterministic-fixturevalue-3110-deterministic-fixturevalue-3111-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3112-deterministic-fixturevalue-3113-deterministic-fixturevalue-3114-deterministic-fixturevalue-3116-deterministic-fixturevalue-3117-deterministic-fixturevalue-3118-deterministic-fixturevalue-3119-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3120-deterministic-fixturevalue-3121-deterministic-fixturevalue-3123-deterministic-fixturevalue-3124-deterministic-fixturevalue-3125-deterministic-fixturevalue-3126-deterministic-fixturevalue-3127-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3128-deterministic-fixturevalue-3130-deterministic-fixturevalue-3131-deterministic-fixturevalue-3132-deterministic-fixturevalue-3133-deterministic-fixturevalue-3134-deterministic-fixturevalue-3135-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3137-deterministic-fixturevalue-3138-deterministic-fixturevalue-3139-deterministic-fixturevalue-3140-deterministic-fixturevalue-3141-deterministic-fixturevalue-3142-deterministic-fixture @`value-3144-deterministic-fixturevalue-3145-deterministic-fixturevalue-3146-deterministic-fixturevalue-3147-deterministic-fixturevalue-3148-deterministic-fixturevalue-3149-deterministic-fixturevalue-3151-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3152-deterministic-fixturevalue-3153-deterministic-fixturevalue-3154-deterministic-fixturevalue-3155-deterministic-fixturevalue-3156-deterministic-fixturevalue-3158-deterministic-fixturevalue-3159-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3160-deterministic-fixturevalue-3161-deterministic-fixturevalue-3162-deterministic-fixturevalue-3163-deterministic-fixturevalue-3165-deterministic-fixturevalue-3166-deterministic-fixturevalue-3167-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3168-deterministic-fixturevalue-3169-deterministic-fixturevalue-3170-deterministic-fixturevalue-3172-deterministic-fixturevalue-3173-deterministic-fixturevalue-3174-deterministic-fixturevalue-3175-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3176-deterministic-fixturevalue-3177-deterministic-fixturevalue-3179-deterministic-fixturevalue-3180-deterministic-fixturevalue-3181-deterministic-fixturevalue-3182-deterministic-fixturevalue-3183-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3184-deterministic-fixturevalue-3186-deterministic-fixturevalue-3187-deterministic-fixturevalue-3188-deterministic-fixturevalue-3189-deterministic-fixturevalue-3190-deterministic-fixturevalue-3191-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3193-deterministic-fixturevalue-3194-deterministic-fixturevalue-3195-deterministic-fixturevalue-3196-deterministic-fixturevalue-3197-deterministic-fixturevalue-3198-deterministic-fixture @`value-3200-deterministic-fixturevalue-3201-deterministic-fixturevalue-3202-deterministic-fixturevalue-3203-deterministic-fixturevalue-3204-deterministic-fixturevalue-3205-deterministic-fixturevalue-3207-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3208-deterministic-fixturevalue-3209-deterministic-fixturevalue-3210-deterministic-fixturevalue-3211-deterministic-fixturevalue-3212-deterministic-fixturevalue-3214-deterministic-fixturevalue-3215-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3216-deterministic-fixturevalue-3217-deterministic-fixturevalue-3218-deterministic-fixturevalue-3219-deterministic-fixturevalue-3221-deterministic-fixturevalue-3222-deterministic-fixturevalue-3223-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3224-deterministic-fixturevalue-3225-deterministic-fixturevalue-3226-deterministic-fixturevalue-3228-deterministic-fixturevalue-3229-deterministic-fixturevalue-3230-deterministic-fixturevalue-3231-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3232-deterministic-fixturevalue-3233-deterministic-fixturevalue-3235-deterministic-fixturevalue-3236-deterministic-fixturevalue-3237-deterministic-fixturevalue-3238-deterministic-fixturevalue-3239-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3240-deterministic-fixturevalue-3242-deterministic-fixturevalue-3243-deterministic-fixturevalue-3244-deterministic-fixturevalue-3245-deterministic-fixturevalue-3246-deterministic-fixturevalue-3247-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3249-deterministic-fixturevalue-3250-deterministic-fixturevalue-3251-deterministic-fixturevalue-3252-deterministic-fixturevalue-3253-deterministic-fixturevalue-3254-deterministic-fixture @`value-3256-deterministic-fixturevalue-3257-deterministic-fixturevalue-3258-deterministic-fixturevalue-3259-deterministic-fixturevalue-3260-deterministic-fixturevalue-3261-deterministic-fixturevalue-3263-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3264-deterministic-fixturevalue-3265-deterministic-fixturevalue-3266-deterministic-fixturevalue-3267-deterministic-fixturevalue-3268-deterministic-fixturevalue-3270-deterministic-fixturevalue-3271-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3272-deterministic-fixturevalue-3273-deterministic-fixturevalue-3274-deterministic-fixturevalue-3275-deterministic-fixturevalue-3277-deterministic-fixturevalue-3278-deterministic-fixturevalue-3279-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3280-deterministic-fixturevalue-3281-deterministic-fixturevalue-3282-deterministic-fixturevalue-3284-deterministic-fixturevalue-3285-deterministic-fixturevalue-3286-deterministic-fixturevalue-3287-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3288-deterministic-fixturevalue-3289-deterministic-fixturevalue-3291-deterministic-fixturevalue-3292-deterministic-fixturevalue-3293-deterministic-fixturevalue-3294-deterministic-fixturevalue-3295-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3296-deterministic-fixturevalue-3298-deterministic-fixturevalue-3299-deterministic-fixturevalue-3300-deterministic-fixturevalue-3301-deterministic-fixturevalue-3302-deterministic-fixturevalue-3303-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3305-deterministic-fixturevalue-3306-deterministic-fixturevalue-3307-deterministic-fixturevalue-3308-deterministic-fixturevalue-3309-deterministic-fixturevalue-3310-deterministic-fixture @`value-3312-deterministic-fixturevalue-3313-deterministic-fixturevalue-3314-deterministic-fixturevalue-3315-deterministic-fixturevalue-3316-deterministic-fixturevalue-3317-deterministic-fixturevalue-3319-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3320-deterministic-fixturevalue-3321-deterministic-fixturevalue-3322-deterministic-fixturevalue-3323-deterministic-fixturevalue-3324-deterministic-fixturevalue-3326-deterministic-fixturevalue-3327-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3328-deterministic-fixturevalue-3329-deterministic-fixturevalue-3330-deterministic-fixturevalue-3331-deterministic-fixturevalue-3333-deterministic-fixturevalue-3334-deterministic-fixturevalue-3335-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3336-deterministic-fixturevalue-3337-deterministic-fixturevalue-3338-deterministic-fixturevalue-3340-deterministic-fixturevalue-3341-deterministic-fixturevalue-3342-deterministic-fixturevalue-3343-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3344-deterministic-fixturevalue-3345-deterministic-fixturevalue-3347-deterministic-fixturevalue-3348-deterministic-fixturevalue-3349-deterministic-fixturevalue-3350-deterministic-fixturevalue-3351-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3352-deterministic-fixturevalue-3354-deterministic-fixturevalue-3355-deterministic-fixturevalue-3356-deterministic-fixturevalue-3357-deterministic-fixturevalue-3358-deterministic-fixturevalue-3359-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3361-deterministic-fixturevalue-3362-deterministic-fixturevalue-3363-deterministic-fixturevalue-3364-deterministic-fixturevalue-3365-deterministic-fixturevalue-3366-deterministic-fixture @`value-3368-deterministic-fixturevalue-3369-deterministic-fixturevalue-3370-deterministic-fixturevalue-3371-deterministic-fixturevalue-3372-deterministic-fixturevalue-3373-deterministic-fixturevalue-3375-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3376-deterministic-fixturevalue-3377-deterministic-fixturevalue-3378-deterministic-fixturevalue-3379-deterministic-fixturevalue-3380-deterministic-fixturevalue-3382-deterministic-fixturevalue-3383-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3384-deterministic-fixturevalue-3385-deterministic-fixturevalue-3386-deterministic-fixturevalue-3387-deterministic-fixturevalue-3389-deterministic-fixturevalue-3390-deterministic-fixturevalue-3391-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3392-deterministic-fixturevalue-3393-deterministic-fixturevalue-3394-deterministic-fixturevalue-3396-deterministic-fixturevalue-3397-deterministic-fixturevalue-3398-deterministic-fixturevalue-3399-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3400-deterministic-fixturevalue-3401-deterministic-fixturevalue-3403-deterministic-fixturevalue-3404-deterministic-fixturevalue-3405-deterministic-fixturevalue-3406-deterministic-fixturevalue-3407-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3408-deterministic-fixturevalue-3410-deterministic-fixturevalue-3411-deterministic-fixturevalue-3412-deterministic-fixturevalue-3413-deterministic-fixturevalue-3414-deterministic-fixturevalue-3415-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3417-deterministic-fixturevalue-3418-deterministic-fixturevalue-3419-deterministic-fixturevalue-3420-deterministic-fixturevalue-3421-deterministic-fixturevalue-3422-deterministic-fixture @`value-3424-deterministic-fixturevalue-3425-deterministic-fixturevalue-3426-deterministic-fixturevalue-3427-deterministic-fixturevalue-3428-deterministic-fixturevalue-3429-deterministic-fixturevalue-3431-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3432-deterministic-fixturevalue-3433-deterministic-fixturevalue-3434-deterministic-fixturevalue-3435-deterministic-fixturevalue-3436-deterministic-fixturevalue-3438-deterministic-fixturevalue-3439-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3440-deterministic-fixturevalue-3441-deterministic-fixturevalue-3442-deterministic-fixturevalue-3443-deterministic-fixturevalue-3445-deterministic-fixturevalue-3446-deterministic-fixturevalue-3447-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3448-deterministic-fixturevalue-3449-deterministic-fixturevalue-3450-deterministic-fixturevalue-3452-deterministic-fixturevalue-3453-deterministic-fixturevalue-3454-deterministic-fixturevalue-3455-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3456-deterministic-fixturevalue-3457-deterministic-fixturevalue-3459-deterministic-fixturevalue-3460-deterministic-fixturevalue-3461-deterministic-fixturevalue-3462-deterministic-fixturevalue-3463-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3464-deterministic-fixturevalue-3466-deterministic-fixturevalue-3467-deterministic-fixturevalue-3468-deterministic-fixturevalue-3469-deterministic-fixturevalue-3470-deterministic-fixturevalue-3471-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3473-deterministic-fixturevalue-3474-deterministic-fixturevalue-3475-deterministic-fixturevalue-3476-deterministic-fixturevalue-3477-deterministic-fixturevalue-3478-deterministic-fixture @`value-3480-deterministic-fixturevalue-3481-deterministic-fixturevalue-3482-deterministic-fixturevalue-3483-deterministic-fixturevalue-3484-deterministic-fixturevalue-3485-deterministic-fixturevalue-3487-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3488-deterministic-fixturevalue-3489-deterministic-fixturevalue-3490-deterministic-fixturevalue-3491-deterministic-fixturevalue-3492-deterministic-fixturevalue-3494-deterministic-fixturevalue-3495-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3496-deterministic-fixturevalue-3497-deterministic-fixturevalue-3498-deterministic-fixturevalue-3499-deterministic-fixturevalue-3501-deterministic-fixturevalue-3502-deterministic-fixturevalue-3503-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3504-deterministic-fixturevalue-3505-deterministic-fixturevalue-3506-deterministic-fixturevalue-3508-deterministic-fixturevalue-3509-deterministic-fixturevalue-3510-deterministic-fixturevalue-3511-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3512-deterministic-fixturevalue-3513-deterministic-fixturevalue-3515-deterministic-fixturevalue-3516-deterministic-fixturevalue-3517-deterministic-fixturevalue-3518-deterministic-fixturevalue-3519-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3520-deterministic-fixturevalue-3522-deterministic-fixturevalue-3523-deterministic-fixturevalue-3524-deterministic-fixturevalue-3525-deterministic-fixturevalue-3526-deterministic-fixturevalue-3527-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3529-deterministic-fixturevalue-3530-deterministic-fixturevalue-3531-deterministic-fixturevalue-3532-deterministic-fixturevalue-3533-deterministic-fixturevalue-3534-deterministic-fixture @`value-3536-deterministic-fixturevalue-3537-deterministic-fixturevalue-3538-deterministic-fixturevalue-3539-deterministic-fixturevalue-3540-deterministic-fixturevalue-3541-deterministic-fixturevalue-3543-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3544-deterministic-fixturevalue-3545-deterministic-fixturevalue-3546-deterministic-fixturevalue-3547-deterministic-fixturevalue-3548-deterministic-fixturevalue-3550-deterministic-fixturevalue-3551-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3552-deterministic-fixturevalue-3553-deterministic-fixturevalue-3554-deterministic-fixturevalue-3555-deterministic-fixturevalue-3557-deterministic-fixturevalue-3558-deterministic-fixturevalue-3559-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3560-deterministic-fixturevalue-3561-deterministic-fixturevalue-3562-deterministic-fixturevalue-3564-deterministic-fixturevalue-3565-deterministic-fixturevalue-3566-deterministic-fixturevalue-3567-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3568-deterministic-fixturevalue-3569-deterministic-fixturevalue-3571-deterministic-fixturevalue-3572-deterministic-fixturevalue-3573-deterministic-fixturevalue-3574-deterministic-fixturevalue-3575-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3576-deterministic-fixturevalue-3578-deterministic-fixturevalue-3579-deterministic-fixturevalue-3580-deterministic-fixturevalue-3581-deterministic-fixturevalue-3582-deterministic-fixturevalue-3583-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3585-deterministic-fixturevalue-3586-deterministic-fixturevalue-3587-deterministic-fixturevalue-3588-deterministic-fixturevalue-3589-deterministic-fixturevalue-3590-deterministic-fixture @`value-3592-deterministic-fixturevalue-3593-deterministic-fixturevalue-3594-deterministic-fixturevalue-3595-deterministic-fixturevalue-3596-deterministic-fixturevalue-3597-deterministic-fixturevalue-3599-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3600-deterministic-fixturevalue-3601-deterministic-fixturevalue-3602-deterministic-fixturevalue-3603-deterministic-fixturevalue-3604-deterministic-fixturevalue-3606-deterministic-fixturevalue-3607-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3608-deterministic-fixturevalue-3609-deterministic-fixturevalue-3610-deterministic-fixturevalue-3611-deterministic-fixturevalue-3613-deterministic-fixturevalue-3614-deterministic-fixturevalue-3615-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3616-deterministic-fixturevalue-3617-deterministic-fixturevalue-3618-deterministic-fixturevalue-3620-deterministic-fixturevalue-3621-deterministic-fixturevalue-3622-deterministic-fixturevalue-3623-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3624-deterministic-fixturevalue-3625-deterministic-fixturevalue-3627-deterministic-fixturevalue-3628-deterministic-fixturevalue-3629-deterministic-fixturevalue-3630-deterministic-fixturevalue-3631-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3632-deterministic-fixturevalue-3634-deterministic-fixturevalue-3635-deterministic-fixturevalue-3636-deterministic-fixturevalue-3637-deterministic-fixturevalue-3638-deterministic-fixturevalue-3639-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3641-deterministic-fixturevalue-3642-deterministic-fixturevalue-3643-deterministic-fixturevalue-3644-deterministic-fixturevalue-3645-deterministic-fixturevalue-3646-deterministic-fixture @`value-3648-deterministic-fixturevalue-3649-deterministic-fixturevalue-3650-deterministic-fixturevalue-3651-deterministic-fixturevalue-3652-deterministic-fixturevalue-3653-deterministic-fixturevalue-3655-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3656-deterministic-fixturevalue-3657-deterministic-fixturevalue-3658-deterministic-fixturevalue-3659-deterministic-fixturevalue-3660-deterministic-fixturevalue-3662-deterministic-fixturevalue-3663-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3664-deterministic-fixturevalue-3665-deterministic-fixturevalue-3666-deterministic-fixturevalue-3667-deterministic-fixturevalue-3669-deterministic-fixturevalue-3670-deterministic-fixturevalue-3671-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3672-deterministic-fixturevalue-3673-deterministic-fixturevalue-3674-deterministic-fixturevalue-3676-deterministic-fixturevalue-3677-deterministic-fixturevalue-3678-deterministic-fixturevalue-3679-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3680-deterministic-fixturevalue-3681-deterministic-fixturevalue-3683-deterministic-fixturevalue-3684-deterministic-fixturevalue-3685-deterministic-fixturevalue-3686-deterministic-fixturevalue-3687-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3688-deterministic-fixturevalue-3690-deterministic-fixturevalue-3691-deterministic-fixturevalue-3692-deterministic-fixturevalue-3693-deterministic-fixturevalue-3694-deterministic-fixturevalue-3695-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3697-deterministic-fixturevalue-3698-deterministic-fixturevalue-3699-deterministic-fixturevalue-3700-deterministic-fixturevalue-3701-deterministic-fixturevalue-3702-deterministic-fixture @`value-3704-deterministic-fixturevalue-3705-deterministic-fixturevalue-3706-deterministic-fixturevalue-3707-deterministic-fixturevalue-3708-deterministic-fixturevalue-3709-deterministic-fixturevalue-3711-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3712-deterministic-fixturevalue-3713-deterministic-fixturevalue-3714-deterministic-fixturevalue-3715-deterministic-fixturevalue-3716-deterministic-fixturevalue-3718-deterministic-fixturevalue-3719-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3720-deterministic-fixturevalue-3721-deterministic-fixturevalue-3722-deterministic-fixturevalue-3723-deterministic-fixturevalue-3725-deterministic-fixturevalue-3726-deterministic-fixturevalue-3727-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3728-deterministic-fixturevalue-3729-deterministic-fixturevalue-3730-deterministic-fixturevalue-3732-deterministic-fixturevalue-3733-deterministic-fixturevalue-3734-deterministic-fixturevalue-3735-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3736-deterministic-fixturevalue-3737-deterministic-fixturevalue-3739-deterministic-fixturevalue-3740-deterministic-fixturevalue-3741-deterministic-fixturevalue-3742-deterministic-fixturevalue-3743-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3744-deterministic-fixturevalue-3746-deterministic-fixturevalue-3747-deterministic-fixturevalue-3748-deterministic-fixturevalue-3749-deterministic-fixturevalue-3750-deterministic-fixturevalue-3751-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3753-deterministic-fixturevalue-3754-deterministic-fixturevalue-3755-deterministic-fixturevalue-3756-deterministic-fixturevalue-3757-deterministic-fixturevalue-3758-deterministic-fixture @`value-3760-deterministic-fixturevalue-3761-deterministic-fixturevalue-3762-deterministic-fixturevalue-3763-deterministic-fixturevalue-3764-deterministic-fixturevalue-3765-deterministic-fixturevalue-3767-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3768-deterministic-fixturevalue-3769-deterministic-fixturevalue-3770-deterministic-fixturevalue-3771-deterministic-fixturevalue-3772-deterministic-fixturevalue-3774-deterministic-fixturevalue-3775-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3776-deterministic-fixturevalue-3777-deterministic-fixturevalue-3778-deterministic-fixturevalue-3779-deterministic-fixturevalue-3781-deterministic-fixturevalue-3782-deterministic-fixturevalue-3783-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3784-deterministic-fixturevalue-3785-deterministic-fixturevalue-3786-deterministic-fixturevalue-3788-deterministic-fixturevalue-3789-deterministic-fixturevalue-3790-deterministic-fixturevalue-3791-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3792-deterministic-fixturevalue-3793-deterministic-fixturevalue-3795-deterministic-fixturevalue-3796-deterministic-fixturevalue-3797-deterministic-fixturevalue-3798-deterministic-fixturevalue-3799-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3800-deterministic-fixturevalue-3802-deterministic-fixturevalue-3803-deterministic-fixturevalue-3804-deterministic-fixturevalue-3805-deterministic-fixturevalue-3806-deterministic-fixturevalue-3807-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3809-deterministic-fixturevalue-3810-deterministic-fixturevalue-3811-deterministic-fixturevalue-3812-deterministic-fixturevalue-3813-deterministic-fixturevalue-3814-deterministic-fixture @`value-3816-deterministic-fixturevalue-3817-deterministic-fixturevalue-3818-deterministic-fixturevalue-3819-deterministic-fixturevalue-3820-deterministic-fixturevalue-3821-deterministic-fixturevalue-3823-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3824-deterministic-fixturevalue-3825-deterministic-fixturevalue-3826-deterministic-fixturevalue-3827-deterministic-fixturevalue-3828-deterministic-fixturevalue-3830-deterministic-fixturevalue-3831-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3832-deterministic-fixturevalue-3833-deterministic-fixturevalue-3834-deterministic-fixturevalue-3835-deterministic-fixturevalue-3837-deterministic-fixturevalue-3838-deterministic-fixturevalue-3839-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3840-deterministic-fixturevalue-3841-deterministic-fixturevalue-3842-deterministic-fixturevalue-3844-deterministic-fixturevalue-3845-deterministic-fixturevalue-3846-deterministic-fixturevalue-3847-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3848-deterministic-fixturevalue-3849-deterministic-fixturevalue-3851-deterministic-fixturevalue-3852-deterministic-fixturevalue-3853-deterministic-fixturevalue-3854-deterministic-fixturevalue-3855-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3856-deterministic-fixturevalue-3858-deterministic-fixturevalue-3859-deterministic-fixturevalue-3860-deterministic-fixturevalue-3861-deterministic-fixturevalue-3862-deterministic-fixturevalue-3863-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3865-deterministic-fixturevalue-3866-deterministic-fixturevalue-3867-deterministic-fixturevalue-3868-deterministic-fixturevalue-3869-deterministic-fixturevalue-3870-deterministic-fixture @`value-3872-deterministic-fixturevalue-3873-deterministic-fixturevalue-3874-deterministic-fixturevalue-3875-deterministic-fixturevalue-3876-deterministic-fixturevalue-3877-deterministic-fixturevalue-3879-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3880-deterministic-fixturevalue-3881-deterministic-fixturevalue-3882-deterministic-fixturevalue-3883-deterministic-fixturevalue-3884-deterministic-fixturevalue-3886-deterministic-fixturevalue-3887-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3888-deterministic-fixturevalue-3889-deterministic-fixturevalue-3890-deterministic-fixturevalue-3891-deterministic-fixturevalue-3893-deterministic-fixturevalue-3894-deterministic-fixturevalue-3895-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3896-deterministic-fixturevalue-3897-deterministic-fixturevalue-3898-deterministic-fixturevalue-3900-deterministic-fixturevalue-3901-deterministic-fixturevalue-3902-deterministic-fixturevalue-3903-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3904-deterministic-fixturevalue-3905-deterministic-fixturevalue-3907-deterministic-fixturevalue-3908-deterministic-fixturevalue-3909-deterministic-fixturevalue-3910-deterministic-fixturevalue-3911-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3912-deterministic-fixturevalue-3914-deterministic-fixturevalue-3915-deterministic-fixturevalue-3916-deterministic-fixturevalue-3917-deterministic-fixturevalue-3918-deterministic-fixturevalue-3919-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3921-deterministic-fixturevalue-3922-deterministic-fixturevalue-3923-deterministic-fixturevalue-3924-deterministic-fixturevalue-3925-deterministic-fixturevalue-3926-deterministic-fixture @`value-3928-deterministic-fixturevalue-3929-deterministic-fixturevalue-3930-deterministic-fixturevalue-3931-deterministic-fixturevalue-3932-deterministic-fixturevalue-3933-deterministic-fixturevalue-3935-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3936-deterministic-fixturevalue-3937-deterministic-fixturevalue-3938-deterministic-fixturevalue-3939-deterministic-fixturevalue-3940-deterministic-fixturevalue-3942-deterministic-fixturevalue-3943-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-3944-deterministic-fixturevalue-3945-deterministic-fixturevalue-3946-deterministic-fixturevalue-3947-deterministic-fixturevalue-3949-deterministic-fixturevalue-3950-deterministic-fixturevalue-3951-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-3952-deterministic-fixturevalue-3953-deterministic-fixturevalue-3954-deterministic-fixturevalue-3956-deterministic-fixturevalue-3957-deterministic-fixturevalue-3958-deterministic-fixturevalue-3959-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-3960-deterministic-fixturevalue-3961-deterministic-fixturevalue-3963-deterministic-fixturevalue-3964-deterministic-fixturevalue-3965-deterministic-fixturevalue-3966-deterministic-fixturevalue-3967-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3968-deterministic-fixturevalue-3970-deterministic-fixturevalue-3971-deterministic-fixturevalue-3972-deterministic-fixturevalue-3973-deterministic-fixturevalue-3974-deterministic-fixturevalue-3975-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3977-deterministic-fixturevalue-3978-deterministic-fixturevalue-3979-deterministic-fixturevalue-3980-deterministic-fixturevalue-3981-deterministic-fixturevalue-3982-deterministic-fixture @`value-3984-deterministic-fixturevalue-3985-deterministic-fixturevalue-3986-deterministic-fixturevalue-3987-deterministic-fixturevalue-3988-deterministic-fixturevalue-3989-deterministic-fixturevalue-3991-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-3992-deterministic-fixturevalue-3993-deterministic-fixturevalue-3994-deterministic-fixturevalue-3995-deterministic-fixturevalue-3996-deterministic-fixturevalue-3998-deterministic-fixturevalue-3999-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-4000-deterministic-fixturevalue-4001-deterministic-fixturevalue-4002-deterministic-fixturevalue-4003-deterministic-fixturevalue-4005-deterministic-fixturevalue-4006-deterministic-fixturevalue-4007-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-4008-deterministic-fixturevalue-4009-deterministic-fixturevalue-4010-deterministic-fixturevalue-4012-deterministic-fixturevalue-4013-deterministic-fixturevalue-4014-deterministic-fixturevalue-4015-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-4016-deterministic-fixturevalue-4017-deterministic-fixturevalue-4019-deterministic-fixturevalue-4020-deterministic-fixturevalue-4021-deterministic-fixturevalue-4022-deterministic-fixturevalue-4023-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-4024-deterministic-fixturevalue-4026-deterministic-fixturevalue-4027-deterministic-fixturevalue-4028-deterministic-fixturevalue-4029-deterministic-fixturevalue-4030-deterministic-fixturevalue-4031-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-4033-deterministic-fixturevalue-4034-deterministic-fixturevalue-4035-deterministic-fixturevalue-4036-deterministic-fixturevalue-4037-deterministic-fixturevalue-4038-deterministic-fixture @`value-4040-deterministic-fixturevalue-4041-deterministic-fixturevalue-4042-deterministic-fixturevalue-4043-deterministic-fixturevalue-4044-deterministic-fixturevalue-4045-deterministic-fixturevalue-4047-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-4048-deterministic-fixturevalue-4049-deterministic-fixturevalue-4050-deterministic-fixturevalue-4051-deterministic-fixturevalue-4052-deterministic-fixturevalue-4054-deterministic-fixturevalue-4055-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`avalue-4056-deterministic-fixturevalue-4057-deterministic-fixturevalue-4058-deterministic-fixturevalue-4059-deterministic-fixturevalue-4061-deterministic-fixturevalue-4062-deterministic-fixturevalue-4063-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`Avalue-4064-deterministic-fixturevalue-4065-deterministic-fixturevalue-4066-deterministic-fixturevalue-4068-deterministic-fixturevalue-4069-deterministic-fixturevalue-4070-deterministic-fixturevalue-4071-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @!`value-4072-deterministic-fixturevalue-4073-deterministic-fixturevalue-4075-deterministic-fixturevalue-4076-deterministic-fixturevalue-4077-deterministic-fixturevalue-4078-deterministic-fixturevalue-4079-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-4080-deterministic-fixturevalue-4082-deterministic-fixturevalue-4083-deterministic-fixturevalue-4084-deterministic-fixturevalue-4085-deterministic-fixturevalue-4086-deterministic-fixturevalue-4087-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @`value-4089-deterministic-fixturevalue-4090-deterministic-fixturevalue-4091-deterministic-fixturevalue-4092-deterministic-fixturevalue-4093-deterministic-fixturevalue-4094-deterministic-fixture  !$'*-036 9<?BEHKNQT> WZ]`cfilor\ ux{~z   #& ),/258;>AD. GJMPSVY\_bL ehknqtwz}j      +  "%(+.14 7:=@CFILOR< UX[^adgjmpZ svy|x      !$'*-0369<?B,EHKNQTWZ]`Jcfilorux{~h  #&),/258;>ADGJMP:SVY\_behknXqtwz}v + " %(+.147:=@*CFILORUX[^Hadgjmpsvy|f  !$'*-0369<?BEHKN8QTWZ]`cfilVorux{~t   +#&),/258;>(ADGJMPSVY\F_behknqtwzd} + "%(+.147:=@CFIL6ORUX[^adgjTmpsvy|r         ! $ ' * - 0 3 6 9 < &? B E H K N Q T W Z D] ` c f i l o r u x b{ ~      + + + + + + + + + + +# +& +) +, +/ +2 +5 +8 +; +> +A +D +G +J +4M +P +S +V +Y +\ +_ +b +e +h +Rk +n +q +t +w +z +} + + + +p + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH $ $ $ $  $ +$ $ $ $ + $ !$ $$ '$ *$ HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH-$ 0$ "3$ 6$ (<$ *?$ ,B$ E$ 0H$ 2K$ 4N$ 6Q$ T$ :W$ >HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH]$ @`$! c$" Df$# Fi$$ Hl$% Jo$& r$' Nu$( Px$* T~$+ $, X$- Z$. \$HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH/ ^$0 $1 b$2 d$3 f$5 $6 l$7 n$8 p$9 r$: $; v$< x$= z$> HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH|$@ $A $B $C $D $E $F $G $H $I $K $L $M $N HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$O $P $Q $R $S $T $V %W %X %Y  %Z %[ %\ %] %HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^ %_ %a #%b &%c )%d ,%e /%f 2%g 5%h 8%i ;%j >%l D%m G%n HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJ%o M%p P%q S%r V%s Y%t \%u _%w e%x h%y k%z n%{ q%| t%} HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHw%~ z% }% % % % % % % % % % % % %HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH % % % "% $% &% % *% ,% 0% % 4% 6% 8% HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH:% % >% @% B% % H% J% L% N% % R% T% V& XHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH& \ +& ^ & `& b& & f& h& j& l"& %& r+& t.& v1& 4&HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH z7& |:& ~=& @& C& F& L& O& R& U& X& [& ^& a& HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHd& g& m& p& s& v& y& |& & & & & & & HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH& & & & & & & & & & & & & & &HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH & & & & & & & & & & & & & & HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH& & & & ' ' '  '  ' ' ' ' ' + ' HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!' $' '' *' -' 3' $6' &9' (<' *?' B' .E' 0H' 2K' 4N'HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH 8T' :W' <Z' >]' `'! Bc'" Df'# Fi'$ Hl'% o'' Nu'( Px') R{'* ~'+ HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHV', X'- Z'. \'/ '0 `'2 d'3 f'4 '5 j'6 l'7 n'8 p'9 ': tHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH'; v'= z'> '? ~'@ 'A 'B 'C 'D 'E 'F 'H 'I 'J 'HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHK 'L 'M 'N 'O 'P 'Q 'S 'T 'U 'V (W (X (Y  (Z HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH([ (\ (^ (_ (`  (a #(b &(c )(d ,(e /(f 2(g 5(i ;(j HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH>(k A(l D(m G(n J(o M(p P(q S(r V(t \(u _(v b(w e(x h(y k(HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHz n({ q(| t(} w( }( ( ( ( ( ( +( ( ( ( HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH( ( ( ( ( ( ( "( $( ( *( ,( .( ( 2HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH( 4( 6( 8( ( <( @( B( ( F( H( J( L( ( P(HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH R( V) ) Z) \ +) ^ ) `) ) d) f) h) ") n%) p() HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHr+) t.) 1) x4) z7) |:) ~=) C) F) I) L) O) R) U) HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHX) [) ^) d) g) j) m) p) s) v) y) |) ) ) )HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH ) ) ) ) ) ) ) ) ) ) ) ) ) ) HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH) ) ) ) ) ) ) ) ) ) ) ) ) ) HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH) ) ) ) ) ) ) )** * * +***HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH* * +* !* $***-*0*"3*$6*&9*(<*?*,B*HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.E*2K*N*6Q*8T*:W*<Z*]* @`*!Bc*"Df*$l*%Jo*&Lr*'NHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHu*(Px*){**T~*+V*,X*-Z*/^*0`*1b*2d*3*4h*5j*6l*HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH7n*8*:t*;v*<x*=*>|*?~*@*A*B*C*E*F*GHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH*H*I*J*K*L*M*N*P*Q*R*S*T*U*VHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH+W+X+Y +[+\+]+^+_+` +a#+b&+c)+d,+f2+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHg5+h8+i;+j>+kA+lD+mG+nJ+oM+qS+rV+sY+t\+u_+vHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHb+we+xh+yk+zn+|t+}w+~z+}++++++ +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH++++++++++ ++&+(+*+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,++0+2+4+6+:+<+>+@++D+F+H+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJ++P+R+T+,X,Z,\ +,^ ,,b,f,h,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,l",n%,p(,r+,.,v1,x4,|:,=,@,C,F,I,L,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHO,R,U,[,^,a,d,g,j,m,p,s,v,|,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,,,,,,,,,,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,,,,,,,,,,,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,,,,,,,,,,--- -HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH -- --- - !- $- '-*--- 0-"3-$6-&HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH9-<-,B-.E-0H-K-4N-6Q-8T-:W-Z->]-!Bc-"Df-#i-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$Hl-%Jo-&Lr-'Nu-(x-)R{-*T~-,X---.\-/^-0`-1b-2-3HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHf-4h-5j-7-8p-9r-:t-;v-<-=z->|-?~-@-B-CHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH-D-E-F-G-H-I-J-K-M-N-O-P-Q-R-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHS-T-U-V.X.Y .Z.[.\.].^._.` .a#.cHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH).d,.e/.f2.g5.h8.i;.j>.kA.lD.nJ.oM.pP.qS.rHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHV.sY.t\.u_.vb.we.yk.zn.{q.|t.}w.~z.}...HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.. +. ......... ..HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$.&.(.*....0.4..8.:.<.>..BHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.D.F..L.N.P.R..V/X/Z/\ +/`/b/HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHd/f//j/l"/n%/p(/+/v1/x4/z7/:/~=/@/HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHC/F/I/L/R/U/X/[/^/a/d/g/j/m/HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHs/v/y/|////////////HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH//////////////HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH//////////////HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@blob-4096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHvalue-4096-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH0HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHgreenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHo HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + +id *int328 +Cname *string08R" +lance-encoding:compressionnone +items *list08 +item *int3208 +ecategory *dict:string:int8:false08BR. +&lance-encoding:dict-values-compressionnone +Bblob * large_binary08R +lance-encoding:blobtrue +)' +% +/lance.encodings.ColumnEncoding +? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +0"20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + 0"20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +="20 +. +/lance.encodings.ArrayEncoding  + + + + ? +0"20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + ="20 +. +/lance.encodings.ArrayEncoding  + + + + ? + 0"20 +. +/lance.encodings.ArrayEncoding  + + + + > +%"20 +. +/lance.encodings.ArrayEncoding  + + + +  +)' +% +/lance.encodings.ColumnEncoding +\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ + @"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +!!@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +none\ +""@"KI +G +/lance.encodings.ArrayEncoding%2# +  + + + +@ + +noneZ +%% "JH +F +/lance.encodings.ArrayEncoding$2" +  + + + +@ + +none! +)' +% +/lance.encodings.ColumnEncoding +J +@"<: +8 +/lance.encodings.ArrayEncoding" +  + + + +@J +@"<: +8 +/lance.encodings.ArrayEncoding" +  + + + +@J +@"<: +8 +/lance.encodings.ArrayEncoding" +  + + + +@J +"@"<: +8 +/lance.encodings.ArrayEncoding" +  + + + +@F +%":8 +6 +/lance.encodings.ArrayEncoding" +  + + + +@ +)' +% +/lance.encodings.ColumnEncoding +M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M +&"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M +)"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M ++"<: +8 +/lance.encodings.ArrayEncoding + + + M +)"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +##+"<: +8 +/lance.encodings.ArrayEncoding + + + M +#$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$+"<: +8 +/lance.encodings.ArrayEncoding + + + M +$$)"<: +8 +/lance.encodings.ArrayEncoding + + + L +%%"<: +8 +/lance.encodings.ArrayEncoding + + + L +%%"<: +8 +/lance.encodings.ArrayEncoding + + + L +%%"<: +8 +/lance.encodings.ArrayEncoding + + +  +)' +% +/lance.encodings.ColumnEncoding +l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + : :"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + : :"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + : :"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$E E"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + $$$: :"XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ + l + %%% "XV +T +/lance.encodings.ArrayEncoding2:0 +  + + + +2 + + + + +@ +  +-+ +) +/lance.encodings.ColumnEncoding + +S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S + @"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S + @"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S + @"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S + @"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S + @"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S + @"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +$@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +$@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +$@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +$@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +$@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@S +%@"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@R +%"FD +B +/lance.encodings.ArrayEncoding J +  + + + +@ +  + + + +@t 6 B +`D +QQ +a +@s Gt +5 +LANC \ No newline at end of file diff --git a/rust/lance-file/test_data/exact_versions/v2_1.lance b/rust/lance-file/test_data/exact_versions/v2_1.lance new file mode 100644 index 00000000000..7ac8ae0c845 --- /dev/null +++ b/rust/lance-file/test_data/exact_versions/v2_1.lance @@ -0,0 +1,639 @@ +blob-0000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +P" 2BRPbr  $P +( , 0 4P8<@DQH"L2PBTRQXb\r`dQhlptQx|` +`` +*`:aJaZajazb"b &b +*b .c 2c 6c:c>dB +dFdJ*dN:eRJeVZeZje^zfbfffjfngrgvgzg~0@pABC 0DpEFG0H pI$J(K,0L0pM4N8O<1P@qQDRHSL1TPqUTVXW\1X`qYdZh[l1\pq]t^x_|p@TpA#pB3pCCqDSTqEcqFsqGrHTrIrJrKsLTsMsNsOtPUtQ#tR3tSCuTSUuUcuVsuWvXUvYvZv[w\Uw]w^w_ 4t ++;K4[tk{"4&t*.246t:>B 5FuJ+N;RK5V[uZk^{b5fujnr5vuz~ !R""#2$B%RR&b'r()R*+,-R./01S2"324B5RS6b7r89S:袓;<=S>?h +h!h"*h#:i$Ji%Zi&ji'zj(j)j*j+k,k-k.k/l0 +l1l2*l3:m4Jm5Zm6jm7zn8n9n:n;oo?2`rabc2drefg2hrijk2lrmno3psqijrs3tsuԳvw3xsyz{3|s}~x`Vxa#xb3xcCydSVyecyfsygzhVzizjzk{lV{m{n{o|pW|q#|r3|sC}tSW}uc}vs}w~xW~y~z~{|W}~ 6v+;K6[vk{6v6v 7w+;K7[wk{7w꫷7wHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHg g ` HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-0001-deterministic-fixturevalue-0002-deterministic-fixturevalue-0003-deterministic-fixturevalue-0004-deterministic-fixturevalue-0005-deterministic-fixturevalue-0006-deterministic-fixturevalue-0008-deterministic-fixturevalue-0009-deterministic-fixturevalue-0010-deterministic-fixturevalue-0011-deterministic-fixturevalue-0012-deterministic-fixturevalue-0013-deterministic-fixturevalue-0015-deterministic-fixturevalue-0016-deterministic-fixturevalue-0017-deterministic-fixturevalue-0018-deterministic-fixturevalue-0019-deterministic-fixturevalue-0020-deterministic-fixturevalue-0022-deterministic-fixturevalue-0023-deterministic-fixturevalue-0024-deterministic-fixturevalue-0025-deterministic-fixturevalue-0026-deterministic-fixturevalue-0027-deterministic-fixturevalue-0029-deterministic-fixturevalue-0030-deterministic-fixturevalue-0031-deterministic-fixturevalue-0032-deterministic-fixturevalue-0033-deterministic-fixturevalue-0034-deterministic-fixturevalue-0036-deterministic-fixturevalue-0037-deterministic-fixturevalue-0038-deterministic-fixturevalue-0039-deterministic-fixturevalue-0040-deterministic-fixturevalue-0041-deterministic-fixturevalue-0043-deterministic-fixturevalue-0044-deterministic-fixturevalue-0045-deterministic-fixturevalue-0046-deterministic-fixturevalue-0047-deterministic-fixturevalue-0048-deterministic-fixturevalue-0050-deterministic-fixturevalue-0051-deterministic-fixturevalue-0052-deterministic-fixturevalue-0053-deterministic-fixturevalue-0054-deterministic-fixturevalue-0055-deterministic-fixturevalue-0057-deterministic-fixturevalue-0058-deterministic-fixturevalue-0059-deterministic-fixturevalue-0060-deterministic-fixturevalue-0061-deterministic-fixturevalue-0062-deterministic-fixturevalue-0064-deterministic-fixturevalue-0065-deterministic-fixturevalue-0066-deterministic-fixturevalue-0067-deterministic-fixturevalue-0068-deterministic-fixturevalue-0069-deterministic-fixturevalue-0071-deterministic-fixturevalue-0072-deterministic-fixturevalue-0073-deterministic-fixturevalue-0074-deterministic-fixturevalue-0075-deterministic-fixturevalue-0076-deterministic-fixturevalue-0078-deterministic-fixturevalue-0079-deterministic-fixturevalue-0080-deterministic-fixturevalue-0081-deterministic-fixturevalue-0082-deterministic-fixturevalue-0083-deterministic-fixturevalue-0085-deterministic-fixturevalue-0086-deterministic-fixturevalue-0087-deterministic-fixturevalue-0088-deterministic-fixturevalue-0089-deterministic-fixturevalue-0090-deterministic-fixturevalue-0092-deterministic-fixturevalue-0093-deterministic-fixturevalue-0094-deterministic-fixturevalue-0095-deterministic-fixturevalue-0096-deterministic-fixturevalue-0097-deterministic-fixturevalue-0099-deterministic-fixturevalue-0100-deterministic-fixturevalue-0101-deterministic-fixturevalue-0102-deterministic-fixturevalue-0103-deterministic-fixturevalue-0104-deterministic-fixturevalue-0106-deterministic-fixturevalue-0107-deterministic-fixturevalue-0108-deterministic-fixturevalue-0109-deterministic-fixturevalue-0110-deterministic-fixturevalue-0111-deterministic-fixturevalue-0113-deterministic-fixturevalue-0114-deterministic-fixturevalue-0115-deterministic-fixturevalue-0116-deterministic-fixturevalue-0117-deterministic-fixturevalue-0118-deterministic-fixturevalue-0120-deterministic-fixturevalue-0121-deterministic-fixturevalue-0122-deterministic-fixturevalue-0123-deterministic-fixturevalue-0124-deterministic-fixturevalue-0125-deterministic-fixturevalue-0127-deterministic-fixture$Dd$Ddd$$Dd$Dd$Ddd$$Dd$Dd $ D d d  +$ +$ +D +d + + + + + + $ D d  $ D d d  $ $ D d $Dd$Dddvalue-0128-deterministic-fixturevalue-0129-deterministic-fixturevalue-0130-deterministic-fixturevalue-0131-deterministic-fixturevalue-0132-deterministic-fixturevalue-0134-deterministic-fixturevalue-0135-deterministic-fixturevalue-0136-deterministic-fixturevalue-0137-deterministic-fixturevalue-0138-deterministic-fixturevalue-0139-deterministic-fixturevalue-0141-deterministic-fixturevalue-0142-deterministic-fixturevalue-0143-deterministic-fixturevalue-0144-deterministic-fixturevalue-0145-deterministic-fixturevalue-0146-deterministic-fixturevalue-0148-deterministic-fixturevalue-0149-deterministic-fixturevalue-0150-deterministic-fixturevalue-0151-deterministic-fixturevalue-0152-deterministic-fixturevalue-0153-deterministic-fixturevalue-0155-deterministic-fixturevalue-0156-deterministic-fixturevalue-0157-deterministic-fixturevalue-0158-deterministic-fixturevalue-0159-deterministic-fixturevalue-0160-deterministic-fixturevalue-0162-deterministic-fixturevalue-0163-deterministic-fixturevalue-0164-deterministic-fixturevalue-0165-deterministic-fixturevalue-0166-deterministic-fixturevalue-0167-deterministic-fixturevalue-0169-deterministic-fixturevalue-0170-deterministic-fixturevalue-0171-deterministic-fixturevalue-0172-deterministic-fixturevalue-0173-deterministic-fixturevalue-0174-deterministic-fixturevalue-0176-deterministic-fixturevalue-0177-deterministic-fixturevalue-0178-deterministic-fixturevalue-0179-deterministic-fixturevalue-0180-deterministic-fixturevalue-0181-deterministic-fixturevalue-0183-deterministic-fixturevalue-0184-deterministic-fixturevalue-0185-deterministic-fixturevalue-0186-deterministic-fixturevalue-0187-deterministic-fixturevalue-0188-deterministic-fixturevalue-0190-deterministic-fixturevalue-0191-deterministic-fixturevalue-0192-deterministic-fixturevalue-0193-deterministic-fixturevalue-0194-deterministic-fixturevalue-0195-deterministic-fixturevalue-0197-deterministic-fixturevalue-0198-deterministic-fixturevalue-0199-deterministic-fixturevalue-0200-deterministic-fixturevalue-0201-deterministic-fixturevalue-0202-deterministic-fixturevalue-0204-deterministic-fixturevalue-0205-deterministic-fixturevalue-0206-deterministic-fixturevalue-0207-deterministic-fixturevalue-0208-deterministic-fixturevalue-0209-deterministic-fixturevalue-0211-deterministic-fixturevalue-0212-deterministic-fixturevalue-0213-deterministic-fixturevalue-0214-deterministic-fixturevalue-0215-deterministic-fixturevalue-0216-deterministic-fixturevalue-0218-deterministic-fixturevalue-0219-deterministic-fixturevalue-0220-deterministic-fixturevalue-0221-deterministic-fixturevalue-0222-deterministic-fixturevalue-0223-deterministic-fixturevalue-0225-deterministic-fixturevalue-0226-deterministic-fixturevalue-0227-deterministic-fixturevalue-0228-deterministic-fixturevalue-0229-deterministic-fixturevalue-0230-deterministic-fixturevalue-0232-deterministic-fixturevalue-0233-deterministic-fixturevalue-0234-deterministic-fixturevalue-0235-deterministic-fixturevalue-0236-deterministic-fixturevalue-0237-deterministic-fixturevalue-0239-deterministic-fixturevalue-0240-deterministic-fixturevalue-0241-deterministic-fixturevalue-0242-deterministic-fixturevalue-0243-deterministic-fixturevalue-0244-deterministic-fixturevalue-0246-deterministic-fixturevalue-0247-deterministic-fixturevalue-0248-deterministic-fixturevalue-0249-deterministic-fixturevalue-0250-deterministic-fixturevalue-0251-deterministic-fixturevalue-0253-deterministic-fixturevalue-0254-deterministic-fixturevalue-0255-deterministic-fixture$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-0256-deterministic-fixturevalue-0257-deterministic-fixturevalue-0258-deterministic-fixturevalue-0260-deterministic-fixturevalue-0261-deterministic-fixturevalue-0262-deterministic-fixturevalue-0263-deterministic-fixturevalue-0264-deterministic-fixturevalue-0265-deterministic-fixturevalue-0267-deterministic-fixturevalue-0268-deterministic-fixturevalue-0269-deterministic-fixturevalue-0270-deterministic-fixturevalue-0271-deterministic-fixturevalue-0272-deterministic-fixturevalue-0274-deterministic-fixturevalue-0275-deterministic-fixturevalue-0276-deterministic-fixturevalue-0277-deterministic-fixturevalue-0278-deterministic-fixturevalue-0279-deterministic-fixturevalue-0281-deterministic-fixturevalue-0282-deterministic-fixturevalue-0283-deterministic-fixturevalue-0284-deterministic-fixturevalue-0285-deterministic-fixturevalue-0286-deterministic-fixturevalue-0288-deterministic-fixturevalue-0289-deterministic-fixturevalue-0290-deterministic-fixturevalue-0291-deterministic-fixturevalue-0292-deterministic-fixturevalue-0293-deterministic-fixturevalue-0295-deterministic-fixturevalue-0296-deterministic-fixturevalue-0297-deterministic-fixturevalue-0298-deterministic-fixturevalue-0299-deterministic-fixturevalue-0300-deterministic-fixturevalue-0302-deterministic-fixturevalue-0303-deterministic-fixturevalue-0304-deterministic-fixturevalue-0305-deterministic-fixturevalue-0306-deterministic-fixturevalue-0307-deterministic-fixturevalue-0309-deterministic-fixturevalue-0310-deterministic-fixturevalue-0311-deterministic-fixturevalue-0312-deterministic-fixturevalue-0313-deterministic-fixturevalue-0314-deterministic-fixturevalue-0316-deterministic-fixturevalue-0317-deterministic-fixturevalue-0318-deterministic-fixturevalue-0319-deterministic-fixturevalue-0320-deterministic-fixturevalue-0321-deterministic-fixturevalue-0323-deterministic-fixturevalue-0324-deterministic-fixturevalue-0325-deterministic-fixturevalue-0326-deterministic-fixturevalue-0327-deterministic-fixturevalue-0328-deterministic-fixturevalue-0330-deterministic-fixturevalue-0331-deterministic-fixturevalue-0332-deterministic-fixturevalue-0333-deterministic-fixturevalue-0334-deterministic-fixturevalue-0335-deterministic-fixturevalue-0337-deterministic-fixturevalue-0338-deterministic-fixturevalue-0339-deterministic-fixturevalue-0340-deterministic-fixturevalue-0341-deterministic-fixturevalue-0342-deterministic-fixturevalue-0344-deterministic-fixturevalue-0345-deterministic-fixturevalue-0346-deterministic-fixturevalue-0347-deterministic-fixturevalue-0348-deterministic-fixturevalue-0349-deterministic-fixturevalue-0351-deterministic-fixturevalue-0352-deterministic-fixturevalue-0353-deterministic-fixturevalue-0354-deterministic-fixturevalue-0355-deterministic-fixturevalue-0356-deterministic-fixturevalue-0358-deterministic-fixturevalue-0359-deterministic-fixturevalue-0360-deterministic-fixturevalue-0361-deterministic-fixturevalue-0362-deterministic-fixturevalue-0363-deterministic-fixturevalue-0365-deterministic-fixturevalue-0366-deterministic-fixturevalue-0367-deterministic-fixturevalue-0368-deterministic-fixturevalue-0369-deterministic-fixturevalue-0370-deterministic-fixturevalue-0372-deterministic-fixturevalue-0373-deterministic-fixturevalue-0374-deterministic-fixturevalue-0375-deterministic-fixturevalue-0376-deterministic-fixturevalue-0377-deterministic-fixturevalue-0379-deterministic-fixturevalue-0380-deterministic-fixturevalue-0381-deterministic-fixturevalue-0382-deterministic-fixturevalue-0383-deterministic-fixture$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-0384-deterministic-fixturevalue-0386-deterministic-fixturevalue-0387-deterministic-fixturevalue-0388-deterministic-fixturevalue-0389-deterministic-fixturevalue-0390-deterministic-fixturevalue-0391-deterministic-fixturevalue-0393-deterministic-fixturevalue-0394-deterministic-fixturevalue-0395-deterministic-fixturevalue-0396-deterministic-fixturevalue-0397-deterministic-fixturevalue-0398-deterministic-fixturevalue-0400-deterministic-fixturevalue-0401-deterministic-fixturevalue-0402-deterministic-fixturevalue-0403-deterministic-fixturevalue-0404-deterministic-fixturevalue-0405-deterministic-fixturevalue-0407-deterministic-fixturevalue-0408-deterministic-fixturevalue-0409-deterministic-fixturevalue-0410-deterministic-fixturevalue-0411-deterministic-fixturevalue-0412-deterministic-fixturevalue-0414-deterministic-fixturevalue-0415-deterministic-fixturevalue-0416-deterministic-fixturevalue-0417-deterministic-fixturevalue-0418-deterministic-fixturevalue-0419-deterministic-fixturevalue-0421-deterministic-fixturevalue-0422-deterministic-fixturevalue-0423-deterministic-fixturevalue-0424-deterministic-fixturevalue-0425-deterministic-fixturevalue-0426-deterministic-fixturevalue-0428-deterministic-fixturevalue-0429-deterministic-fixturevalue-0430-deterministic-fixturevalue-0431-deterministic-fixturevalue-0432-deterministic-fixturevalue-0433-deterministic-fixturevalue-0435-deterministic-fixturevalue-0436-deterministic-fixturevalue-0437-deterministic-fixturevalue-0438-deterministic-fixturevalue-0439-deterministic-fixturevalue-0440-deterministic-fixturevalue-0442-deterministic-fixturevalue-0443-deterministic-fixturevalue-0444-deterministic-fixturevalue-0445-deterministic-fixturevalue-0446-deterministic-fixturevalue-0447-deterministic-fixturevalue-0449-deterministic-fixturevalue-0450-deterministic-fixturevalue-0451-deterministic-fixturevalue-0452-deterministic-fixturevalue-0453-deterministic-fixturevalue-0454-deterministic-fixturevalue-0456-deterministic-fixturevalue-0457-deterministic-fixturevalue-0458-deterministic-fixturevalue-0459-deterministic-fixturevalue-0460-deterministic-fixturevalue-0461-deterministic-fixturevalue-0463-deterministic-fixturevalue-0464-deterministic-fixturevalue-0465-deterministic-fixturevalue-0466-deterministic-fixturevalue-0467-deterministic-fixturevalue-0468-deterministic-fixturevalue-0470-deterministic-fixturevalue-0471-deterministic-fixturevalue-0472-deterministic-fixturevalue-0473-deterministic-fixturevalue-0474-deterministic-fixturevalue-0475-deterministic-fixturevalue-0477-deterministic-fixturevalue-0478-deterministic-fixturevalue-0479-deterministic-fixturevalue-0480-deterministic-fixturevalue-0481-deterministic-fixturevalue-0482-deterministic-fixturevalue-0484-deterministic-fixturevalue-0485-deterministic-fixturevalue-0486-deterministic-fixturevalue-0487-deterministic-fixturevalue-0488-deterministic-fixturevalue-0489-deterministic-fixturevalue-0491-deterministic-fixturevalue-0492-deterministic-fixturevalue-0493-deterministic-fixturevalue-0494-deterministic-fixturevalue-0495-deterministic-fixturevalue-0496-deterministic-fixturevalue-0498-deterministic-fixturevalue-0499-deterministic-fixturevalue-0500-deterministic-fixturevalue-0501-deterministic-fixturevalue-0502-deterministic-fixturevalue-0503-deterministic-fixturevalue-0505-deterministic-fixturevalue-0506-deterministic-fixturevalue-0507-deterministic-fixturevalue-0508-deterministic-fixturevalue-0509-deterministic-fixturevalue-0510-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-0512-deterministic-fixturevalue-0513-deterministic-fixturevalue-0514-deterministic-fixturevalue-0515-deterministic-fixturevalue-0516-deterministic-fixturevalue-0517-deterministic-fixturevalue-0519-deterministic-fixturevalue-0520-deterministic-fixturevalue-0521-deterministic-fixturevalue-0522-deterministic-fixturevalue-0523-deterministic-fixturevalue-0524-deterministic-fixturevalue-0526-deterministic-fixturevalue-0527-deterministic-fixturevalue-0528-deterministic-fixturevalue-0529-deterministic-fixturevalue-0530-deterministic-fixturevalue-0531-deterministic-fixturevalue-0533-deterministic-fixturevalue-0534-deterministic-fixturevalue-0535-deterministic-fixturevalue-0536-deterministic-fixturevalue-0537-deterministic-fixturevalue-0538-deterministic-fixturevalue-0540-deterministic-fixturevalue-0541-deterministic-fixturevalue-0542-deterministic-fixturevalue-0543-deterministic-fixturevalue-0544-deterministic-fixturevalue-0545-deterministic-fixturevalue-0547-deterministic-fixturevalue-0548-deterministic-fixturevalue-0549-deterministic-fixturevalue-0550-deterministic-fixturevalue-0551-deterministic-fixturevalue-0552-deterministic-fixturevalue-0554-deterministic-fixturevalue-0555-deterministic-fixturevalue-0556-deterministic-fixturevalue-0557-deterministic-fixturevalue-0558-deterministic-fixturevalue-0559-deterministic-fixturevalue-0561-deterministic-fixturevalue-0562-deterministic-fixturevalue-0563-deterministic-fixturevalue-0564-deterministic-fixturevalue-0565-deterministic-fixturevalue-0566-deterministic-fixturevalue-0568-deterministic-fixturevalue-0569-deterministic-fixturevalue-0570-deterministic-fixturevalue-0571-deterministic-fixturevalue-0572-deterministic-fixturevalue-0573-deterministic-fixturevalue-0575-deterministic-fixturevalue-0576-deterministic-fixturevalue-0577-deterministic-fixturevalue-0578-deterministic-fixturevalue-0579-deterministic-fixturevalue-0580-deterministic-fixturevalue-0582-deterministic-fixturevalue-0583-deterministic-fixturevalue-0584-deterministic-fixturevalue-0585-deterministic-fixturevalue-0586-deterministic-fixturevalue-0587-deterministic-fixturevalue-0589-deterministic-fixturevalue-0590-deterministic-fixturevalue-0591-deterministic-fixturevalue-0592-deterministic-fixturevalue-0593-deterministic-fixturevalue-0594-deterministic-fixturevalue-0596-deterministic-fixturevalue-0597-deterministic-fixturevalue-0598-deterministic-fixturevalue-0599-deterministic-fixturevalue-0600-deterministic-fixturevalue-0601-deterministic-fixturevalue-0603-deterministic-fixturevalue-0604-deterministic-fixturevalue-0605-deterministic-fixturevalue-0606-deterministic-fixturevalue-0607-deterministic-fixturevalue-0608-deterministic-fixturevalue-0610-deterministic-fixturevalue-0611-deterministic-fixturevalue-0612-deterministic-fixturevalue-0613-deterministic-fixturevalue-0614-deterministic-fixturevalue-0615-deterministic-fixturevalue-0617-deterministic-fixturevalue-0618-deterministic-fixturevalue-0619-deterministic-fixturevalue-0620-deterministic-fixturevalue-0621-deterministic-fixturevalue-0622-deterministic-fixturevalue-0624-deterministic-fixturevalue-0625-deterministic-fixturevalue-0626-deterministic-fixturevalue-0627-deterministic-fixturevalue-0628-deterministic-fixturevalue-0629-deterministic-fixturevalue-0631-deterministic-fixturevalue-0632-deterministic-fixturevalue-0633-deterministic-fixturevalue-0634-deterministic-fixturevalue-0635-deterministic-fixturevalue-0636-deterministic-fixturevalue-0638-deterministic-fixturevalue-0639-deterministic-fixture$Dd$DDd$Dd$Dd$DDd$Dd$Dd $ D D d  + +$ +D +d + + + + + + $ D d  $ D D d   $ D d $Dd$DDdvalue-0640-deterministic-fixturevalue-0641-deterministic-fixturevalue-0642-deterministic-fixturevalue-0643-deterministic-fixturevalue-0645-deterministic-fixturevalue-0646-deterministic-fixturevalue-0647-deterministic-fixturevalue-0648-deterministic-fixturevalue-0649-deterministic-fixturevalue-0650-deterministic-fixturevalue-0652-deterministic-fixturevalue-0653-deterministic-fixturevalue-0654-deterministic-fixturevalue-0655-deterministic-fixturevalue-0656-deterministic-fixturevalue-0657-deterministic-fixturevalue-0659-deterministic-fixturevalue-0660-deterministic-fixturevalue-0661-deterministic-fixturevalue-0662-deterministic-fixturevalue-0663-deterministic-fixturevalue-0664-deterministic-fixturevalue-0666-deterministic-fixturevalue-0667-deterministic-fixturevalue-0668-deterministic-fixturevalue-0669-deterministic-fixturevalue-0670-deterministic-fixturevalue-0671-deterministic-fixturevalue-0673-deterministic-fixturevalue-0674-deterministic-fixturevalue-0675-deterministic-fixturevalue-0676-deterministic-fixturevalue-0677-deterministic-fixturevalue-0678-deterministic-fixturevalue-0680-deterministic-fixturevalue-0681-deterministic-fixturevalue-0682-deterministic-fixturevalue-0683-deterministic-fixturevalue-0684-deterministic-fixturevalue-0685-deterministic-fixturevalue-0687-deterministic-fixturevalue-0688-deterministic-fixturevalue-0689-deterministic-fixturevalue-0690-deterministic-fixturevalue-0691-deterministic-fixturevalue-0692-deterministic-fixturevalue-0694-deterministic-fixturevalue-0695-deterministic-fixturevalue-0696-deterministic-fixturevalue-0697-deterministic-fixturevalue-0698-deterministic-fixturevalue-0699-deterministic-fixturevalue-0701-deterministic-fixturevalue-0702-deterministic-fixturevalue-0703-deterministic-fixturevalue-0704-deterministic-fixturevalue-0705-deterministic-fixturevalue-0706-deterministic-fixturevalue-0708-deterministic-fixturevalue-0709-deterministic-fixturevalue-0710-deterministic-fixturevalue-0711-deterministic-fixturevalue-0712-deterministic-fixturevalue-0713-deterministic-fixturevalue-0715-deterministic-fixturevalue-0716-deterministic-fixturevalue-0717-deterministic-fixturevalue-0718-deterministic-fixturevalue-0719-deterministic-fixturevalue-0720-deterministic-fixturevalue-0722-deterministic-fixturevalue-0723-deterministic-fixturevalue-0724-deterministic-fixturevalue-0725-deterministic-fixturevalue-0726-deterministic-fixturevalue-0727-deterministic-fixturevalue-0729-deterministic-fixturevalue-0730-deterministic-fixturevalue-0731-deterministic-fixturevalue-0732-deterministic-fixturevalue-0733-deterministic-fixturevalue-0734-deterministic-fixturevalue-0736-deterministic-fixturevalue-0737-deterministic-fixturevalue-0738-deterministic-fixturevalue-0739-deterministic-fixturevalue-0740-deterministic-fixturevalue-0741-deterministic-fixturevalue-0743-deterministic-fixturevalue-0744-deterministic-fixturevalue-0745-deterministic-fixturevalue-0746-deterministic-fixturevalue-0747-deterministic-fixturevalue-0748-deterministic-fixturevalue-0750-deterministic-fixturevalue-0751-deterministic-fixturevalue-0752-deterministic-fixturevalue-0753-deterministic-fixturevalue-0754-deterministic-fixturevalue-0755-deterministic-fixturevalue-0757-deterministic-fixturevalue-0758-deterministic-fixturevalue-0759-deterministic-fixturevalue-0760-deterministic-fixturevalue-0761-deterministic-fixturevalue-0762-deterministic-fixturevalue-0764-deterministic-fixturevalue-0765-deterministic-fixturevalue-0766-deterministic-fixturevalue-0767-deterministic-fixture$DDd$Dd$Dd$DDd$Dd$Dd$DDd  $ D d  +$ +D +d + + + + + + $ D D d   $ D d  $ D d $DDd$Ddvalue-0768-deterministic-fixturevalue-0769-deterministic-fixturevalue-0771-deterministic-fixturevalue-0772-deterministic-fixturevalue-0773-deterministic-fixturevalue-0774-deterministic-fixturevalue-0775-deterministic-fixturevalue-0776-deterministic-fixturevalue-0778-deterministic-fixturevalue-0779-deterministic-fixturevalue-0780-deterministic-fixturevalue-0781-deterministic-fixturevalue-0782-deterministic-fixturevalue-0783-deterministic-fixturevalue-0785-deterministic-fixturevalue-0786-deterministic-fixturevalue-0787-deterministic-fixturevalue-0788-deterministic-fixturevalue-0789-deterministic-fixturevalue-0790-deterministic-fixturevalue-0792-deterministic-fixturevalue-0793-deterministic-fixturevalue-0794-deterministic-fixturevalue-0795-deterministic-fixturevalue-0796-deterministic-fixturevalue-0797-deterministic-fixturevalue-0799-deterministic-fixturevalue-0800-deterministic-fixturevalue-0801-deterministic-fixturevalue-0802-deterministic-fixturevalue-0803-deterministic-fixturevalue-0804-deterministic-fixturevalue-0806-deterministic-fixturevalue-0807-deterministic-fixturevalue-0808-deterministic-fixturevalue-0809-deterministic-fixturevalue-0810-deterministic-fixturevalue-0811-deterministic-fixturevalue-0813-deterministic-fixturevalue-0814-deterministic-fixturevalue-0815-deterministic-fixturevalue-0816-deterministic-fixturevalue-0817-deterministic-fixturevalue-0818-deterministic-fixturevalue-0820-deterministic-fixturevalue-0821-deterministic-fixturevalue-0822-deterministic-fixturevalue-0823-deterministic-fixturevalue-0824-deterministic-fixturevalue-0825-deterministic-fixturevalue-0827-deterministic-fixturevalue-0828-deterministic-fixturevalue-0829-deterministic-fixturevalue-0830-deterministic-fixturevalue-0831-deterministic-fixturevalue-0832-deterministic-fixturevalue-0834-deterministic-fixturevalue-0835-deterministic-fixturevalue-0836-deterministic-fixturevalue-0837-deterministic-fixturevalue-0838-deterministic-fixturevalue-0839-deterministic-fixturevalue-0841-deterministic-fixturevalue-0842-deterministic-fixturevalue-0843-deterministic-fixturevalue-0844-deterministic-fixturevalue-0845-deterministic-fixturevalue-0846-deterministic-fixturevalue-0848-deterministic-fixturevalue-0849-deterministic-fixturevalue-0850-deterministic-fixturevalue-0851-deterministic-fixturevalue-0852-deterministic-fixturevalue-0853-deterministic-fixturevalue-0855-deterministic-fixturevalue-0856-deterministic-fixturevalue-0857-deterministic-fixturevalue-0858-deterministic-fixturevalue-0859-deterministic-fixturevalue-0860-deterministic-fixturevalue-0862-deterministic-fixturevalue-0863-deterministic-fixturevalue-0864-deterministic-fixturevalue-0865-deterministic-fixturevalue-0866-deterministic-fixturevalue-0867-deterministic-fixturevalue-0869-deterministic-fixturevalue-0870-deterministic-fixturevalue-0871-deterministic-fixturevalue-0872-deterministic-fixturevalue-0873-deterministic-fixturevalue-0874-deterministic-fixturevalue-0876-deterministic-fixturevalue-0877-deterministic-fixturevalue-0878-deterministic-fixturevalue-0879-deterministic-fixturevalue-0880-deterministic-fixturevalue-0881-deterministic-fixturevalue-0883-deterministic-fixturevalue-0884-deterministic-fixturevalue-0885-deterministic-fixturevalue-0886-deterministic-fixturevalue-0887-deterministic-fixturevalue-0888-deterministic-fixturevalue-0890-deterministic-fixturevalue-0891-deterministic-fixturevalue-0892-deterministic-fixturevalue-0893-deterministic-fixturevalue-0894-deterministic-fixturevalue-0895-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-0897-deterministic-fixturevalue-0898-deterministic-fixturevalue-0899-deterministic-fixturevalue-0900-deterministic-fixturevalue-0901-deterministic-fixturevalue-0902-deterministic-fixturevalue-0904-deterministic-fixturevalue-0905-deterministic-fixturevalue-0906-deterministic-fixturevalue-0907-deterministic-fixturevalue-0908-deterministic-fixturevalue-0909-deterministic-fixturevalue-0911-deterministic-fixturevalue-0912-deterministic-fixturevalue-0913-deterministic-fixturevalue-0914-deterministic-fixturevalue-0915-deterministic-fixturevalue-0916-deterministic-fixturevalue-0918-deterministic-fixturevalue-0919-deterministic-fixturevalue-0920-deterministic-fixturevalue-0921-deterministic-fixturevalue-0922-deterministic-fixturevalue-0923-deterministic-fixturevalue-0925-deterministic-fixturevalue-0926-deterministic-fixturevalue-0927-deterministic-fixturevalue-0928-deterministic-fixturevalue-0929-deterministic-fixturevalue-0930-deterministic-fixturevalue-0932-deterministic-fixturevalue-0933-deterministic-fixturevalue-0934-deterministic-fixturevalue-0935-deterministic-fixturevalue-0936-deterministic-fixturevalue-0937-deterministic-fixturevalue-0939-deterministic-fixturevalue-0940-deterministic-fixturevalue-0941-deterministic-fixturevalue-0942-deterministic-fixturevalue-0943-deterministic-fixturevalue-0944-deterministic-fixturevalue-0946-deterministic-fixturevalue-0947-deterministic-fixturevalue-0948-deterministic-fixturevalue-0949-deterministic-fixturevalue-0950-deterministic-fixturevalue-0951-deterministic-fixturevalue-0953-deterministic-fixturevalue-0954-deterministic-fixturevalue-0955-deterministic-fixturevalue-0956-deterministic-fixturevalue-0957-deterministic-fixturevalue-0958-deterministic-fixturevalue-0960-deterministic-fixturevalue-0961-deterministic-fixturevalue-0962-deterministic-fixturevalue-0963-deterministic-fixturevalue-0964-deterministic-fixturevalue-0965-deterministic-fixturevalue-0967-deterministic-fixturevalue-0968-deterministic-fixturevalue-0969-deterministic-fixturevalue-0970-deterministic-fixturevalue-0971-deterministic-fixturevalue-0972-deterministic-fixturevalue-0974-deterministic-fixturevalue-0975-deterministic-fixturevalue-0976-deterministic-fixturevalue-0977-deterministic-fixturevalue-0978-deterministic-fixturevalue-0979-deterministic-fixturevalue-0981-deterministic-fixturevalue-0982-deterministic-fixturevalue-0983-deterministic-fixturevalue-0984-deterministic-fixturevalue-0985-deterministic-fixturevalue-0986-deterministic-fixturevalue-0988-deterministic-fixturevalue-0989-deterministic-fixturevalue-0990-deterministic-fixturevalue-0991-deterministic-fixturevalue-0992-deterministic-fixturevalue-0993-deterministic-fixturevalue-0995-deterministic-fixturevalue-0996-deterministic-fixturevalue-0997-deterministic-fixturevalue-0998-deterministic-fixturevalue-0999-deterministic-fixturevalue-1000-deterministic-fixturevalue-1002-deterministic-fixturevalue-1003-deterministic-fixturevalue-1004-deterministic-fixturevalue-1005-deterministic-fixturevalue-1006-deterministic-fixturevalue-1007-deterministic-fixturevalue-1009-deterministic-fixturevalue-1010-deterministic-fixturevalue-1011-deterministic-fixturevalue-1012-deterministic-fixturevalue-1013-deterministic-fixturevalue-1014-deterministic-fixturevalue-1016-deterministic-fixturevalue-1017-deterministic-fixturevalue-1018-deterministic-fixturevalue-1019-deterministic-fixturevalue-1020-deterministic-fixturevalue-1021-deterministic-fixturevalue-1023-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH::HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#F$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I@ @@ @!@@@ @@ @!@@ H@ @@ h/FAG0H @0H 01I@ KPC2K`3L @3AM +X4NC pNA#4ҋ`Nvq#8 bTv #@c`w"# efw$$Hfl#x&1$L" hrCx(a$P2ixy*$TBl~z,$P nz.$\ro{0Q%` qh{0()H8MRld{h)KDRp$|4)NP St|6)Q\MS||8)TtSD}:)W } *Z T~> *]MT d~@I*`T"~D!i*fT#$)*i8Wpب;L@X;NHBYPPZ=RX[=T`B\ >Vp]DPXx]h?Z^?\B_ @^` E B L'RX'b Úd!(r|Q(ق (ݐ#(ࢍD( + d) ҍѤq) ěġ)  ,$NX dI,'XĄi,*$,-YЄ ,0NY",3YD$,6Y& -90H )-<@NZ`d*(@-?PZxć.0-E[52Od#56APC68QpCc6:Qt7qT|@Æ8@T9BтUDBV":FaW2@c:H{ %r1|ȍ%D t|%uA } & wQ)} &dxYI~& z`&$ }i&~q(N' yɀ4'D@'Ť$H*l0U%J*o@U&LB+r@V'N+u`(+xpV)(RB+{V*XT+~@W+pV+W-+.\B+W/^@b)` &d*a ,Af1*b"!2DBha*c0!8dCj*dB!>l*eR"DDn*fb"JDp!+f"PEr+g"V$Et+h#\Dx+i􄈕-䈖-[E-[ +. \ *.O\ eJ.$Ŋ!j.0\%.<]1.HO]勤9.T!A +/"D$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$d @@ @H@ @@ @!@@@ @@ @!@@  rh4yQOk5zOn{OqD5|Otd5}Pw5~APzP}5ހP6߂Q$61Q#D6@4b`h4` f@5!a@s @6QalT 7and %A8"apt EA9Rar &eA:b FA;Abv f=qbz A>Bb| A?rbr",&L2r$%,&GXLbr&5,&gXLr*E,&XL„s,U,&§Xs.e,&M"s0u,&XMRs,&YMt4,&'Yt6,&GYMt8,&ԇY7퐡Sǰ7S"ʳ8T$ж$81T&#ӹaT(Cּ8TPٿ8T,c8!U0sQU29U$9UAe!FCLqe!CMe!CNBe"DOrf"P1f#"EDQaf0"&eDS2f C"FDTbf4S"fDUfDs"DVQgT"'[OvR-''[wV-'OwX-'h[Ow-'[OBw\-( +[rx^-( [Px`-(P҉xb.(\Px .(H\Pbxf5.(h\aQ3⅑Q +C6QP6Qc6!Rs6QR$7花RD7Rd7S7ASqS!%B@1c#!Aac3!eBBc@!BC2c$S!FBDbc@c!̆BE!dTs!ϦFQdd!CH"dt!%CIRd!ECJd!&KMBt:,&קYMrt,'YNu@,'Y҆uB-'NuD-''ZN2u -'GZNbuH5-'gZvJE-'ZN‡vLU-'OvNu-'ZORv-6D9U8d9V:AV<ڤ9qV9V@:WB1WF#D:aW0d:WJC:WLS :EWgd"%EX"gp"EEYRg" Zh"E[Ah"gE\qh"E^Bh"E_rh#`i$# FFabi0##fFbixhE.(PŠylU.("\Py`.(%\Q"ypu.((\Ryr.(+]Qzt.(.Qzv.(1H]Qz.(4h]Qzz.(7]rz|.):R{~.)=]$ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ @ @@ H@ @@@ @ cM1cNҕ2P2(dQ2 2HdRbE2܈dS–U2Te2dU"p2dVR2eW2(eXCV7Kos7koZ7 Cӧ\8 + ps^8 )p3`&8 Ipc68+ip3f'F8K cèh7V8 pjGp8pSP8"={={={={={>|>|>|>>|>|`3,ff#v32fg S35 gh +38Igi 3;!igj 3>$gkC3A*gls3D- m 3G0gnӛ4J3 ho$4MF9F+r3ë'P9I rc7f9Lkrœ#Pv9OrSg9R +#w9U *sS9[ Jsʃ9^+js˳9aK +s9dks9g sC~??~?~?~??????2Y◲2 he[2e\B2e]r2^Ә3 f_3)f`3&3 Ifac63# ifb +F3& cÙ V3)fd +qng8% +#pw8(JqSr8+ jqt8.KqC81kqsx84 +C|8: qsӪ~9= +r&9@Jrc69C +>|>|>>}>}>}>>}>}?~?6)hq3&&4S9Ihrc(64V< s*V4Y?ht`4\Bhu#.v4_EhvS24bK w44eN)ix4hQIiy84kTiiz:4nWi|Cӭ:j +ts:m*tУ3&:p +c6:s jt@:v tîV:| tf: t#: +u: *u: Ju: ??HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHxwHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@ @@ @@ @@ @@ aaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH $@ d@@@$$A,dA4A?%HA eHCHE+HG;%IIKeIK[IMkIO{%JQeJSJUJW%KYeK[ۥK]K_%La eLcLe+Lg;%MiKeMk[MmkMo{%NqeNsNuNw%OyeO{ۥO}ObPPQ6Q6$mD64mɣFR6DmHS6S6+GPre+/@Gr+kGr%,/Gs,˹GsE-/Gs-Gt.+GPte.KH. +k Ht %/0Hu `e: +#0(ve< S0)e$= 0*e*? 0%+e6@I0)e:pWz@pW{A qW'{C cqW7{Dk:“qWG{F:qXW{Iqe//m"/q(/y4/:/@/F/L/R/ G .U +gN ǵn! ''*U-G 0"3X'\K*;cX0^k2ʓXG_XWaBXgb J;#Xd R̃Xe+Z;ͳYgKYj jYǸkr;C(Yt|/t|/t|/t|/t|/u|/u|/u|/u|/v|/v|/-TfMm&F.Ug@ n ' +XaJ XiLҺ8XlM:DXqOPXuP\XyRhX|S:tX́UX̅V +X̉YX͍[g{X/#rw{^/ Sr{d/ r{j/+r{v/Kr{|/ks{/ss{/s{/s|/ t|/ 36&9(<*?,B0H2K4N6QT @YUŝgNLYǸXY̝;dYU繢pY՝G;|YYݝ.Yg;NYǻʻ`Y'msv|nУv|pv}q w}s+3w0}tkғwG}vwW}w wg}z#xp}|Sx}} Ճx///HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHwwHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @@ @@ @@ @@ @@aaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaaHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH (8HXhx + + Ȉ ؈ (8HXhxȉ؉ +)*9:IJYZijyz + ɘ ٘   +)*9:IJYZijyzəٙ +;K[k{ + ˸ ۸븨 +;K[k{˹۹빩@@AAB(BC8CDHDEXEFhFGxGHHIIJJKKLȌLM،MNNOOPPQQR(RS8STHTUXUVhVWxWXXYYZZ[[\ȍ\]؍]^^__ @ +A)B*9C:IDJYEZiFjyGzHIJKɜLٜMNO P +Q)R*9S:ITJYUZiVjyWzXYZ[ɝ\ٝ]^_@ AB+C;DKE[FkG{HIJKL˼MۼN뼬OP QR+S;TKU[VkW{XYZ[\˽]۽^뽭_  !!"("#8#$H$%X%&h&'x'(())**++,Ȋ,-؊-..//00112(23834H45X56h67x78899::;;<ȋ<=؋=>>?? +!)"*9#:I$JY%Zi&jy'z()*+ɚ,ٚ-./ 0 +1)2*93:I4JY5Zi6jy7z89:;ɛ<ٛ=>? !"+#;$K%[&k'{()*+,˺-ۺ.뺪/0 12+3;4K5[6k7{89:;<˻=ۻ>뻫?``aab(bc8cdHdeXefhfgxghhiijjkklȎlm؎mnnooppqqr(rs8stHtuXuvhvwxwxxyyzz{{|ȏ|}؏}~~ ` +a)b*9c:IdJYeZifjygzhijkɞlٞmno p +q)r*9s:ItJYuZivjywzxyz{ɟ|ٟ}~` ab+c;dKe[fkg{hijkl˾m۾n뾮op qr+s;tKu[vkw{xyz{|˿}ۿ~뿯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH g g HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-2048-deterministic-fixturevalue-2049-deterministic-fixturevalue-2050-deterministic-fixturevalue-2052-deterministic-fixturevalue-2053-deterministic-fixturevalue-2054-deterministic-fixturevalue-2055-deterministic-fixturevalue-2056-deterministic-fixturevalue-2057-deterministic-fixturevalue-2059-deterministic-fixturevalue-2060-deterministic-fixturevalue-2061-deterministic-fixturevalue-2062-deterministic-fixturevalue-2063-deterministic-fixturevalue-2064-deterministic-fixturevalue-2066-deterministic-fixturevalue-2067-deterministic-fixturevalue-2068-deterministic-fixturevalue-2069-deterministic-fixturevalue-2070-deterministic-fixturevalue-2071-deterministic-fixturevalue-2073-deterministic-fixturevalue-2074-deterministic-fixturevalue-2075-deterministic-fixturevalue-2076-deterministic-fixturevalue-2077-deterministic-fixturevalue-2078-deterministic-fixturevalue-2080-deterministic-fixturevalue-2081-deterministic-fixturevalue-2082-deterministic-fixturevalue-2083-deterministic-fixturevalue-2084-deterministic-fixturevalue-2085-deterministic-fixturevalue-2087-deterministic-fixturevalue-2088-deterministic-fixturevalue-2089-deterministic-fixturevalue-2090-deterministic-fixturevalue-2091-deterministic-fixturevalue-2092-deterministic-fixturevalue-2094-deterministic-fixturevalue-2095-deterministic-fixturevalue-2096-deterministic-fixturevalue-2097-deterministic-fixturevalue-2098-deterministic-fixturevalue-2099-deterministic-fixturevalue-2101-deterministic-fixturevalue-2102-deterministic-fixturevalue-2103-deterministic-fixturevalue-2104-deterministic-fixturevalue-2105-deterministic-fixturevalue-2106-deterministic-fixturevalue-2108-deterministic-fixturevalue-2109-deterministic-fixturevalue-2110-deterministic-fixturevalue-2111-deterministic-fixturevalue-2112-deterministic-fixturevalue-2113-deterministic-fixturevalue-2115-deterministic-fixturevalue-2116-deterministic-fixturevalue-2117-deterministic-fixturevalue-2118-deterministic-fixturevalue-2119-deterministic-fixturevalue-2120-deterministic-fixturevalue-2122-deterministic-fixturevalue-2123-deterministic-fixturevalue-2124-deterministic-fixturevalue-2125-deterministic-fixturevalue-2126-deterministic-fixturevalue-2127-deterministic-fixturevalue-2129-deterministic-fixturevalue-2130-deterministic-fixturevalue-2131-deterministic-fixturevalue-2132-deterministic-fixturevalue-2133-deterministic-fixturevalue-2134-deterministic-fixturevalue-2136-deterministic-fixturevalue-2137-deterministic-fixturevalue-2138-deterministic-fixturevalue-2139-deterministic-fixturevalue-2140-deterministic-fixturevalue-2141-deterministic-fixturevalue-2143-deterministic-fixturevalue-2144-deterministic-fixturevalue-2145-deterministic-fixturevalue-2146-deterministic-fixturevalue-2147-deterministic-fixturevalue-2148-deterministic-fixturevalue-2150-deterministic-fixturevalue-2151-deterministic-fixturevalue-2152-deterministic-fixturevalue-2153-deterministic-fixturevalue-2154-deterministic-fixturevalue-2155-deterministic-fixturevalue-2157-deterministic-fixturevalue-2158-deterministic-fixturevalue-2159-deterministic-fixturevalue-2160-deterministic-fixturevalue-2161-deterministic-fixturevalue-2162-deterministic-fixturevalue-2164-deterministic-fixturevalue-2165-deterministic-fixturevalue-2166-deterministic-fixturevalue-2167-deterministic-fixturevalue-2168-deterministic-fixturevalue-2169-deterministic-fixturevalue-2171-deterministic-fixturevalue-2172-deterministic-fixturevalue-2173-deterministic-fixturevalue-2174-deterministic-fixturevalue-2175-deterministic-fixture$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-2176-deterministic-fixturevalue-2178-deterministic-fixturevalue-2179-deterministic-fixturevalue-2180-deterministic-fixturevalue-2181-deterministic-fixturevalue-2182-deterministic-fixturevalue-2183-deterministic-fixturevalue-2185-deterministic-fixturevalue-2186-deterministic-fixturevalue-2187-deterministic-fixturevalue-2188-deterministic-fixturevalue-2189-deterministic-fixturevalue-2190-deterministic-fixturevalue-2192-deterministic-fixturevalue-2193-deterministic-fixturevalue-2194-deterministic-fixturevalue-2195-deterministic-fixturevalue-2196-deterministic-fixturevalue-2197-deterministic-fixturevalue-2199-deterministic-fixturevalue-2200-deterministic-fixturevalue-2201-deterministic-fixturevalue-2202-deterministic-fixturevalue-2203-deterministic-fixturevalue-2204-deterministic-fixturevalue-2206-deterministic-fixturevalue-2207-deterministic-fixturevalue-2208-deterministic-fixturevalue-2209-deterministic-fixturevalue-2210-deterministic-fixturevalue-2211-deterministic-fixturevalue-2213-deterministic-fixturevalue-2214-deterministic-fixturevalue-2215-deterministic-fixturevalue-2216-deterministic-fixturevalue-2217-deterministic-fixturevalue-2218-deterministic-fixturevalue-2220-deterministic-fixturevalue-2221-deterministic-fixturevalue-2222-deterministic-fixturevalue-2223-deterministic-fixturevalue-2224-deterministic-fixturevalue-2225-deterministic-fixturevalue-2227-deterministic-fixturevalue-2228-deterministic-fixturevalue-2229-deterministic-fixturevalue-2230-deterministic-fixturevalue-2231-deterministic-fixturevalue-2232-deterministic-fixturevalue-2234-deterministic-fixturevalue-2235-deterministic-fixturevalue-2236-deterministic-fixturevalue-2237-deterministic-fixturevalue-2238-deterministic-fixturevalue-2239-deterministic-fixturevalue-2241-deterministic-fixturevalue-2242-deterministic-fixturevalue-2243-deterministic-fixturevalue-2244-deterministic-fixturevalue-2245-deterministic-fixturevalue-2246-deterministic-fixturevalue-2248-deterministic-fixturevalue-2249-deterministic-fixturevalue-2250-deterministic-fixturevalue-2251-deterministic-fixturevalue-2252-deterministic-fixturevalue-2253-deterministic-fixturevalue-2255-deterministic-fixturevalue-2256-deterministic-fixturevalue-2257-deterministic-fixturevalue-2258-deterministic-fixturevalue-2259-deterministic-fixturevalue-2260-deterministic-fixturevalue-2262-deterministic-fixturevalue-2263-deterministic-fixturevalue-2264-deterministic-fixturevalue-2265-deterministic-fixturevalue-2266-deterministic-fixturevalue-2267-deterministic-fixturevalue-2269-deterministic-fixturevalue-2270-deterministic-fixturevalue-2271-deterministic-fixturevalue-2272-deterministic-fixturevalue-2273-deterministic-fixturevalue-2274-deterministic-fixturevalue-2276-deterministic-fixturevalue-2277-deterministic-fixturevalue-2278-deterministic-fixturevalue-2279-deterministic-fixturevalue-2280-deterministic-fixturevalue-2281-deterministic-fixturevalue-2283-deterministic-fixturevalue-2284-deterministic-fixturevalue-2285-deterministic-fixturevalue-2286-deterministic-fixturevalue-2287-deterministic-fixturevalue-2288-deterministic-fixturevalue-2290-deterministic-fixturevalue-2291-deterministic-fixturevalue-2292-deterministic-fixturevalue-2293-deterministic-fixturevalue-2294-deterministic-fixturevalue-2295-deterministic-fixturevalue-2297-deterministic-fixturevalue-2298-deterministic-fixturevalue-2299-deterministic-fixturevalue-2300-deterministic-fixturevalue-2301-deterministic-fixturevalue-2302-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-2304-deterministic-fixturevalue-2305-deterministic-fixturevalue-2306-deterministic-fixturevalue-2307-deterministic-fixturevalue-2308-deterministic-fixturevalue-2309-deterministic-fixturevalue-2311-deterministic-fixturevalue-2312-deterministic-fixturevalue-2313-deterministic-fixturevalue-2314-deterministic-fixturevalue-2315-deterministic-fixturevalue-2316-deterministic-fixturevalue-2318-deterministic-fixturevalue-2319-deterministic-fixturevalue-2320-deterministic-fixturevalue-2321-deterministic-fixturevalue-2322-deterministic-fixturevalue-2323-deterministic-fixturevalue-2325-deterministic-fixturevalue-2326-deterministic-fixturevalue-2327-deterministic-fixturevalue-2328-deterministic-fixturevalue-2329-deterministic-fixturevalue-2330-deterministic-fixturevalue-2332-deterministic-fixturevalue-2333-deterministic-fixturevalue-2334-deterministic-fixturevalue-2335-deterministic-fixturevalue-2336-deterministic-fixturevalue-2337-deterministic-fixturevalue-2339-deterministic-fixturevalue-2340-deterministic-fixturevalue-2341-deterministic-fixturevalue-2342-deterministic-fixturevalue-2343-deterministic-fixturevalue-2344-deterministic-fixturevalue-2346-deterministic-fixturevalue-2347-deterministic-fixturevalue-2348-deterministic-fixturevalue-2349-deterministic-fixturevalue-2350-deterministic-fixturevalue-2351-deterministic-fixturevalue-2353-deterministic-fixturevalue-2354-deterministic-fixturevalue-2355-deterministic-fixturevalue-2356-deterministic-fixturevalue-2357-deterministic-fixturevalue-2358-deterministic-fixturevalue-2360-deterministic-fixturevalue-2361-deterministic-fixturevalue-2362-deterministic-fixturevalue-2363-deterministic-fixturevalue-2364-deterministic-fixturevalue-2365-deterministic-fixturevalue-2367-deterministic-fixturevalue-2368-deterministic-fixturevalue-2369-deterministic-fixturevalue-2370-deterministic-fixturevalue-2371-deterministic-fixturevalue-2372-deterministic-fixturevalue-2374-deterministic-fixturevalue-2375-deterministic-fixturevalue-2376-deterministic-fixturevalue-2377-deterministic-fixturevalue-2378-deterministic-fixturevalue-2379-deterministic-fixturevalue-2381-deterministic-fixturevalue-2382-deterministic-fixturevalue-2383-deterministic-fixturevalue-2384-deterministic-fixturevalue-2385-deterministic-fixturevalue-2386-deterministic-fixturevalue-2388-deterministic-fixturevalue-2389-deterministic-fixturevalue-2390-deterministic-fixturevalue-2391-deterministic-fixturevalue-2392-deterministic-fixturevalue-2393-deterministic-fixturevalue-2395-deterministic-fixturevalue-2396-deterministic-fixturevalue-2397-deterministic-fixturevalue-2398-deterministic-fixturevalue-2399-deterministic-fixturevalue-2400-deterministic-fixturevalue-2402-deterministic-fixturevalue-2403-deterministic-fixturevalue-2404-deterministic-fixturevalue-2405-deterministic-fixturevalue-2406-deterministic-fixturevalue-2407-deterministic-fixturevalue-2409-deterministic-fixturevalue-2410-deterministic-fixturevalue-2411-deterministic-fixturevalue-2412-deterministic-fixturevalue-2413-deterministic-fixturevalue-2414-deterministic-fixturevalue-2416-deterministic-fixturevalue-2417-deterministic-fixturevalue-2418-deterministic-fixturevalue-2419-deterministic-fixturevalue-2420-deterministic-fixturevalue-2421-deterministic-fixturevalue-2423-deterministic-fixturevalue-2424-deterministic-fixturevalue-2425-deterministic-fixturevalue-2426-deterministic-fixturevalue-2427-deterministic-fixturevalue-2428-deterministic-fixturevalue-2430-deterministic-fixturevalue-2431-deterministic-fixture$Dd$DDd$Dd$Dd$DDd$Dd$Dd $ D D d  + +$ +D +d + + + + + + $ D d  $ D D d   $ D d $Dd$DDdvalue-2432-deterministic-fixturevalue-2433-deterministic-fixturevalue-2434-deterministic-fixturevalue-2435-deterministic-fixturevalue-2437-deterministic-fixturevalue-2438-deterministic-fixturevalue-2439-deterministic-fixturevalue-2440-deterministic-fixturevalue-2441-deterministic-fixturevalue-2442-deterministic-fixturevalue-2444-deterministic-fixturevalue-2445-deterministic-fixturevalue-2446-deterministic-fixturevalue-2447-deterministic-fixturevalue-2448-deterministic-fixturevalue-2449-deterministic-fixturevalue-2451-deterministic-fixturevalue-2452-deterministic-fixturevalue-2453-deterministic-fixturevalue-2454-deterministic-fixturevalue-2455-deterministic-fixturevalue-2456-deterministic-fixturevalue-2458-deterministic-fixturevalue-2459-deterministic-fixturevalue-2460-deterministic-fixturevalue-2461-deterministic-fixturevalue-2462-deterministic-fixturevalue-2463-deterministic-fixturevalue-2465-deterministic-fixturevalue-2466-deterministic-fixturevalue-2467-deterministic-fixturevalue-2468-deterministic-fixturevalue-2469-deterministic-fixturevalue-2470-deterministic-fixturevalue-2472-deterministic-fixturevalue-2473-deterministic-fixturevalue-2474-deterministic-fixturevalue-2475-deterministic-fixturevalue-2476-deterministic-fixturevalue-2477-deterministic-fixturevalue-2479-deterministic-fixturevalue-2480-deterministic-fixturevalue-2481-deterministic-fixturevalue-2482-deterministic-fixturevalue-2483-deterministic-fixturevalue-2484-deterministic-fixturevalue-2486-deterministic-fixturevalue-2487-deterministic-fixturevalue-2488-deterministic-fixturevalue-2489-deterministic-fixturevalue-2490-deterministic-fixturevalue-2491-deterministic-fixturevalue-2493-deterministic-fixturevalue-2494-deterministic-fixturevalue-2495-deterministic-fixturevalue-2496-deterministic-fixturevalue-2497-deterministic-fixturevalue-2498-deterministic-fixturevalue-2500-deterministic-fixturevalue-2501-deterministic-fixturevalue-2502-deterministic-fixturevalue-2503-deterministic-fixturevalue-2504-deterministic-fixturevalue-2505-deterministic-fixturevalue-2507-deterministic-fixturevalue-2508-deterministic-fixturevalue-2509-deterministic-fixturevalue-2510-deterministic-fixturevalue-2511-deterministic-fixturevalue-2512-deterministic-fixturevalue-2514-deterministic-fixturevalue-2515-deterministic-fixturevalue-2516-deterministic-fixturevalue-2517-deterministic-fixturevalue-2518-deterministic-fixturevalue-2519-deterministic-fixturevalue-2521-deterministic-fixturevalue-2522-deterministic-fixturevalue-2523-deterministic-fixturevalue-2524-deterministic-fixturevalue-2525-deterministic-fixturevalue-2526-deterministic-fixturevalue-2528-deterministic-fixturevalue-2529-deterministic-fixturevalue-2530-deterministic-fixturevalue-2531-deterministic-fixturevalue-2532-deterministic-fixturevalue-2533-deterministic-fixturevalue-2535-deterministic-fixturevalue-2536-deterministic-fixturevalue-2537-deterministic-fixturevalue-2538-deterministic-fixturevalue-2539-deterministic-fixturevalue-2540-deterministic-fixturevalue-2542-deterministic-fixturevalue-2543-deterministic-fixturevalue-2544-deterministic-fixturevalue-2545-deterministic-fixturevalue-2546-deterministic-fixturevalue-2547-deterministic-fixturevalue-2549-deterministic-fixturevalue-2550-deterministic-fixturevalue-2551-deterministic-fixturevalue-2552-deterministic-fixturevalue-2553-deterministic-fixturevalue-2554-deterministic-fixturevalue-2556-deterministic-fixturevalue-2557-deterministic-fixturevalue-2558-deterministic-fixturevalue-2559-deterministic-fixture$DDd$Dd$Dd$DDd$Dd$Dd$DDd  $ D d  +$ +D +d + + + + + + $ D D d   $ D d  $ D d $DDd$Ddvalue-2560-deterministic-fixturevalue-2561-deterministic-fixturevalue-2563-deterministic-fixturevalue-2564-deterministic-fixturevalue-2565-deterministic-fixturevalue-2566-deterministic-fixturevalue-2567-deterministic-fixturevalue-2568-deterministic-fixturevalue-2570-deterministic-fixturevalue-2571-deterministic-fixturevalue-2572-deterministic-fixturevalue-2573-deterministic-fixturevalue-2574-deterministic-fixturevalue-2575-deterministic-fixturevalue-2577-deterministic-fixturevalue-2578-deterministic-fixturevalue-2579-deterministic-fixturevalue-2580-deterministic-fixturevalue-2581-deterministic-fixturevalue-2582-deterministic-fixturevalue-2584-deterministic-fixturevalue-2585-deterministic-fixturevalue-2586-deterministic-fixturevalue-2587-deterministic-fixturevalue-2588-deterministic-fixturevalue-2589-deterministic-fixturevalue-2591-deterministic-fixturevalue-2592-deterministic-fixturevalue-2593-deterministic-fixturevalue-2594-deterministic-fixturevalue-2595-deterministic-fixturevalue-2596-deterministic-fixturevalue-2598-deterministic-fixturevalue-2599-deterministic-fixturevalue-2600-deterministic-fixturevalue-2601-deterministic-fixturevalue-2602-deterministic-fixturevalue-2603-deterministic-fixturevalue-2605-deterministic-fixturevalue-2606-deterministic-fixturevalue-2607-deterministic-fixturevalue-2608-deterministic-fixturevalue-2609-deterministic-fixturevalue-2610-deterministic-fixturevalue-2612-deterministic-fixturevalue-2613-deterministic-fixturevalue-2614-deterministic-fixturevalue-2615-deterministic-fixturevalue-2616-deterministic-fixturevalue-2617-deterministic-fixturevalue-2619-deterministic-fixturevalue-2620-deterministic-fixturevalue-2621-deterministic-fixturevalue-2622-deterministic-fixturevalue-2623-deterministic-fixturevalue-2624-deterministic-fixturevalue-2626-deterministic-fixturevalue-2627-deterministic-fixturevalue-2628-deterministic-fixturevalue-2629-deterministic-fixturevalue-2630-deterministic-fixturevalue-2631-deterministic-fixturevalue-2633-deterministic-fixturevalue-2634-deterministic-fixturevalue-2635-deterministic-fixturevalue-2636-deterministic-fixturevalue-2637-deterministic-fixturevalue-2638-deterministic-fixturevalue-2640-deterministic-fixturevalue-2641-deterministic-fixturevalue-2642-deterministic-fixturevalue-2643-deterministic-fixturevalue-2644-deterministic-fixturevalue-2645-deterministic-fixturevalue-2647-deterministic-fixturevalue-2648-deterministic-fixturevalue-2649-deterministic-fixturevalue-2650-deterministic-fixturevalue-2651-deterministic-fixturevalue-2652-deterministic-fixturevalue-2654-deterministic-fixturevalue-2655-deterministic-fixturevalue-2656-deterministic-fixturevalue-2657-deterministic-fixturevalue-2658-deterministic-fixturevalue-2659-deterministic-fixturevalue-2661-deterministic-fixturevalue-2662-deterministic-fixturevalue-2663-deterministic-fixturevalue-2664-deterministic-fixturevalue-2665-deterministic-fixturevalue-2666-deterministic-fixturevalue-2668-deterministic-fixturevalue-2669-deterministic-fixturevalue-2670-deterministic-fixturevalue-2671-deterministic-fixturevalue-2672-deterministic-fixturevalue-2673-deterministic-fixturevalue-2675-deterministic-fixturevalue-2676-deterministic-fixturevalue-2677-deterministic-fixturevalue-2678-deterministic-fixturevalue-2679-deterministic-fixturevalue-2680-deterministic-fixturevalue-2682-deterministic-fixturevalue-2683-deterministic-fixturevalue-2684-deterministic-fixturevalue-2685-deterministic-fixturevalue-2686-deterministic-fixturevalue-2687-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-2689-deterministic-fixturevalue-2690-deterministic-fixturevalue-2691-deterministic-fixturevalue-2692-deterministic-fixturevalue-2693-deterministic-fixturevalue-2694-deterministic-fixturevalue-2696-deterministic-fixturevalue-2697-deterministic-fixturevalue-2698-deterministic-fixturevalue-2699-deterministic-fixturevalue-2700-deterministic-fixturevalue-2701-deterministic-fixturevalue-2703-deterministic-fixturevalue-2704-deterministic-fixturevalue-2705-deterministic-fixturevalue-2706-deterministic-fixturevalue-2707-deterministic-fixturevalue-2708-deterministic-fixturevalue-2710-deterministic-fixturevalue-2711-deterministic-fixturevalue-2712-deterministic-fixturevalue-2713-deterministic-fixturevalue-2714-deterministic-fixturevalue-2715-deterministic-fixturevalue-2717-deterministic-fixturevalue-2718-deterministic-fixturevalue-2719-deterministic-fixturevalue-2720-deterministic-fixturevalue-2721-deterministic-fixturevalue-2722-deterministic-fixturevalue-2724-deterministic-fixturevalue-2725-deterministic-fixturevalue-2726-deterministic-fixturevalue-2727-deterministic-fixturevalue-2728-deterministic-fixturevalue-2729-deterministic-fixturevalue-2731-deterministic-fixturevalue-2732-deterministic-fixturevalue-2733-deterministic-fixturevalue-2734-deterministic-fixturevalue-2735-deterministic-fixturevalue-2736-deterministic-fixturevalue-2738-deterministic-fixturevalue-2739-deterministic-fixturevalue-2740-deterministic-fixturevalue-2741-deterministic-fixturevalue-2742-deterministic-fixturevalue-2743-deterministic-fixturevalue-2745-deterministic-fixturevalue-2746-deterministic-fixturevalue-2747-deterministic-fixturevalue-2748-deterministic-fixturevalue-2749-deterministic-fixturevalue-2750-deterministic-fixturevalue-2752-deterministic-fixturevalue-2753-deterministic-fixturevalue-2754-deterministic-fixturevalue-2755-deterministic-fixturevalue-2756-deterministic-fixturevalue-2757-deterministic-fixturevalue-2759-deterministic-fixturevalue-2760-deterministic-fixturevalue-2761-deterministic-fixturevalue-2762-deterministic-fixturevalue-2763-deterministic-fixturevalue-2764-deterministic-fixturevalue-2766-deterministic-fixturevalue-2767-deterministic-fixturevalue-2768-deterministic-fixturevalue-2769-deterministic-fixturevalue-2770-deterministic-fixturevalue-2771-deterministic-fixturevalue-2773-deterministic-fixturevalue-2774-deterministic-fixturevalue-2775-deterministic-fixturevalue-2776-deterministic-fixturevalue-2777-deterministic-fixturevalue-2778-deterministic-fixturevalue-2780-deterministic-fixturevalue-2781-deterministic-fixturevalue-2782-deterministic-fixturevalue-2783-deterministic-fixturevalue-2784-deterministic-fixturevalue-2785-deterministic-fixturevalue-2787-deterministic-fixturevalue-2788-deterministic-fixturevalue-2789-deterministic-fixturevalue-2790-deterministic-fixturevalue-2791-deterministic-fixturevalue-2792-deterministic-fixturevalue-2794-deterministic-fixturevalue-2795-deterministic-fixturevalue-2796-deterministic-fixturevalue-2797-deterministic-fixturevalue-2798-deterministic-fixturevalue-2799-deterministic-fixturevalue-2801-deterministic-fixturevalue-2802-deterministic-fixturevalue-2803-deterministic-fixturevalue-2804-deterministic-fixturevalue-2805-deterministic-fixturevalue-2806-deterministic-fixturevalue-2808-deterministic-fixturevalue-2809-deterministic-fixturevalue-2810-deterministic-fixturevalue-2811-deterministic-fixturevalue-2812-deterministic-fixturevalue-2813-deterministic-fixturevalue-2815-deterministic-fixture$Dd$Ddd$$Dd$Dd$Ddd$$Dd$Dd $ D d d  +$ +$ +D +d + + + + + + $ D d  $ D d d  $ $ D d $Dd$Dddvalue-2816-deterministic-fixturevalue-2817-deterministic-fixturevalue-2818-deterministic-fixturevalue-2819-deterministic-fixturevalue-2820-deterministic-fixturevalue-2822-deterministic-fixturevalue-2823-deterministic-fixturevalue-2824-deterministic-fixturevalue-2825-deterministic-fixturevalue-2826-deterministic-fixturevalue-2827-deterministic-fixturevalue-2829-deterministic-fixturevalue-2830-deterministic-fixturevalue-2831-deterministic-fixturevalue-2832-deterministic-fixturevalue-2833-deterministic-fixturevalue-2834-deterministic-fixturevalue-2836-deterministic-fixturevalue-2837-deterministic-fixturevalue-2838-deterministic-fixturevalue-2839-deterministic-fixturevalue-2840-deterministic-fixturevalue-2841-deterministic-fixturevalue-2843-deterministic-fixturevalue-2844-deterministic-fixturevalue-2845-deterministic-fixturevalue-2846-deterministic-fixturevalue-2847-deterministic-fixturevalue-2848-deterministic-fixturevalue-2850-deterministic-fixturevalue-2851-deterministic-fixturevalue-2852-deterministic-fixturevalue-2853-deterministic-fixturevalue-2854-deterministic-fixturevalue-2855-deterministic-fixturevalue-2857-deterministic-fixturevalue-2858-deterministic-fixturevalue-2859-deterministic-fixturevalue-2860-deterministic-fixturevalue-2861-deterministic-fixturevalue-2862-deterministic-fixturevalue-2864-deterministic-fixturevalue-2865-deterministic-fixturevalue-2866-deterministic-fixturevalue-2867-deterministic-fixturevalue-2868-deterministic-fixturevalue-2869-deterministic-fixturevalue-2871-deterministic-fixturevalue-2872-deterministic-fixturevalue-2873-deterministic-fixturevalue-2874-deterministic-fixturevalue-2875-deterministic-fixturevalue-2876-deterministic-fixturevalue-2878-deterministic-fixturevalue-2879-deterministic-fixturevalue-2880-deterministic-fixturevalue-2881-deterministic-fixturevalue-2882-deterministic-fixturevalue-2883-deterministic-fixturevalue-2885-deterministic-fixturevalue-2886-deterministic-fixturevalue-2887-deterministic-fixturevalue-2888-deterministic-fixturevalue-2889-deterministic-fixturevalue-2890-deterministic-fixturevalue-2892-deterministic-fixturevalue-2893-deterministic-fixturevalue-2894-deterministic-fixturevalue-2895-deterministic-fixturevalue-2896-deterministic-fixturevalue-2897-deterministic-fixturevalue-2899-deterministic-fixturevalue-2900-deterministic-fixturevalue-2901-deterministic-fixturevalue-2902-deterministic-fixturevalue-2903-deterministic-fixturevalue-2904-deterministic-fixturevalue-2906-deterministic-fixturevalue-2907-deterministic-fixturevalue-2908-deterministic-fixturevalue-2909-deterministic-fixturevalue-2910-deterministic-fixturevalue-2911-deterministic-fixturevalue-2913-deterministic-fixturevalue-2914-deterministic-fixturevalue-2915-deterministic-fixturevalue-2916-deterministic-fixturevalue-2917-deterministic-fixturevalue-2918-deterministic-fixturevalue-2920-deterministic-fixturevalue-2921-deterministic-fixturevalue-2922-deterministic-fixturevalue-2923-deterministic-fixturevalue-2924-deterministic-fixturevalue-2925-deterministic-fixturevalue-2927-deterministic-fixturevalue-2928-deterministic-fixturevalue-2929-deterministic-fixturevalue-2930-deterministic-fixturevalue-2931-deterministic-fixturevalue-2932-deterministic-fixturevalue-2934-deterministic-fixturevalue-2935-deterministic-fixturevalue-2936-deterministic-fixturevalue-2937-deterministic-fixturevalue-2938-deterministic-fixturevalue-2939-deterministic-fixturevalue-2941-deterministic-fixturevalue-2942-deterministic-fixturevalue-2943-deterministic-fixture$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-2944-deterministic-fixturevalue-2945-deterministic-fixturevalue-2946-deterministic-fixturevalue-2948-deterministic-fixturevalue-2949-deterministic-fixturevalue-2950-deterministic-fixturevalue-2951-deterministic-fixturevalue-2952-deterministic-fixturevalue-2953-deterministic-fixturevalue-2955-deterministic-fixturevalue-2956-deterministic-fixturevalue-2957-deterministic-fixturevalue-2958-deterministic-fixturevalue-2959-deterministic-fixturevalue-2960-deterministic-fixturevalue-2962-deterministic-fixturevalue-2963-deterministic-fixturevalue-2964-deterministic-fixturevalue-2965-deterministic-fixturevalue-2966-deterministic-fixturevalue-2967-deterministic-fixturevalue-2969-deterministic-fixturevalue-2970-deterministic-fixturevalue-2971-deterministic-fixturevalue-2972-deterministic-fixturevalue-2973-deterministic-fixturevalue-2974-deterministic-fixturevalue-2976-deterministic-fixturevalue-2977-deterministic-fixturevalue-2978-deterministic-fixturevalue-2979-deterministic-fixturevalue-2980-deterministic-fixturevalue-2981-deterministic-fixturevalue-2983-deterministic-fixturevalue-2984-deterministic-fixturevalue-2985-deterministic-fixturevalue-2986-deterministic-fixturevalue-2987-deterministic-fixturevalue-2988-deterministic-fixturevalue-2990-deterministic-fixturevalue-2991-deterministic-fixturevalue-2992-deterministic-fixturevalue-2993-deterministic-fixturevalue-2994-deterministic-fixturevalue-2995-deterministic-fixturevalue-2997-deterministic-fixturevalue-2998-deterministic-fixturevalue-2999-deterministic-fixturevalue-3000-deterministic-fixturevalue-3001-deterministic-fixturevalue-3002-deterministic-fixturevalue-3004-deterministic-fixturevalue-3005-deterministic-fixturevalue-3006-deterministic-fixturevalue-3007-deterministic-fixturevalue-3008-deterministic-fixturevalue-3009-deterministic-fixturevalue-3011-deterministic-fixturevalue-3012-deterministic-fixturevalue-3013-deterministic-fixturevalue-3014-deterministic-fixturevalue-3015-deterministic-fixturevalue-3016-deterministic-fixturevalue-3018-deterministic-fixturevalue-3019-deterministic-fixturevalue-3020-deterministic-fixturevalue-3021-deterministic-fixturevalue-3022-deterministic-fixturevalue-3023-deterministic-fixturevalue-3025-deterministic-fixturevalue-3026-deterministic-fixturevalue-3027-deterministic-fixturevalue-3028-deterministic-fixturevalue-3029-deterministic-fixturevalue-3030-deterministic-fixturevalue-3032-deterministic-fixturevalue-3033-deterministic-fixturevalue-3034-deterministic-fixturevalue-3035-deterministic-fixturevalue-3036-deterministic-fixturevalue-3037-deterministic-fixturevalue-3039-deterministic-fixturevalue-3040-deterministic-fixturevalue-3041-deterministic-fixturevalue-3042-deterministic-fixturevalue-3043-deterministic-fixturevalue-3044-deterministic-fixturevalue-3046-deterministic-fixturevalue-3047-deterministic-fixturevalue-3048-deterministic-fixturevalue-3049-deterministic-fixturevalue-3050-deterministic-fixturevalue-3051-deterministic-fixturevalue-3053-deterministic-fixturevalue-3054-deterministic-fixturevalue-3055-deterministic-fixturevalue-3056-deterministic-fixturevalue-3057-deterministic-fixturevalue-3058-deterministic-fixturevalue-3060-deterministic-fixturevalue-3061-deterministic-fixturevalue-3062-deterministic-fixturevalue-3063-deterministic-fixturevalue-3064-deterministic-fixturevalue-3065-deterministic-fixturevalue-3067-deterministic-fixturevalue-3068-deterministic-fixturevalue-3069-deterministic-fixturevalue-3070-deterministic-fixturevalue-3071-deterministic-fixture::HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"D$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$!@@@ @@ @!@@ H@ @@ @@ @H@  H]xi ~ uX hP p ( +#  H+  +;0 hCPBFdȋ!ҌBGDdЋ!լGtd苡CHd!Hԣd CId!,I4d LCJdd8!lJdX CKdh!K$dxcF|LҖFڂ5PّӦF򈵍\QԶ@G +dG"5lޑֽG:5pQ#HR5|@HjͣH5HQ&#I5.JEH0JH2 JK40 JNXh@ JQ8 JT: JW< JZ H J`!B +Jc"D0 +JfXqe8!lR}eHڡ CSĉeX!Se`CT$ex!TTe CUe! UemCVe!VDeWte 6 dL  L6 R$M6 dMM*6!M0R%$N66)dN<NB61NHRK! ` K$ (K'&2K*X6J$K-FHb ,K0z 4K3Xfh$P8K6vȪ&pDK9(*TK?\KBXKLdSCMd!MekCNe!sN)e{CO5e!OAeơCPMe!PYeΡCQee!CRTcĵ ф5,񑴥#5 c5l5D،赬Qt#5c 5,4 LRdF#IFJiV$IHJl%JJov&J*LJrކ'JBNJuߖ(JZPJxক)KrRJ{+KVJƕ,KXJ֕-KZJ.L\hDP +f" p + f(D +%f" +1fX+D +=f" 3 If!;D0 UfX""h!CP af&!p mf*"("SD yf,""[İ fX5NXԩ9T Y=Z -!Y4(` M%ZdI8l &ZMHr)[ĪQ`x+[$x~-\TY .]]-1]a$O +6dKƖO"6lK֖O:⶟tKRPR6|KdPjⶠKP6KPⶡKR&Q6K6dQⶢKVQ6KfQ6K"Ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd@@ @!@@@ @@ @!@@ H@ @@ @iePxRjnԔ{>'jqդ|?Gktִ} AgRkw~AkBzԀBkrICl DSl҆!P30xf:(4x|R1Ԙ215JgR@ +ˇRT2U˧R3eJ"4u +RTRS5'S5JGS6 +T+V+ V +VI" + $+!V0&+)VI<(+1V H,S+œ9VT!+`0)+ȜQVIx2,+YV ĞDMpoBPN=uo\O=yphP>}ptQp2$RG>qb 0Sg>qDS>qTT>q)dUr"1tV>yT(ޞE+ݜVÍI+ ؁DSM+朙VFT+VHY+VIJ]+rR9V'?r够WG?rI崟XsQğY?sBYПZ?sra[?t\?tq(\t@]'@t2 L^G@ubDXQV +yT(BWy(CQXy(DQY +yT( DQZz(EQ\zԢ(E]Jz(Q^ +zT(FQ_z(GQ`zԣ( GaJz+WUV+"WeV+"*WuV+RȂW+:WԂ+BWGW+JWgW+ūW+BbWիW+rjWW+rW2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 2@ H@ @@@ @!@ UޕB VǢРJW  +X Y(2٠%JZHb 5 +[hUɒ⠒E[ U\ `J]" u +^ĉTi-ЉVi-[܉X-[Zj-[j. +\^j.  bj .\d .\$j.*\@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH ,<L\l| + +  ,<L\l| -.=>MN]^mn}~ +  -.=>MN]^mn}~/?O_o + /?O_o@ @AAB,BCMDN]E^mFn}G~HIJKLMNO PQ-R.=S>MTN]U^mVn}W~XYZ[\]^_@AB/C?DOE_FoGHIJKLMNOPQR/S?TOU_VoWXYZ[\]^_ !!","#<#$L$%\%&l&'|'(())**++,,--..//0 0112,23<34L45\56l67|78899::;;<<==>>?? !-".=#>M$N]%^m&n}'~()*+,-./ 01-2.=3>M4N]5^m6n}7~89:;<=>? !"/#?$O%_&o'()*+,-./012/3?4O5_6o789:;<=>?` `aab,bcMdN]e^mfn}g~hijklmno pq-r.=s>MtN]u^mvn}w~xyz{|}~`ab/c?dOe_foghijklmnopqr/s?tOu_vowxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHg g ` HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-3072-deterministic-fixturevalue-3074-deterministic-fixturevalue-3075-deterministic-fixturevalue-3076-deterministic-fixturevalue-3077-deterministic-fixturevalue-3078-deterministic-fixturevalue-3079-deterministic-fixturevalue-3081-deterministic-fixturevalue-3082-deterministic-fixturevalue-3083-deterministic-fixturevalue-3084-deterministic-fixturevalue-3085-deterministic-fixturevalue-3086-deterministic-fixturevalue-3088-deterministic-fixturevalue-3089-deterministic-fixturevalue-3090-deterministic-fixturevalue-3091-deterministic-fixturevalue-3092-deterministic-fixturevalue-3093-deterministic-fixturevalue-3095-deterministic-fixturevalue-3096-deterministic-fixturevalue-3097-deterministic-fixturevalue-3098-deterministic-fixturevalue-3099-deterministic-fixturevalue-3100-deterministic-fixturevalue-3102-deterministic-fixturevalue-3103-deterministic-fixturevalue-3104-deterministic-fixturevalue-3105-deterministic-fixturevalue-3106-deterministic-fixturevalue-3107-deterministic-fixturevalue-3109-deterministic-fixturevalue-3110-deterministic-fixturevalue-3111-deterministic-fixturevalue-3112-deterministic-fixturevalue-3113-deterministic-fixturevalue-3114-deterministic-fixturevalue-3116-deterministic-fixturevalue-3117-deterministic-fixturevalue-3118-deterministic-fixturevalue-3119-deterministic-fixturevalue-3120-deterministic-fixturevalue-3121-deterministic-fixturevalue-3123-deterministic-fixturevalue-3124-deterministic-fixturevalue-3125-deterministic-fixturevalue-3126-deterministic-fixturevalue-3127-deterministic-fixturevalue-3128-deterministic-fixturevalue-3130-deterministic-fixturevalue-3131-deterministic-fixturevalue-3132-deterministic-fixturevalue-3133-deterministic-fixturevalue-3134-deterministic-fixturevalue-3135-deterministic-fixturevalue-3137-deterministic-fixturevalue-3138-deterministic-fixturevalue-3139-deterministic-fixturevalue-3140-deterministic-fixturevalue-3141-deterministic-fixturevalue-3142-deterministic-fixturevalue-3144-deterministic-fixturevalue-3145-deterministic-fixturevalue-3146-deterministic-fixturevalue-3147-deterministic-fixturevalue-3148-deterministic-fixturevalue-3149-deterministic-fixturevalue-3151-deterministic-fixturevalue-3152-deterministic-fixturevalue-3153-deterministic-fixturevalue-3154-deterministic-fixturevalue-3155-deterministic-fixturevalue-3156-deterministic-fixturevalue-3158-deterministic-fixturevalue-3159-deterministic-fixturevalue-3160-deterministic-fixturevalue-3161-deterministic-fixturevalue-3162-deterministic-fixturevalue-3163-deterministic-fixturevalue-3165-deterministic-fixturevalue-3166-deterministic-fixturevalue-3167-deterministic-fixturevalue-3168-deterministic-fixturevalue-3169-deterministic-fixturevalue-3170-deterministic-fixturevalue-3172-deterministic-fixturevalue-3173-deterministic-fixturevalue-3174-deterministic-fixturevalue-3175-deterministic-fixturevalue-3176-deterministic-fixturevalue-3177-deterministic-fixturevalue-3179-deterministic-fixturevalue-3180-deterministic-fixturevalue-3181-deterministic-fixturevalue-3182-deterministic-fixturevalue-3183-deterministic-fixturevalue-3184-deterministic-fixturevalue-3186-deterministic-fixturevalue-3187-deterministic-fixturevalue-3188-deterministic-fixturevalue-3189-deterministic-fixturevalue-3190-deterministic-fixturevalue-3191-deterministic-fixturevalue-3193-deterministic-fixturevalue-3194-deterministic-fixturevalue-3195-deterministic-fixturevalue-3196-deterministic-fixturevalue-3197-deterministic-fixturevalue-3198-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-3200-deterministic-fixturevalue-3201-deterministic-fixturevalue-3202-deterministic-fixturevalue-3203-deterministic-fixturevalue-3204-deterministic-fixturevalue-3205-deterministic-fixturevalue-3207-deterministic-fixturevalue-3208-deterministic-fixturevalue-3209-deterministic-fixturevalue-3210-deterministic-fixturevalue-3211-deterministic-fixturevalue-3212-deterministic-fixturevalue-3214-deterministic-fixturevalue-3215-deterministic-fixturevalue-3216-deterministic-fixturevalue-3217-deterministic-fixturevalue-3218-deterministic-fixturevalue-3219-deterministic-fixturevalue-3221-deterministic-fixturevalue-3222-deterministic-fixturevalue-3223-deterministic-fixturevalue-3224-deterministic-fixturevalue-3225-deterministic-fixturevalue-3226-deterministic-fixturevalue-3228-deterministic-fixturevalue-3229-deterministic-fixturevalue-3230-deterministic-fixturevalue-3231-deterministic-fixturevalue-3232-deterministic-fixturevalue-3233-deterministic-fixturevalue-3235-deterministic-fixturevalue-3236-deterministic-fixturevalue-3237-deterministic-fixturevalue-3238-deterministic-fixturevalue-3239-deterministic-fixturevalue-3240-deterministic-fixturevalue-3242-deterministic-fixturevalue-3243-deterministic-fixturevalue-3244-deterministic-fixturevalue-3245-deterministic-fixturevalue-3246-deterministic-fixturevalue-3247-deterministic-fixturevalue-3249-deterministic-fixturevalue-3250-deterministic-fixturevalue-3251-deterministic-fixturevalue-3252-deterministic-fixturevalue-3253-deterministic-fixturevalue-3254-deterministic-fixturevalue-3256-deterministic-fixturevalue-3257-deterministic-fixturevalue-3258-deterministic-fixturevalue-3259-deterministic-fixturevalue-3260-deterministic-fixturevalue-3261-deterministic-fixturevalue-3263-deterministic-fixturevalue-3264-deterministic-fixturevalue-3265-deterministic-fixturevalue-3266-deterministic-fixturevalue-3267-deterministic-fixturevalue-3268-deterministic-fixturevalue-3270-deterministic-fixturevalue-3271-deterministic-fixturevalue-3272-deterministic-fixturevalue-3273-deterministic-fixturevalue-3274-deterministic-fixturevalue-3275-deterministic-fixturevalue-3277-deterministic-fixturevalue-3278-deterministic-fixturevalue-3279-deterministic-fixturevalue-3280-deterministic-fixturevalue-3281-deterministic-fixturevalue-3282-deterministic-fixturevalue-3284-deterministic-fixturevalue-3285-deterministic-fixturevalue-3286-deterministic-fixturevalue-3287-deterministic-fixturevalue-3288-deterministic-fixturevalue-3289-deterministic-fixturevalue-3291-deterministic-fixturevalue-3292-deterministic-fixturevalue-3293-deterministic-fixturevalue-3294-deterministic-fixturevalue-3295-deterministic-fixturevalue-3296-deterministic-fixturevalue-3298-deterministic-fixturevalue-3299-deterministic-fixturevalue-3300-deterministic-fixturevalue-3301-deterministic-fixturevalue-3302-deterministic-fixturevalue-3303-deterministic-fixturevalue-3305-deterministic-fixturevalue-3306-deterministic-fixturevalue-3307-deterministic-fixturevalue-3308-deterministic-fixturevalue-3309-deterministic-fixturevalue-3310-deterministic-fixturevalue-3312-deterministic-fixturevalue-3313-deterministic-fixturevalue-3314-deterministic-fixturevalue-3315-deterministic-fixturevalue-3316-deterministic-fixturevalue-3317-deterministic-fixturevalue-3319-deterministic-fixturevalue-3320-deterministic-fixturevalue-3321-deterministic-fixturevalue-3322-deterministic-fixturevalue-3323-deterministic-fixturevalue-3324-deterministic-fixturevalue-3326-deterministic-fixturevalue-3327-deterministic-fixture$Dd$DDd$Dd$Dd$DDd$Dd$Dd $ D D d  + +$ +D +d + + + + + + $ D d  $ D D d   $ D d $Dd$DDdvalue-3328-deterministic-fixturevalue-3329-deterministic-fixturevalue-3330-deterministic-fixturevalue-3331-deterministic-fixturevalue-3333-deterministic-fixturevalue-3334-deterministic-fixturevalue-3335-deterministic-fixturevalue-3336-deterministic-fixturevalue-3337-deterministic-fixturevalue-3338-deterministic-fixturevalue-3340-deterministic-fixturevalue-3341-deterministic-fixturevalue-3342-deterministic-fixturevalue-3343-deterministic-fixturevalue-3344-deterministic-fixturevalue-3345-deterministic-fixturevalue-3347-deterministic-fixturevalue-3348-deterministic-fixturevalue-3349-deterministic-fixturevalue-3350-deterministic-fixturevalue-3351-deterministic-fixturevalue-3352-deterministic-fixturevalue-3354-deterministic-fixturevalue-3355-deterministic-fixturevalue-3356-deterministic-fixturevalue-3357-deterministic-fixturevalue-3358-deterministic-fixturevalue-3359-deterministic-fixturevalue-3361-deterministic-fixturevalue-3362-deterministic-fixturevalue-3363-deterministic-fixturevalue-3364-deterministic-fixturevalue-3365-deterministic-fixturevalue-3366-deterministic-fixturevalue-3368-deterministic-fixturevalue-3369-deterministic-fixturevalue-3370-deterministic-fixturevalue-3371-deterministic-fixturevalue-3372-deterministic-fixturevalue-3373-deterministic-fixturevalue-3375-deterministic-fixturevalue-3376-deterministic-fixturevalue-3377-deterministic-fixturevalue-3378-deterministic-fixturevalue-3379-deterministic-fixturevalue-3380-deterministic-fixturevalue-3382-deterministic-fixturevalue-3383-deterministic-fixturevalue-3384-deterministic-fixturevalue-3385-deterministic-fixturevalue-3386-deterministic-fixturevalue-3387-deterministic-fixturevalue-3389-deterministic-fixturevalue-3390-deterministic-fixturevalue-3391-deterministic-fixturevalue-3392-deterministic-fixturevalue-3393-deterministic-fixturevalue-3394-deterministic-fixturevalue-3396-deterministic-fixturevalue-3397-deterministic-fixturevalue-3398-deterministic-fixturevalue-3399-deterministic-fixturevalue-3400-deterministic-fixturevalue-3401-deterministic-fixturevalue-3403-deterministic-fixturevalue-3404-deterministic-fixturevalue-3405-deterministic-fixturevalue-3406-deterministic-fixturevalue-3407-deterministic-fixturevalue-3408-deterministic-fixturevalue-3410-deterministic-fixturevalue-3411-deterministic-fixturevalue-3412-deterministic-fixturevalue-3413-deterministic-fixturevalue-3414-deterministic-fixturevalue-3415-deterministic-fixturevalue-3417-deterministic-fixturevalue-3418-deterministic-fixturevalue-3419-deterministic-fixturevalue-3420-deterministic-fixturevalue-3421-deterministic-fixturevalue-3422-deterministic-fixturevalue-3424-deterministic-fixturevalue-3425-deterministic-fixturevalue-3426-deterministic-fixturevalue-3427-deterministic-fixturevalue-3428-deterministic-fixturevalue-3429-deterministic-fixturevalue-3431-deterministic-fixturevalue-3432-deterministic-fixturevalue-3433-deterministic-fixturevalue-3434-deterministic-fixturevalue-3435-deterministic-fixturevalue-3436-deterministic-fixturevalue-3438-deterministic-fixturevalue-3439-deterministic-fixturevalue-3440-deterministic-fixturevalue-3441-deterministic-fixturevalue-3442-deterministic-fixturevalue-3443-deterministic-fixturevalue-3445-deterministic-fixturevalue-3446-deterministic-fixturevalue-3447-deterministic-fixturevalue-3448-deterministic-fixturevalue-3449-deterministic-fixturevalue-3450-deterministic-fixturevalue-3452-deterministic-fixturevalue-3453-deterministic-fixturevalue-3454-deterministic-fixturevalue-3455-deterministic-fixture$DDd$Dd$Dd$DDd$Dd$Dd$DDd  $ D d  +$ +D +d + + + + + + $ D D d   $ D d  $ D d $DDd$Ddvalue-3456-deterministic-fixturevalue-3457-deterministic-fixturevalue-3459-deterministic-fixturevalue-3460-deterministic-fixturevalue-3461-deterministic-fixturevalue-3462-deterministic-fixturevalue-3463-deterministic-fixturevalue-3464-deterministic-fixturevalue-3466-deterministic-fixturevalue-3467-deterministic-fixturevalue-3468-deterministic-fixturevalue-3469-deterministic-fixturevalue-3470-deterministic-fixturevalue-3471-deterministic-fixturevalue-3473-deterministic-fixturevalue-3474-deterministic-fixturevalue-3475-deterministic-fixturevalue-3476-deterministic-fixturevalue-3477-deterministic-fixturevalue-3478-deterministic-fixturevalue-3480-deterministic-fixturevalue-3481-deterministic-fixturevalue-3482-deterministic-fixturevalue-3483-deterministic-fixturevalue-3484-deterministic-fixturevalue-3485-deterministic-fixturevalue-3487-deterministic-fixturevalue-3488-deterministic-fixturevalue-3489-deterministic-fixturevalue-3490-deterministic-fixturevalue-3491-deterministic-fixturevalue-3492-deterministic-fixturevalue-3494-deterministic-fixturevalue-3495-deterministic-fixturevalue-3496-deterministic-fixturevalue-3497-deterministic-fixturevalue-3498-deterministic-fixturevalue-3499-deterministic-fixturevalue-3501-deterministic-fixturevalue-3502-deterministic-fixturevalue-3503-deterministic-fixturevalue-3504-deterministic-fixturevalue-3505-deterministic-fixturevalue-3506-deterministic-fixturevalue-3508-deterministic-fixturevalue-3509-deterministic-fixturevalue-3510-deterministic-fixturevalue-3511-deterministic-fixturevalue-3512-deterministic-fixturevalue-3513-deterministic-fixturevalue-3515-deterministic-fixturevalue-3516-deterministic-fixturevalue-3517-deterministic-fixturevalue-3518-deterministic-fixturevalue-3519-deterministic-fixturevalue-3520-deterministic-fixturevalue-3522-deterministic-fixturevalue-3523-deterministic-fixturevalue-3524-deterministic-fixturevalue-3525-deterministic-fixturevalue-3526-deterministic-fixturevalue-3527-deterministic-fixturevalue-3529-deterministic-fixturevalue-3530-deterministic-fixturevalue-3531-deterministic-fixturevalue-3532-deterministic-fixturevalue-3533-deterministic-fixturevalue-3534-deterministic-fixturevalue-3536-deterministic-fixturevalue-3537-deterministic-fixturevalue-3538-deterministic-fixturevalue-3539-deterministic-fixturevalue-3540-deterministic-fixturevalue-3541-deterministic-fixturevalue-3543-deterministic-fixturevalue-3544-deterministic-fixturevalue-3545-deterministic-fixturevalue-3546-deterministic-fixturevalue-3547-deterministic-fixturevalue-3548-deterministic-fixturevalue-3550-deterministic-fixturevalue-3551-deterministic-fixturevalue-3552-deterministic-fixturevalue-3553-deterministic-fixturevalue-3554-deterministic-fixturevalue-3555-deterministic-fixturevalue-3557-deterministic-fixturevalue-3558-deterministic-fixturevalue-3559-deterministic-fixturevalue-3560-deterministic-fixturevalue-3561-deterministic-fixturevalue-3562-deterministic-fixturevalue-3564-deterministic-fixturevalue-3565-deterministic-fixturevalue-3566-deterministic-fixturevalue-3567-deterministic-fixturevalue-3568-deterministic-fixturevalue-3569-deterministic-fixturevalue-3571-deterministic-fixturevalue-3572-deterministic-fixturevalue-3573-deterministic-fixturevalue-3574-deterministic-fixturevalue-3575-deterministic-fixturevalue-3576-deterministic-fixturevalue-3578-deterministic-fixturevalue-3579-deterministic-fixturevalue-3580-deterministic-fixturevalue-3581-deterministic-fixturevalue-3582-deterministic-fixturevalue-3583-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-3585-deterministic-fixturevalue-3586-deterministic-fixturevalue-3587-deterministic-fixturevalue-3588-deterministic-fixturevalue-3589-deterministic-fixturevalue-3590-deterministic-fixturevalue-3592-deterministic-fixturevalue-3593-deterministic-fixturevalue-3594-deterministic-fixturevalue-3595-deterministic-fixturevalue-3596-deterministic-fixturevalue-3597-deterministic-fixturevalue-3599-deterministic-fixturevalue-3600-deterministic-fixturevalue-3601-deterministic-fixturevalue-3602-deterministic-fixturevalue-3603-deterministic-fixturevalue-3604-deterministic-fixturevalue-3606-deterministic-fixturevalue-3607-deterministic-fixturevalue-3608-deterministic-fixturevalue-3609-deterministic-fixturevalue-3610-deterministic-fixturevalue-3611-deterministic-fixturevalue-3613-deterministic-fixturevalue-3614-deterministic-fixturevalue-3615-deterministic-fixturevalue-3616-deterministic-fixturevalue-3617-deterministic-fixturevalue-3618-deterministic-fixturevalue-3620-deterministic-fixturevalue-3621-deterministic-fixturevalue-3622-deterministic-fixturevalue-3623-deterministic-fixturevalue-3624-deterministic-fixturevalue-3625-deterministic-fixturevalue-3627-deterministic-fixturevalue-3628-deterministic-fixturevalue-3629-deterministic-fixturevalue-3630-deterministic-fixturevalue-3631-deterministic-fixturevalue-3632-deterministic-fixturevalue-3634-deterministic-fixturevalue-3635-deterministic-fixturevalue-3636-deterministic-fixturevalue-3637-deterministic-fixturevalue-3638-deterministic-fixturevalue-3639-deterministic-fixturevalue-3641-deterministic-fixturevalue-3642-deterministic-fixturevalue-3643-deterministic-fixturevalue-3644-deterministic-fixturevalue-3645-deterministic-fixturevalue-3646-deterministic-fixturevalue-3648-deterministic-fixturevalue-3649-deterministic-fixturevalue-3650-deterministic-fixturevalue-3651-deterministic-fixturevalue-3652-deterministic-fixturevalue-3653-deterministic-fixturevalue-3655-deterministic-fixturevalue-3656-deterministic-fixturevalue-3657-deterministic-fixturevalue-3658-deterministic-fixturevalue-3659-deterministic-fixturevalue-3660-deterministic-fixturevalue-3662-deterministic-fixturevalue-3663-deterministic-fixturevalue-3664-deterministic-fixturevalue-3665-deterministic-fixturevalue-3666-deterministic-fixturevalue-3667-deterministic-fixturevalue-3669-deterministic-fixturevalue-3670-deterministic-fixturevalue-3671-deterministic-fixturevalue-3672-deterministic-fixturevalue-3673-deterministic-fixturevalue-3674-deterministic-fixturevalue-3676-deterministic-fixturevalue-3677-deterministic-fixturevalue-3678-deterministic-fixturevalue-3679-deterministic-fixturevalue-3680-deterministic-fixturevalue-3681-deterministic-fixturevalue-3683-deterministic-fixturevalue-3684-deterministic-fixturevalue-3685-deterministic-fixturevalue-3686-deterministic-fixturevalue-3687-deterministic-fixturevalue-3688-deterministic-fixturevalue-3690-deterministic-fixturevalue-3691-deterministic-fixturevalue-3692-deterministic-fixturevalue-3693-deterministic-fixturevalue-3694-deterministic-fixturevalue-3695-deterministic-fixturevalue-3697-deterministic-fixturevalue-3698-deterministic-fixturevalue-3699-deterministic-fixturevalue-3700-deterministic-fixturevalue-3701-deterministic-fixturevalue-3702-deterministic-fixturevalue-3704-deterministic-fixturevalue-3705-deterministic-fixturevalue-3706-deterministic-fixturevalue-3707-deterministic-fixturevalue-3708-deterministic-fixturevalue-3709-deterministic-fixturevalue-3711-deterministic-fixture$Dd$Ddd$$Dd$Dd$Ddd$$Dd$Dd $ D d d  +$ +$ +D +d + + + + + + $ D d  $ D d d  $ $ D d $Dd$Dddvalue-3712-deterministic-fixturevalue-3713-deterministic-fixturevalue-3714-deterministic-fixturevalue-3715-deterministic-fixturevalue-3716-deterministic-fixturevalue-3718-deterministic-fixturevalue-3719-deterministic-fixturevalue-3720-deterministic-fixturevalue-3721-deterministic-fixturevalue-3722-deterministic-fixturevalue-3723-deterministic-fixturevalue-3725-deterministic-fixturevalue-3726-deterministic-fixturevalue-3727-deterministic-fixturevalue-3728-deterministic-fixturevalue-3729-deterministic-fixturevalue-3730-deterministic-fixturevalue-3732-deterministic-fixturevalue-3733-deterministic-fixturevalue-3734-deterministic-fixturevalue-3735-deterministic-fixturevalue-3736-deterministic-fixturevalue-3737-deterministic-fixturevalue-3739-deterministic-fixturevalue-3740-deterministic-fixturevalue-3741-deterministic-fixturevalue-3742-deterministic-fixturevalue-3743-deterministic-fixturevalue-3744-deterministic-fixturevalue-3746-deterministic-fixturevalue-3747-deterministic-fixturevalue-3748-deterministic-fixturevalue-3749-deterministic-fixturevalue-3750-deterministic-fixturevalue-3751-deterministic-fixturevalue-3753-deterministic-fixturevalue-3754-deterministic-fixturevalue-3755-deterministic-fixturevalue-3756-deterministic-fixturevalue-3757-deterministic-fixturevalue-3758-deterministic-fixturevalue-3760-deterministic-fixturevalue-3761-deterministic-fixturevalue-3762-deterministic-fixturevalue-3763-deterministic-fixturevalue-3764-deterministic-fixturevalue-3765-deterministic-fixturevalue-3767-deterministic-fixturevalue-3768-deterministic-fixturevalue-3769-deterministic-fixturevalue-3770-deterministic-fixturevalue-3771-deterministic-fixturevalue-3772-deterministic-fixturevalue-3774-deterministic-fixturevalue-3775-deterministic-fixturevalue-3776-deterministic-fixturevalue-3777-deterministic-fixturevalue-3778-deterministic-fixturevalue-3779-deterministic-fixturevalue-3781-deterministic-fixturevalue-3782-deterministic-fixturevalue-3783-deterministic-fixturevalue-3784-deterministic-fixturevalue-3785-deterministic-fixturevalue-3786-deterministic-fixturevalue-3788-deterministic-fixturevalue-3789-deterministic-fixturevalue-3790-deterministic-fixturevalue-3791-deterministic-fixturevalue-3792-deterministic-fixturevalue-3793-deterministic-fixturevalue-3795-deterministic-fixturevalue-3796-deterministic-fixturevalue-3797-deterministic-fixturevalue-3798-deterministic-fixturevalue-3799-deterministic-fixturevalue-3800-deterministic-fixturevalue-3802-deterministic-fixturevalue-3803-deterministic-fixturevalue-3804-deterministic-fixturevalue-3805-deterministic-fixturevalue-3806-deterministic-fixturevalue-3807-deterministic-fixturevalue-3809-deterministic-fixturevalue-3810-deterministic-fixturevalue-3811-deterministic-fixturevalue-3812-deterministic-fixturevalue-3813-deterministic-fixturevalue-3814-deterministic-fixturevalue-3816-deterministic-fixturevalue-3817-deterministic-fixturevalue-3818-deterministic-fixturevalue-3819-deterministic-fixturevalue-3820-deterministic-fixturevalue-3821-deterministic-fixturevalue-3823-deterministic-fixturevalue-3824-deterministic-fixturevalue-3825-deterministic-fixturevalue-3826-deterministic-fixturevalue-3827-deterministic-fixturevalue-3828-deterministic-fixturevalue-3830-deterministic-fixturevalue-3831-deterministic-fixturevalue-3832-deterministic-fixturevalue-3833-deterministic-fixturevalue-3834-deterministic-fixturevalue-3835-deterministic-fixturevalue-3837-deterministic-fixturevalue-3838-deterministic-fixturevalue-3839-deterministic-fixture$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-3840-deterministic-fixturevalue-3841-deterministic-fixturevalue-3842-deterministic-fixturevalue-3844-deterministic-fixturevalue-3845-deterministic-fixturevalue-3846-deterministic-fixturevalue-3847-deterministic-fixturevalue-3848-deterministic-fixturevalue-3849-deterministic-fixturevalue-3851-deterministic-fixturevalue-3852-deterministic-fixturevalue-3853-deterministic-fixturevalue-3854-deterministic-fixturevalue-3855-deterministic-fixturevalue-3856-deterministic-fixturevalue-3858-deterministic-fixturevalue-3859-deterministic-fixturevalue-3860-deterministic-fixturevalue-3861-deterministic-fixturevalue-3862-deterministic-fixturevalue-3863-deterministic-fixturevalue-3865-deterministic-fixturevalue-3866-deterministic-fixturevalue-3867-deterministic-fixturevalue-3868-deterministic-fixturevalue-3869-deterministic-fixturevalue-3870-deterministic-fixturevalue-3872-deterministic-fixturevalue-3873-deterministic-fixturevalue-3874-deterministic-fixturevalue-3875-deterministic-fixturevalue-3876-deterministic-fixturevalue-3877-deterministic-fixturevalue-3879-deterministic-fixturevalue-3880-deterministic-fixturevalue-3881-deterministic-fixturevalue-3882-deterministic-fixturevalue-3883-deterministic-fixturevalue-3884-deterministic-fixturevalue-3886-deterministic-fixturevalue-3887-deterministic-fixturevalue-3888-deterministic-fixturevalue-3889-deterministic-fixturevalue-3890-deterministic-fixturevalue-3891-deterministic-fixturevalue-3893-deterministic-fixturevalue-3894-deterministic-fixturevalue-3895-deterministic-fixturevalue-3896-deterministic-fixturevalue-3897-deterministic-fixturevalue-3898-deterministic-fixturevalue-3900-deterministic-fixturevalue-3901-deterministic-fixturevalue-3902-deterministic-fixturevalue-3903-deterministic-fixturevalue-3904-deterministic-fixturevalue-3905-deterministic-fixturevalue-3907-deterministic-fixturevalue-3908-deterministic-fixturevalue-3909-deterministic-fixturevalue-3910-deterministic-fixturevalue-3911-deterministic-fixturevalue-3912-deterministic-fixturevalue-3914-deterministic-fixturevalue-3915-deterministic-fixturevalue-3916-deterministic-fixturevalue-3917-deterministic-fixturevalue-3918-deterministic-fixturevalue-3919-deterministic-fixturevalue-3921-deterministic-fixturevalue-3922-deterministic-fixturevalue-3923-deterministic-fixturevalue-3924-deterministic-fixturevalue-3925-deterministic-fixturevalue-3926-deterministic-fixturevalue-3928-deterministic-fixturevalue-3929-deterministic-fixturevalue-3930-deterministic-fixturevalue-3931-deterministic-fixturevalue-3932-deterministic-fixturevalue-3933-deterministic-fixturevalue-3935-deterministic-fixturevalue-3936-deterministic-fixturevalue-3937-deterministic-fixturevalue-3938-deterministic-fixturevalue-3939-deterministic-fixturevalue-3940-deterministic-fixturevalue-3942-deterministic-fixturevalue-3943-deterministic-fixturevalue-3944-deterministic-fixturevalue-3945-deterministic-fixturevalue-3946-deterministic-fixturevalue-3947-deterministic-fixturevalue-3949-deterministic-fixturevalue-3950-deterministic-fixturevalue-3951-deterministic-fixturevalue-3952-deterministic-fixturevalue-3953-deterministic-fixturevalue-3954-deterministic-fixturevalue-3956-deterministic-fixturevalue-3957-deterministic-fixturevalue-3958-deterministic-fixturevalue-3959-deterministic-fixturevalue-3960-deterministic-fixturevalue-3961-deterministic-fixturevalue-3963-deterministic-fixturevalue-3964-deterministic-fixturevalue-3965-deterministic-fixturevalue-3966-deterministic-fixturevalue-3967-deterministic-fixture$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-3968-deterministic-fixturevalue-3970-deterministic-fixturevalue-3971-deterministic-fixturevalue-3972-deterministic-fixturevalue-3973-deterministic-fixturevalue-3974-deterministic-fixturevalue-3975-deterministic-fixturevalue-3977-deterministic-fixturevalue-3978-deterministic-fixturevalue-3979-deterministic-fixturevalue-3980-deterministic-fixturevalue-3981-deterministic-fixturevalue-3982-deterministic-fixturevalue-3984-deterministic-fixturevalue-3985-deterministic-fixturevalue-3986-deterministic-fixturevalue-3987-deterministic-fixturevalue-3988-deterministic-fixturevalue-3989-deterministic-fixturevalue-3991-deterministic-fixturevalue-3992-deterministic-fixturevalue-3993-deterministic-fixturevalue-3994-deterministic-fixturevalue-3995-deterministic-fixturevalue-3996-deterministic-fixturevalue-3998-deterministic-fixturevalue-3999-deterministic-fixturevalue-4000-deterministic-fixturevalue-4001-deterministic-fixturevalue-4002-deterministic-fixturevalue-4003-deterministic-fixturevalue-4005-deterministic-fixturevalue-4006-deterministic-fixturevalue-4007-deterministic-fixturevalue-4008-deterministic-fixturevalue-4009-deterministic-fixturevalue-4010-deterministic-fixturevalue-4012-deterministic-fixturevalue-4013-deterministic-fixturevalue-4014-deterministic-fixturevalue-4015-deterministic-fixturevalue-4016-deterministic-fixturevalue-4017-deterministic-fixturevalue-4019-deterministic-fixturevalue-4020-deterministic-fixturevalue-4021-deterministic-fixturevalue-4022-deterministic-fixturevalue-4023-deterministic-fixturevalue-4024-deterministic-fixturevalue-4026-deterministic-fixturevalue-4027-deterministic-fixturevalue-4028-deterministic-fixturevalue-4029-deterministic-fixturevalue-4030-deterministic-fixturevalue-4031-deterministic-fixturevalue-4033-deterministic-fixturevalue-4034-deterministic-fixturevalue-4035-deterministic-fixturevalue-4036-deterministic-fixturevalue-4037-deterministic-fixturevalue-4038-deterministic-fixturevalue-4040-deterministic-fixturevalue-4041-deterministic-fixturevalue-4042-deterministic-fixturevalue-4043-deterministic-fixturevalue-4044-deterministic-fixturevalue-4045-deterministic-fixturevalue-4047-deterministic-fixturevalue-4048-deterministic-fixturevalue-4049-deterministic-fixturevalue-4050-deterministic-fixturevalue-4051-deterministic-fixturevalue-4052-deterministic-fixturevalue-4054-deterministic-fixturevalue-4055-deterministic-fixturevalue-4056-deterministic-fixturevalue-4057-deterministic-fixturevalue-4058-deterministic-fixturevalue-4059-deterministic-fixturevalue-4061-deterministic-fixturevalue-4062-deterministic-fixturevalue-4063-deterministic-fixturevalue-4064-deterministic-fixturevalue-4065-deterministic-fixturevalue-4066-deterministic-fixturevalue-4068-deterministic-fixturevalue-4069-deterministic-fixturevalue-4070-deterministic-fixturevalue-4071-deterministic-fixturevalue-4072-deterministic-fixturevalue-4073-deterministic-fixturevalue-4075-deterministic-fixturevalue-4076-deterministic-fixturevalue-4077-deterministic-fixturevalue-4078-deterministic-fixturevalue-4079-deterministic-fixturevalue-4080-deterministic-fixturevalue-4082-deterministic-fixturevalue-4083-deterministic-fixturevalue-4084-deterministic-fixturevalue-4085-deterministic-fixturevalue-4086-deterministic-fixturevalue-4087-deterministic-fixturevalue-4089-deterministic-fixturevalue-4090-deterministic-fixturevalue-4091-deterministic-fixturevalue-4092-deterministic-fixturevalue-4093-deterministic-fixturevalue-4094-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH::HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"Ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$dd @@ @H@ @@ @!@@@ @@ @!@@ " sL# $ $X $) 3 $&%  c 6L&i +X $@' $V( f L)#$v + * XS * 421QjdŜ924QrdŨz=27Qƴ|A2:Rd~D2=Rd̖I2@RdؖM2CRd䖄Q2FR𖈌U2LSdX2OSda2RSd&uь):tuLI;t&v Y;Cuv̱;su&ьɣA5]$j @E5`j!L̟M5c$j" DXQ5fU"#Vǰ4U*#vǼ5鎙SV2$6 V:6)V%8IVJ&9 WR&9CWZ:sWb';ɏWr((<鏽Wz(4e@2y&eԌ@2|eL—A2&e ×A2&׌ėA2&eLŗB2&e Ɨ@B2eƘB2&e܌ǘB2eLȘC2&e ɘYC 5yk ӀI5cy6ӌi5ykFӘ 5ykVӤ5y"k`Ӱɦ5#z*kvӼ香5SzȠ 5z:kԠ 5zJkI5zRki5{!$ +)$ L,I'$ - XC*-s-$. 0$  0"X3 0 6$!1 (3Sw& Y>w|̺)>w}L ?w&~ iY?x&̽?Cx&Ҍ?sxLɥ@x& Y@xӢj#FT5i$j$HY5l$j%LJ]5o$& a5r$j' Nd5uj(L,Pi5x$j* 8TXm5~j+Dq5$,PXy5j-LhZ|5$j. t\X< X)&@=IcX0L>iX*FX?X+Vd@ X+fpAɐ#Yp|BSY,ȈB Y-ȔC)Y-ȬD Y.ȸEiZ2eɘC2&eʘC2L˘YD2&e̘ęD2&e͘D2eLΘE2&e ϘYE2ϘΙE2&eИE2eLјF2&e 5s{bk 5{jk駹5{rk( 5|zk4)53|&@I5c|k6L 5|kFX5|kPp5|f|ɨ5#}kԈ 5"Dd2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2DH@ @@ @@ @H@ @@ @!@@@ @@ @ie(Vx +@jnԖ{> +jqզ|M@Jkt(} AjZkwAkC}(րBksMC +l( D[lӆ("p308*p4޴| +Ҁ*1' +ܘc17Nj*2GˊҜû*3W̪ܙ3gN#*4w +\S4 +Ӝ**ܚ5NJ*6j\;v;( +v ;vM"[ ;v$$;(0;*vM<*;(2v H,[;(:vT.%;(l);ȨRvMx2-;(Zv DM +posPNʽuo\OypӜhP +}ptQ*p3&߀R +qc +6ߌSjq@ߘSqÝV߰Tq*f߼Uʾr#2V +\8*pKک8*=pL۩8*>pMݩ\8>Nݩ8*qPީܟ8?qQNߩ8*@qR੤\8@S੦8*AqTᩨܠ8*qUN⩪8*Bv*a; Je;v, +h;v8m;CvDq;svPu;\};vt +;w뀲*օ;3 +w0댲j։;G똲D +l(&EJlc6MF + +m(F G mÒ(PHmfMIn#(v J[nS܆J +n(ݖK*n(MLJo(޶ Mj@oA8?~E8p@̨H8pAبM8ÚpBQ8ƚpC𨆼U8ɛEY8ϛpF\8қpGe8՛pH,i8؛I8m8ۜpJ6ѪӜs*7ܛ*N\ӽ8 +Ԁ*9 +ܜ39'NJc*0j\:G׊Ԁþ*;Wتܝ;gN +#*< +Մ4[1;Ψbv͐65;(jv89;( @=;רzvʹ<@;(v@E;(vM̱BI;(v ر@Q;樢vFU;(vHX;쨲vMJ];(r:V*rBWJsYjsRY +sCZZssb[ʿtj\꿽tӟz4\ +t@] +t36L^ju@XqV@8BqW㩮ܡ8CXN婰8+qY橲\8 Dq[8+Eq\窸ܢ8Eq]N誺8+Fq^骼\8+q_骾8+Gq`8 GqaN֍;wW뤲 +;"wg밲֕;#*wp뼲֙;S2wȲ +ם;Բ*ס;Bw +;Rwj׭;Zwױ;C׵;jw +;rw2"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"!@@ @@ H@ @@ʬUޝs,VǣЬNW@ӬX ,Y+3, NZKc,7[k@ɓ⬒G[,W\ 謓gN]#,^ Ĺ={йV=ܹX={Z={\> +|`> +| b >d>"|0f>*|<>2|Hj>@ / / _ / O / _/ / O / ^-O- ^ !- $ O'-^*-- 0O"g L⚚WgQ`iU#㪜wjYSj ]΃㺝k `㝠lKi8mkmDmqCPn tϣ\oy>C/|>F/|Ï>I|O>L/_>R/|>U/|ȏ>X|O>[/_>^|>a/|̏>d|O           ]ʃ^+ʳ,_ ,ak],aC,bsOc̣-d ^d  -e+3 7Ofk͓-GTl!>"B|`n$>%J|lp)>(R|x->+Z|v1>1x5>4j|z<>7z|A>:|~E>=̺I>@|غ _ O / @ / /O / @ / O 3-$6-&9O<-,^B.E-0HOK-4^N6Q-8T-Ohp }tq+3'rKc +7s ѓGsPtgu#2wvS:v+ҳBwKJxk嗢@>g|>j/Ϗ>m/}O>s/}>v/}>y/>|/}>/}>/}>/}>/   HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHwwHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @@ @@ @@ @@ @@!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHPHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH((value-4096-deterministic-fixtureHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  0HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + +id *int328 +Cname *string08R" +lance-encoding:compressionnone +items *list08 +item *int3208 +ecategory *dict:string:int8:false08BR. +&lance-encoding:dict-values-compressionnone +Bblob * large_binary08R +lance-encoding:blobtrue +)' +% +/lance.encodings.ColumnEncoding +G + +"53 +1 +/lance.encodings21.PageLayout +* 28HJ +  "53 +1 +/lance.encodings21.PageLayout +* 28H(J + "53 +1 +/lance.encodings21.PageLayout +* 28H(J + "53 +1 +/lance.encodings21.PageLayout +* 28H(G +"42 +0 +/lance.encodings21.PageLayout +  + 28H( +)' +% +/lance.encodings.ColumnEncoding +R +"?= +; +/lance.encodings21.PageLayout +* + + 28HU + "?= +; +/lance.encodings21.PageLayout +* + + 28H(U +"?= +; +/lance.encodings21.PageLayout +* + + 28H(U +"?= +; +/lance.encodings21.PageLayout +* + + 28H(K +0"86 +4 +/lance.encodings21.PageLayout + + + 28H( +)' +% +/lance.encodings.ColumnEncoding +f + .0"PN +L +/lance.encodings21.PageLayout+ +) + +" + +" +* 28@Hi + 20"PN +L +/lance.encodings21.PageLayout+ +) + +" + +" +* 28@H(i + 40"PN +L +/lance.encodings21.PageLayout+ +) + +" + +" +* 28@H(i + 50"PN +L +/lance.encodings21.PageLayout+ +) + +" + +" +* 28@H(Z + ("CA +? +/lance.encodings21.PageLayout + + +* + 28@H( +)' +% +/lance.encodings.ColumnEncoding +] + $"GE +C +/lance.encodings21.PageLayout" + **" + + (28H` + $"GE +C +/lance.encodings21.PageLayout" + **" + + (28H(` + $"GE +C +/lance.encodings21.PageLayout" + **" + + (28H(` + $"GE +C +/lance.encodings21.PageLayout" + **" + + (28H(W + $"@> +< +/lance.encodings21.PageLayout + +" + + (28H( +)' +% +/lance.encodings.ColumnEncoding +X +"EC +A +/lance.encodings21.PageLayout " + + b +@@ +28H[ +"EC +A +/lance.encodings21.PageLayout " + + b +@@ +28H([ +"EC +A +/lance.encodings21.PageLayout " + + b +@@ +28H([ +"EC +A +/lance.encodings21.PageLayout " + + b +@@ +28H(W +"DB +@ +/lance.encodings21.PageLayout" + + b +@@ +28H( (0) 2@G'wLANC \ No newline at end of file diff --git a/rust/lance-file/test_data/exact_versions/v2_2.lance b/rust/lance-file/test_data/exact_versions/v2_2.lance new file mode 100644 index 00000000000..683c06cd2fe --- /dev/null +++ b/rust/lance-file/test_data/exact_versions/v2_2.lance @@ -0,0 +1,633 @@ +blob-0000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-0999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +P" 2BRPbr  $P +( , 0 4P8<@DQH"L2PBTRQXb\r`dQhlptQx|` +`` +*`:aJaZajazb"b &b +*b .c 2c 6c:c>dB +dFdJ*dN:eRJeVZeZje^zfbfffjfngrgvgzg~0@pABC 0DpEFG0H pI$J(K,0L0pM4N8O<1P@qQDRHSL1TPqUTVXW\1X`qYdZh[l1\pq]t^x_|p@TpA#pB3pCCqDSTqEcqFsqGrHTrIrJrKsLTsMsNsOtPUtQ#tR3tSCuTSUuUcuVsuWvXUvYvZv[w\Uw]w^w_ 4t ++;K4[tk{"4&t*.246t:>B 5FuJ+N;RK5V[uZk^{b5fujnr5vuz~ !R""#2$B%RR&b'r()R*+,-R./01S2"324B5RS6b7r89S:袓;<=S>?h +h!h"*h#:i$Ji%Zi&ji'zj(j)j*j+k,k-k.k/l0 +l1l2*l3:m4Jm5Zm6jm7zn8n9n:n;oo?2`rabc2drefg2hrijk2lrmno3psqijrs3tsuԳvw3xsyz{3|s}~x`Vxa#xb3xcCydSVyecyfsygzhVzizjzk{lV{m{n{o|pW|q#|r3|sC}tSW}uc}vs}w~xW~y~z~{|W}~ 6v+;K6[vk{6v6v 7w+;K7[wk{7w꫷7wHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHg g ` HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-0001-deterministic-fixturevalue-0002-deterministic-fixturevalue-0003-deterministic-fixturevalue-0004-deterministic-fixturevalue-0005-deterministic-fixturevalue-0006-deterministic-fixturevalue-0008-deterministic-fixturevalue-0009-deterministic-fixturevalue-0010-deterministic-fixturevalue-0011-deterministic-fixturevalue-0012-deterministic-fixturevalue-0013-deterministic-fixturevalue-0015-deterministic-fixturevalue-0016-deterministic-fixturevalue-0017-deterministic-fixturevalue-0018-deterministic-fixturevalue-0019-deterministic-fixturevalue-0020-deterministic-fixturevalue-0022-deterministic-fixturevalue-0023-deterministic-fixturevalue-0024-deterministic-fixturevalue-0025-deterministic-fixturevalue-0026-deterministic-fixturevalue-0027-deterministic-fixturevalue-0029-deterministic-fixturevalue-0030-deterministic-fixturevalue-0031-deterministic-fixturevalue-0032-deterministic-fixturevalue-0033-deterministic-fixturevalue-0034-deterministic-fixturevalue-0036-deterministic-fixturevalue-0037-deterministic-fixturevalue-0038-deterministic-fixturevalue-0039-deterministic-fixturevalue-0040-deterministic-fixturevalue-0041-deterministic-fixturevalue-0043-deterministic-fixturevalue-0044-deterministic-fixturevalue-0045-deterministic-fixturevalue-0046-deterministic-fixturevalue-0047-deterministic-fixturevalue-0048-deterministic-fixturevalue-0050-deterministic-fixturevalue-0051-deterministic-fixturevalue-0052-deterministic-fixturevalue-0053-deterministic-fixturevalue-0054-deterministic-fixturevalue-0055-deterministic-fixturevalue-0057-deterministic-fixturevalue-0058-deterministic-fixturevalue-0059-deterministic-fixturevalue-0060-deterministic-fixturevalue-0061-deterministic-fixturevalue-0062-deterministic-fixturevalue-0064-deterministic-fixturevalue-0065-deterministic-fixturevalue-0066-deterministic-fixturevalue-0067-deterministic-fixturevalue-0068-deterministic-fixturevalue-0069-deterministic-fixturevalue-0071-deterministic-fixturevalue-0072-deterministic-fixturevalue-0073-deterministic-fixturevalue-0074-deterministic-fixturevalue-0075-deterministic-fixturevalue-0076-deterministic-fixturevalue-0078-deterministic-fixturevalue-0079-deterministic-fixturevalue-0080-deterministic-fixturevalue-0081-deterministic-fixturevalue-0082-deterministic-fixturevalue-0083-deterministic-fixturevalue-0085-deterministic-fixturevalue-0086-deterministic-fixturevalue-0087-deterministic-fixturevalue-0088-deterministic-fixturevalue-0089-deterministic-fixturevalue-0090-deterministic-fixturevalue-0092-deterministic-fixturevalue-0093-deterministic-fixturevalue-0094-deterministic-fixturevalue-0095-deterministic-fixturevalue-0096-deterministic-fixturevalue-0097-deterministic-fixturevalue-0099-deterministic-fixturevalue-0100-deterministic-fixturevalue-0101-deterministic-fixturevalue-0102-deterministic-fixturevalue-0103-deterministic-fixturevalue-0104-deterministic-fixturevalue-0106-deterministic-fixturevalue-0107-deterministic-fixturevalue-0108-deterministic-fixturevalue-0109-deterministic-fixturevalue-0110-deterministic-fixturevalue-0111-deterministic-fixturevalue-0113-deterministic-fixturevalue-0114-deterministic-fixturevalue-0115-deterministic-fixturevalue-0116-deterministic-fixturevalue-0117-deterministic-fixturevalue-0118-deterministic-fixturevalue-0120-deterministic-fixturevalue-0121-deterministic-fixturevalue-0122-deterministic-fixturevalue-0123-deterministic-fixturevalue-0124-deterministic-fixturevalue-0125-deterministic-fixturevalue-0127-deterministic-fixture$Dd$Ddd$$Dd$Dd$Ddd$$Dd$Dd $ D d d  +$ +$ +D +d + + + + + + $ D d  $ D d d  $ $ D d $Dd$Dddvalue-0128-deterministic-fixturevalue-0129-deterministic-fixturevalue-0130-deterministic-fixturevalue-0131-deterministic-fixturevalue-0132-deterministic-fixturevalue-0134-deterministic-fixturevalue-0135-deterministic-fixturevalue-0136-deterministic-fixturevalue-0137-deterministic-fixturevalue-0138-deterministic-fixturevalue-0139-deterministic-fixturevalue-0141-deterministic-fixturevalue-0142-deterministic-fixturevalue-0143-deterministic-fixturevalue-0144-deterministic-fixturevalue-0145-deterministic-fixturevalue-0146-deterministic-fixturevalue-0148-deterministic-fixturevalue-0149-deterministic-fixturevalue-0150-deterministic-fixturevalue-0151-deterministic-fixturevalue-0152-deterministic-fixturevalue-0153-deterministic-fixturevalue-0155-deterministic-fixturevalue-0156-deterministic-fixturevalue-0157-deterministic-fixturevalue-0158-deterministic-fixturevalue-0159-deterministic-fixturevalue-0160-deterministic-fixturevalue-0162-deterministic-fixturevalue-0163-deterministic-fixturevalue-0164-deterministic-fixturevalue-0165-deterministic-fixturevalue-0166-deterministic-fixturevalue-0167-deterministic-fixturevalue-0169-deterministic-fixturevalue-0170-deterministic-fixturevalue-0171-deterministic-fixturevalue-0172-deterministic-fixturevalue-0173-deterministic-fixturevalue-0174-deterministic-fixturevalue-0176-deterministic-fixturevalue-0177-deterministic-fixturevalue-0178-deterministic-fixturevalue-0179-deterministic-fixturevalue-0180-deterministic-fixturevalue-0181-deterministic-fixturevalue-0183-deterministic-fixturevalue-0184-deterministic-fixturevalue-0185-deterministic-fixturevalue-0186-deterministic-fixturevalue-0187-deterministic-fixturevalue-0188-deterministic-fixturevalue-0190-deterministic-fixturevalue-0191-deterministic-fixturevalue-0192-deterministic-fixturevalue-0193-deterministic-fixturevalue-0194-deterministic-fixturevalue-0195-deterministic-fixturevalue-0197-deterministic-fixturevalue-0198-deterministic-fixturevalue-0199-deterministic-fixturevalue-0200-deterministic-fixturevalue-0201-deterministic-fixturevalue-0202-deterministic-fixturevalue-0204-deterministic-fixturevalue-0205-deterministic-fixturevalue-0206-deterministic-fixturevalue-0207-deterministic-fixturevalue-0208-deterministic-fixturevalue-0209-deterministic-fixturevalue-0211-deterministic-fixturevalue-0212-deterministic-fixturevalue-0213-deterministic-fixturevalue-0214-deterministic-fixturevalue-0215-deterministic-fixturevalue-0216-deterministic-fixturevalue-0218-deterministic-fixturevalue-0219-deterministic-fixturevalue-0220-deterministic-fixturevalue-0221-deterministic-fixturevalue-0222-deterministic-fixturevalue-0223-deterministic-fixturevalue-0225-deterministic-fixturevalue-0226-deterministic-fixturevalue-0227-deterministic-fixturevalue-0228-deterministic-fixturevalue-0229-deterministic-fixturevalue-0230-deterministic-fixturevalue-0232-deterministic-fixturevalue-0233-deterministic-fixturevalue-0234-deterministic-fixturevalue-0235-deterministic-fixturevalue-0236-deterministic-fixturevalue-0237-deterministic-fixturevalue-0239-deterministic-fixturevalue-0240-deterministic-fixturevalue-0241-deterministic-fixturevalue-0242-deterministic-fixturevalue-0243-deterministic-fixturevalue-0244-deterministic-fixturevalue-0246-deterministic-fixturevalue-0247-deterministic-fixturevalue-0248-deterministic-fixturevalue-0249-deterministic-fixturevalue-0250-deterministic-fixturevalue-0251-deterministic-fixturevalue-0253-deterministic-fixturevalue-0254-deterministic-fixturevalue-0255-deterministic-fixture$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-0256-deterministic-fixturevalue-0257-deterministic-fixturevalue-0258-deterministic-fixturevalue-0260-deterministic-fixturevalue-0261-deterministic-fixturevalue-0262-deterministic-fixturevalue-0263-deterministic-fixturevalue-0264-deterministic-fixturevalue-0265-deterministic-fixturevalue-0267-deterministic-fixturevalue-0268-deterministic-fixturevalue-0269-deterministic-fixturevalue-0270-deterministic-fixturevalue-0271-deterministic-fixturevalue-0272-deterministic-fixturevalue-0274-deterministic-fixturevalue-0275-deterministic-fixturevalue-0276-deterministic-fixturevalue-0277-deterministic-fixturevalue-0278-deterministic-fixturevalue-0279-deterministic-fixturevalue-0281-deterministic-fixturevalue-0282-deterministic-fixturevalue-0283-deterministic-fixturevalue-0284-deterministic-fixturevalue-0285-deterministic-fixturevalue-0286-deterministic-fixturevalue-0288-deterministic-fixturevalue-0289-deterministic-fixturevalue-0290-deterministic-fixturevalue-0291-deterministic-fixturevalue-0292-deterministic-fixturevalue-0293-deterministic-fixturevalue-0295-deterministic-fixturevalue-0296-deterministic-fixturevalue-0297-deterministic-fixturevalue-0298-deterministic-fixturevalue-0299-deterministic-fixturevalue-0300-deterministic-fixturevalue-0302-deterministic-fixturevalue-0303-deterministic-fixturevalue-0304-deterministic-fixturevalue-0305-deterministic-fixturevalue-0306-deterministic-fixturevalue-0307-deterministic-fixturevalue-0309-deterministic-fixturevalue-0310-deterministic-fixturevalue-0311-deterministic-fixturevalue-0312-deterministic-fixturevalue-0313-deterministic-fixturevalue-0314-deterministic-fixturevalue-0316-deterministic-fixturevalue-0317-deterministic-fixturevalue-0318-deterministic-fixturevalue-0319-deterministic-fixturevalue-0320-deterministic-fixturevalue-0321-deterministic-fixturevalue-0323-deterministic-fixturevalue-0324-deterministic-fixturevalue-0325-deterministic-fixturevalue-0326-deterministic-fixturevalue-0327-deterministic-fixturevalue-0328-deterministic-fixturevalue-0330-deterministic-fixturevalue-0331-deterministic-fixturevalue-0332-deterministic-fixturevalue-0333-deterministic-fixturevalue-0334-deterministic-fixturevalue-0335-deterministic-fixturevalue-0337-deterministic-fixturevalue-0338-deterministic-fixturevalue-0339-deterministic-fixturevalue-0340-deterministic-fixturevalue-0341-deterministic-fixturevalue-0342-deterministic-fixturevalue-0344-deterministic-fixturevalue-0345-deterministic-fixturevalue-0346-deterministic-fixturevalue-0347-deterministic-fixturevalue-0348-deterministic-fixturevalue-0349-deterministic-fixturevalue-0351-deterministic-fixturevalue-0352-deterministic-fixturevalue-0353-deterministic-fixturevalue-0354-deterministic-fixturevalue-0355-deterministic-fixturevalue-0356-deterministic-fixturevalue-0358-deterministic-fixturevalue-0359-deterministic-fixturevalue-0360-deterministic-fixturevalue-0361-deterministic-fixturevalue-0362-deterministic-fixturevalue-0363-deterministic-fixturevalue-0365-deterministic-fixturevalue-0366-deterministic-fixturevalue-0367-deterministic-fixturevalue-0368-deterministic-fixturevalue-0369-deterministic-fixturevalue-0370-deterministic-fixturevalue-0372-deterministic-fixturevalue-0373-deterministic-fixturevalue-0374-deterministic-fixturevalue-0375-deterministic-fixturevalue-0376-deterministic-fixturevalue-0377-deterministic-fixturevalue-0379-deterministic-fixturevalue-0380-deterministic-fixturevalue-0381-deterministic-fixturevalue-0382-deterministic-fixturevalue-0383-deterministic-fixture$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-0384-deterministic-fixturevalue-0386-deterministic-fixturevalue-0387-deterministic-fixturevalue-0388-deterministic-fixturevalue-0389-deterministic-fixturevalue-0390-deterministic-fixturevalue-0391-deterministic-fixturevalue-0393-deterministic-fixturevalue-0394-deterministic-fixturevalue-0395-deterministic-fixturevalue-0396-deterministic-fixturevalue-0397-deterministic-fixturevalue-0398-deterministic-fixturevalue-0400-deterministic-fixturevalue-0401-deterministic-fixturevalue-0402-deterministic-fixturevalue-0403-deterministic-fixturevalue-0404-deterministic-fixturevalue-0405-deterministic-fixturevalue-0407-deterministic-fixturevalue-0408-deterministic-fixturevalue-0409-deterministic-fixturevalue-0410-deterministic-fixturevalue-0411-deterministic-fixturevalue-0412-deterministic-fixturevalue-0414-deterministic-fixturevalue-0415-deterministic-fixturevalue-0416-deterministic-fixturevalue-0417-deterministic-fixturevalue-0418-deterministic-fixturevalue-0419-deterministic-fixturevalue-0421-deterministic-fixturevalue-0422-deterministic-fixturevalue-0423-deterministic-fixturevalue-0424-deterministic-fixturevalue-0425-deterministic-fixturevalue-0426-deterministic-fixturevalue-0428-deterministic-fixturevalue-0429-deterministic-fixturevalue-0430-deterministic-fixturevalue-0431-deterministic-fixturevalue-0432-deterministic-fixturevalue-0433-deterministic-fixturevalue-0435-deterministic-fixturevalue-0436-deterministic-fixturevalue-0437-deterministic-fixturevalue-0438-deterministic-fixturevalue-0439-deterministic-fixturevalue-0440-deterministic-fixturevalue-0442-deterministic-fixturevalue-0443-deterministic-fixturevalue-0444-deterministic-fixturevalue-0445-deterministic-fixturevalue-0446-deterministic-fixturevalue-0447-deterministic-fixturevalue-0449-deterministic-fixturevalue-0450-deterministic-fixturevalue-0451-deterministic-fixturevalue-0452-deterministic-fixturevalue-0453-deterministic-fixturevalue-0454-deterministic-fixturevalue-0456-deterministic-fixturevalue-0457-deterministic-fixturevalue-0458-deterministic-fixturevalue-0459-deterministic-fixturevalue-0460-deterministic-fixturevalue-0461-deterministic-fixturevalue-0463-deterministic-fixturevalue-0464-deterministic-fixturevalue-0465-deterministic-fixturevalue-0466-deterministic-fixturevalue-0467-deterministic-fixturevalue-0468-deterministic-fixturevalue-0470-deterministic-fixturevalue-0471-deterministic-fixturevalue-0472-deterministic-fixturevalue-0473-deterministic-fixturevalue-0474-deterministic-fixturevalue-0475-deterministic-fixturevalue-0477-deterministic-fixturevalue-0478-deterministic-fixturevalue-0479-deterministic-fixturevalue-0480-deterministic-fixturevalue-0481-deterministic-fixturevalue-0482-deterministic-fixturevalue-0484-deterministic-fixturevalue-0485-deterministic-fixturevalue-0486-deterministic-fixturevalue-0487-deterministic-fixturevalue-0488-deterministic-fixturevalue-0489-deterministic-fixturevalue-0491-deterministic-fixturevalue-0492-deterministic-fixturevalue-0493-deterministic-fixturevalue-0494-deterministic-fixturevalue-0495-deterministic-fixturevalue-0496-deterministic-fixturevalue-0498-deterministic-fixturevalue-0499-deterministic-fixturevalue-0500-deterministic-fixturevalue-0501-deterministic-fixturevalue-0502-deterministic-fixturevalue-0503-deterministic-fixturevalue-0505-deterministic-fixturevalue-0506-deterministic-fixturevalue-0507-deterministic-fixturevalue-0508-deterministic-fixturevalue-0509-deterministic-fixturevalue-0510-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-0512-deterministic-fixturevalue-0513-deterministic-fixturevalue-0514-deterministic-fixturevalue-0515-deterministic-fixturevalue-0516-deterministic-fixturevalue-0517-deterministic-fixturevalue-0519-deterministic-fixturevalue-0520-deterministic-fixturevalue-0521-deterministic-fixturevalue-0522-deterministic-fixturevalue-0523-deterministic-fixturevalue-0524-deterministic-fixturevalue-0526-deterministic-fixturevalue-0527-deterministic-fixturevalue-0528-deterministic-fixturevalue-0529-deterministic-fixturevalue-0530-deterministic-fixturevalue-0531-deterministic-fixturevalue-0533-deterministic-fixturevalue-0534-deterministic-fixturevalue-0535-deterministic-fixturevalue-0536-deterministic-fixturevalue-0537-deterministic-fixturevalue-0538-deterministic-fixturevalue-0540-deterministic-fixturevalue-0541-deterministic-fixturevalue-0542-deterministic-fixturevalue-0543-deterministic-fixturevalue-0544-deterministic-fixturevalue-0545-deterministic-fixturevalue-0547-deterministic-fixturevalue-0548-deterministic-fixturevalue-0549-deterministic-fixturevalue-0550-deterministic-fixturevalue-0551-deterministic-fixturevalue-0552-deterministic-fixturevalue-0554-deterministic-fixturevalue-0555-deterministic-fixturevalue-0556-deterministic-fixturevalue-0557-deterministic-fixturevalue-0558-deterministic-fixturevalue-0559-deterministic-fixturevalue-0561-deterministic-fixturevalue-0562-deterministic-fixturevalue-0563-deterministic-fixturevalue-0564-deterministic-fixturevalue-0565-deterministic-fixturevalue-0566-deterministic-fixturevalue-0568-deterministic-fixturevalue-0569-deterministic-fixturevalue-0570-deterministic-fixturevalue-0571-deterministic-fixturevalue-0572-deterministic-fixturevalue-0573-deterministic-fixturevalue-0575-deterministic-fixturevalue-0576-deterministic-fixturevalue-0577-deterministic-fixturevalue-0578-deterministic-fixturevalue-0579-deterministic-fixturevalue-0580-deterministic-fixturevalue-0582-deterministic-fixturevalue-0583-deterministic-fixturevalue-0584-deterministic-fixturevalue-0585-deterministic-fixturevalue-0586-deterministic-fixturevalue-0587-deterministic-fixturevalue-0589-deterministic-fixturevalue-0590-deterministic-fixturevalue-0591-deterministic-fixturevalue-0592-deterministic-fixturevalue-0593-deterministic-fixturevalue-0594-deterministic-fixturevalue-0596-deterministic-fixturevalue-0597-deterministic-fixturevalue-0598-deterministic-fixturevalue-0599-deterministic-fixturevalue-0600-deterministic-fixturevalue-0601-deterministic-fixturevalue-0603-deterministic-fixturevalue-0604-deterministic-fixturevalue-0605-deterministic-fixturevalue-0606-deterministic-fixturevalue-0607-deterministic-fixturevalue-0608-deterministic-fixturevalue-0610-deterministic-fixturevalue-0611-deterministic-fixturevalue-0612-deterministic-fixturevalue-0613-deterministic-fixturevalue-0614-deterministic-fixturevalue-0615-deterministic-fixturevalue-0617-deterministic-fixturevalue-0618-deterministic-fixturevalue-0619-deterministic-fixturevalue-0620-deterministic-fixturevalue-0621-deterministic-fixturevalue-0622-deterministic-fixturevalue-0624-deterministic-fixturevalue-0625-deterministic-fixturevalue-0626-deterministic-fixturevalue-0627-deterministic-fixturevalue-0628-deterministic-fixturevalue-0629-deterministic-fixturevalue-0631-deterministic-fixturevalue-0632-deterministic-fixturevalue-0633-deterministic-fixturevalue-0634-deterministic-fixturevalue-0635-deterministic-fixturevalue-0636-deterministic-fixturevalue-0638-deterministic-fixturevalue-0639-deterministic-fixture$Dd$DDd$Dd$Dd$DDd$Dd$Dd $ D D d  + +$ +D +d + + + + + + $ D d  $ D D d   $ D d $Dd$DDdvalue-0640-deterministic-fixturevalue-0641-deterministic-fixturevalue-0642-deterministic-fixturevalue-0643-deterministic-fixturevalue-0645-deterministic-fixturevalue-0646-deterministic-fixturevalue-0647-deterministic-fixturevalue-0648-deterministic-fixturevalue-0649-deterministic-fixturevalue-0650-deterministic-fixturevalue-0652-deterministic-fixturevalue-0653-deterministic-fixturevalue-0654-deterministic-fixturevalue-0655-deterministic-fixturevalue-0656-deterministic-fixturevalue-0657-deterministic-fixturevalue-0659-deterministic-fixturevalue-0660-deterministic-fixturevalue-0661-deterministic-fixturevalue-0662-deterministic-fixturevalue-0663-deterministic-fixturevalue-0664-deterministic-fixturevalue-0666-deterministic-fixturevalue-0667-deterministic-fixturevalue-0668-deterministic-fixturevalue-0669-deterministic-fixturevalue-0670-deterministic-fixturevalue-0671-deterministic-fixturevalue-0673-deterministic-fixturevalue-0674-deterministic-fixturevalue-0675-deterministic-fixturevalue-0676-deterministic-fixturevalue-0677-deterministic-fixturevalue-0678-deterministic-fixturevalue-0680-deterministic-fixturevalue-0681-deterministic-fixturevalue-0682-deterministic-fixturevalue-0683-deterministic-fixturevalue-0684-deterministic-fixturevalue-0685-deterministic-fixturevalue-0687-deterministic-fixturevalue-0688-deterministic-fixturevalue-0689-deterministic-fixturevalue-0690-deterministic-fixturevalue-0691-deterministic-fixturevalue-0692-deterministic-fixturevalue-0694-deterministic-fixturevalue-0695-deterministic-fixturevalue-0696-deterministic-fixturevalue-0697-deterministic-fixturevalue-0698-deterministic-fixturevalue-0699-deterministic-fixturevalue-0701-deterministic-fixturevalue-0702-deterministic-fixturevalue-0703-deterministic-fixturevalue-0704-deterministic-fixturevalue-0705-deterministic-fixturevalue-0706-deterministic-fixturevalue-0708-deterministic-fixturevalue-0709-deterministic-fixturevalue-0710-deterministic-fixturevalue-0711-deterministic-fixturevalue-0712-deterministic-fixturevalue-0713-deterministic-fixturevalue-0715-deterministic-fixturevalue-0716-deterministic-fixturevalue-0717-deterministic-fixturevalue-0718-deterministic-fixturevalue-0719-deterministic-fixturevalue-0720-deterministic-fixturevalue-0722-deterministic-fixturevalue-0723-deterministic-fixturevalue-0724-deterministic-fixturevalue-0725-deterministic-fixturevalue-0726-deterministic-fixturevalue-0727-deterministic-fixturevalue-0729-deterministic-fixturevalue-0730-deterministic-fixturevalue-0731-deterministic-fixturevalue-0732-deterministic-fixturevalue-0733-deterministic-fixturevalue-0734-deterministic-fixturevalue-0736-deterministic-fixturevalue-0737-deterministic-fixturevalue-0738-deterministic-fixturevalue-0739-deterministic-fixturevalue-0740-deterministic-fixturevalue-0741-deterministic-fixturevalue-0743-deterministic-fixturevalue-0744-deterministic-fixturevalue-0745-deterministic-fixturevalue-0746-deterministic-fixturevalue-0747-deterministic-fixturevalue-0748-deterministic-fixturevalue-0750-deterministic-fixturevalue-0751-deterministic-fixturevalue-0752-deterministic-fixturevalue-0753-deterministic-fixturevalue-0754-deterministic-fixturevalue-0755-deterministic-fixturevalue-0757-deterministic-fixturevalue-0758-deterministic-fixturevalue-0759-deterministic-fixturevalue-0760-deterministic-fixturevalue-0761-deterministic-fixturevalue-0762-deterministic-fixturevalue-0764-deterministic-fixturevalue-0765-deterministic-fixturevalue-0766-deterministic-fixturevalue-0767-deterministic-fixture$DDd$Dd$Dd$DDd$Dd$Dd$DDd  $ D d  +$ +D +d + + + + + + $ D D d   $ D d  $ D d $DDd$Ddvalue-0768-deterministic-fixturevalue-0769-deterministic-fixturevalue-0771-deterministic-fixturevalue-0772-deterministic-fixturevalue-0773-deterministic-fixturevalue-0774-deterministic-fixturevalue-0775-deterministic-fixturevalue-0776-deterministic-fixturevalue-0778-deterministic-fixturevalue-0779-deterministic-fixturevalue-0780-deterministic-fixturevalue-0781-deterministic-fixturevalue-0782-deterministic-fixturevalue-0783-deterministic-fixturevalue-0785-deterministic-fixturevalue-0786-deterministic-fixturevalue-0787-deterministic-fixturevalue-0788-deterministic-fixturevalue-0789-deterministic-fixturevalue-0790-deterministic-fixturevalue-0792-deterministic-fixturevalue-0793-deterministic-fixturevalue-0794-deterministic-fixturevalue-0795-deterministic-fixturevalue-0796-deterministic-fixturevalue-0797-deterministic-fixturevalue-0799-deterministic-fixturevalue-0800-deterministic-fixturevalue-0801-deterministic-fixturevalue-0802-deterministic-fixturevalue-0803-deterministic-fixturevalue-0804-deterministic-fixturevalue-0806-deterministic-fixturevalue-0807-deterministic-fixturevalue-0808-deterministic-fixturevalue-0809-deterministic-fixturevalue-0810-deterministic-fixturevalue-0811-deterministic-fixturevalue-0813-deterministic-fixturevalue-0814-deterministic-fixturevalue-0815-deterministic-fixturevalue-0816-deterministic-fixturevalue-0817-deterministic-fixturevalue-0818-deterministic-fixturevalue-0820-deterministic-fixturevalue-0821-deterministic-fixturevalue-0822-deterministic-fixturevalue-0823-deterministic-fixturevalue-0824-deterministic-fixturevalue-0825-deterministic-fixturevalue-0827-deterministic-fixturevalue-0828-deterministic-fixturevalue-0829-deterministic-fixturevalue-0830-deterministic-fixturevalue-0831-deterministic-fixturevalue-0832-deterministic-fixturevalue-0834-deterministic-fixturevalue-0835-deterministic-fixturevalue-0836-deterministic-fixturevalue-0837-deterministic-fixturevalue-0838-deterministic-fixturevalue-0839-deterministic-fixturevalue-0841-deterministic-fixturevalue-0842-deterministic-fixturevalue-0843-deterministic-fixturevalue-0844-deterministic-fixturevalue-0845-deterministic-fixturevalue-0846-deterministic-fixturevalue-0848-deterministic-fixturevalue-0849-deterministic-fixturevalue-0850-deterministic-fixturevalue-0851-deterministic-fixturevalue-0852-deterministic-fixturevalue-0853-deterministic-fixturevalue-0855-deterministic-fixturevalue-0856-deterministic-fixturevalue-0857-deterministic-fixturevalue-0858-deterministic-fixturevalue-0859-deterministic-fixturevalue-0860-deterministic-fixturevalue-0862-deterministic-fixturevalue-0863-deterministic-fixturevalue-0864-deterministic-fixturevalue-0865-deterministic-fixturevalue-0866-deterministic-fixturevalue-0867-deterministic-fixturevalue-0869-deterministic-fixturevalue-0870-deterministic-fixturevalue-0871-deterministic-fixturevalue-0872-deterministic-fixturevalue-0873-deterministic-fixturevalue-0874-deterministic-fixturevalue-0876-deterministic-fixturevalue-0877-deterministic-fixturevalue-0878-deterministic-fixturevalue-0879-deterministic-fixturevalue-0880-deterministic-fixturevalue-0881-deterministic-fixturevalue-0883-deterministic-fixturevalue-0884-deterministic-fixturevalue-0885-deterministic-fixturevalue-0886-deterministic-fixturevalue-0887-deterministic-fixturevalue-0888-deterministic-fixturevalue-0890-deterministic-fixturevalue-0891-deterministic-fixturevalue-0892-deterministic-fixturevalue-0893-deterministic-fixturevalue-0894-deterministic-fixturevalue-0895-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-0897-deterministic-fixturevalue-0898-deterministic-fixturevalue-0899-deterministic-fixturevalue-0900-deterministic-fixturevalue-0901-deterministic-fixturevalue-0902-deterministic-fixturevalue-0904-deterministic-fixturevalue-0905-deterministic-fixturevalue-0906-deterministic-fixturevalue-0907-deterministic-fixturevalue-0908-deterministic-fixturevalue-0909-deterministic-fixturevalue-0911-deterministic-fixturevalue-0912-deterministic-fixturevalue-0913-deterministic-fixturevalue-0914-deterministic-fixturevalue-0915-deterministic-fixturevalue-0916-deterministic-fixturevalue-0918-deterministic-fixturevalue-0919-deterministic-fixturevalue-0920-deterministic-fixturevalue-0921-deterministic-fixturevalue-0922-deterministic-fixturevalue-0923-deterministic-fixturevalue-0925-deterministic-fixturevalue-0926-deterministic-fixturevalue-0927-deterministic-fixturevalue-0928-deterministic-fixturevalue-0929-deterministic-fixturevalue-0930-deterministic-fixturevalue-0932-deterministic-fixturevalue-0933-deterministic-fixturevalue-0934-deterministic-fixturevalue-0935-deterministic-fixturevalue-0936-deterministic-fixturevalue-0937-deterministic-fixturevalue-0939-deterministic-fixturevalue-0940-deterministic-fixturevalue-0941-deterministic-fixturevalue-0942-deterministic-fixturevalue-0943-deterministic-fixturevalue-0944-deterministic-fixturevalue-0946-deterministic-fixturevalue-0947-deterministic-fixturevalue-0948-deterministic-fixturevalue-0949-deterministic-fixturevalue-0950-deterministic-fixturevalue-0951-deterministic-fixturevalue-0953-deterministic-fixturevalue-0954-deterministic-fixturevalue-0955-deterministic-fixturevalue-0956-deterministic-fixturevalue-0957-deterministic-fixturevalue-0958-deterministic-fixturevalue-0960-deterministic-fixturevalue-0961-deterministic-fixturevalue-0962-deterministic-fixturevalue-0963-deterministic-fixturevalue-0964-deterministic-fixturevalue-0965-deterministic-fixturevalue-0967-deterministic-fixturevalue-0968-deterministic-fixturevalue-0969-deterministic-fixturevalue-0970-deterministic-fixturevalue-0971-deterministic-fixturevalue-0972-deterministic-fixturevalue-0974-deterministic-fixturevalue-0975-deterministic-fixturevalue-0976-deterministic-fixturevalue-0977-deterministic-fixturevalue-0978-deterministic-fixturevalue-0979-deterministic-fixturevalue-0981-deterministic-fixturevalue-0982-deterministic-fixturevalue-0983-deterministic-fixturevalue-0984-deterministic-fixturevalue-0985-deterministic-fixturevalue-0986-deterministic-fixturevalue-0988-deterministic-fixturevalue-0989-deterministic-fixturevalue-0990-deterministic-fixturevalue-0991-deterministic-fixturevalue-0992-deterministic-fixturevalue-0993-deterministic-fixturevalue-0995-deterministic-fixturevalue-0996-deterministic-fixturevalue-0997-deterministic-fixturevalue-0998-deterministic-fixturevalue-0999-deterministic-fixturevalue-1000-deterministic-fixturevalue-1002-deterministic-fixturevalue-1003-deterministic-fixturevalue-1004-deterministic-fixturevalue-1005-deterministic-fixturevalue-1006-deterministic-fixturevalue-1007-deterministic-fixturevalue-1009-deterministic-fixturevalue-1010-deterministic-fixturevalue-1011-deterministic-fixturevalue-1012-deterministic-fixturevalue-1013-deterministic-fixturevalue-1014-deterministic-fixturevalue-1016-deterministic-fixturevalue-1017-deterministic-fixturevalue-1018-deterministic-fixturevalue-1019-deterministic-fixturevalue-1020-deterministic-fixturevalue-1021-deterministic-fixturevalue-1023-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJJ HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#F$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I@ @@ @!@@@ @@ @!@@ H@ @@ h/FAG0H @0H 01I@ KPC2K`3L @3AM +X4NC pNA#4ҋ`Nvq#8 bTv #@c`w"# efw$$Hfl#x&1$L" hrCx(a$P2ixy*$TBl~z,$P nz.$\ro{0Q%` qh{0()H8MRld{h)KDRp$|4)NP St|6)Q\MS||8)TtSD}:)W } *Z T~> *]MT d~@I*`T"~D!i*fT#$)*i8Wpب;L@X;NHBYPPZ=RX[=T`B\ >Vp]DPXx]h?Z^?\B_ @^` E B L'RX'b Úd!(r|Q(ق (ݐ#(ࢍD( + d) ҍѤq) ěġ)  ,$NX dI,'XĄi,*$,-YЄ ,0NY",3YD$,6Y& -90H )-<@NZ`d*(@-?PZxć.0-E[52Od#56APC68QpCc6:Qt7qT|@Æ8@T9BтUDBV":FaW2@c:H{ %r1|ȍ%D t|%uA } & wQ)} &dxYI~& z`&$ }i&~q(N' yɀ4'D@'Ť$H*l0U%J*o@U&LB+r@V'N+u`(+xpV)(RB+{V*XT+~@W+pV+W-+.\B+W/^@b)` &d*a ,Af1*b"!2DBha*c0!8dCj*dB!>l*eR"DDn*fb"JDp!+f"PEr+g"V$Et+h#\Dx+i􄈕-䈖-[E-[ +. \ *.O\ eJ.$Ŋ!j.0\%.<]1.HO]勤9.T!A +/"D$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$d @@ @H@ @@ @!@@@ @@ @!@@  rh4yQOk5zOn{OqD5|Otd5}Pw5~APzP}5ހP6߂Q$61Q#D6@4b`h4` f@5!a@s @6QalT 7and %A8"apt EA9Rar &eA:b FA;Abv f=qbz A>Bb| A?rbr",&L2r$%,&GXLbr&5,&gXLr*E,&XL„s,U,&§Xs.e,&M"s0u,&XMRs,&YMt4,&'Yt6,&GYMt8,&ԇY7퐡Sǰ7S"ʳ8T$ж$81T&#ӹaT(Cּ8TPٿ8T,c8!U0sQU29U$9UAe!FCLqe!CMe!CNBe"DOrf"P1f#"EDQaf0"&eDS2f C"FDTbf4S"fDUfDs"DVQgT"'[OvR-''[wV-'OwX-'h[Ow-'[OBw\-( +[rx^-( [Px`-(P҉xb.(\Px .(H\Pbxf5.(h\aQ3⅑Q +C6QP6Qc6!Rs6QR$7花RD7Rd7S7ASqS!%B@1c#!Aac3!eBBc@!BC2c$S!FBDbc@c!̆BE!dTs!ϦFQdd!CH"dt!%CIRd!ECJd!&KMBt:,&קYMrt,'YNu@,'Y҆uB-'NuD-''ZN2u -'GZNbuH5-'gZvJE-'ZN‡vLU-'OvNu-'ZORv-6D9U8d9V:AV<ڤ9qV9V@:WB1WF#D:aW0d:WJC:WLS :EWgd"%EX"gp"EEYRg" Zh"E[Ah"gE\qh"E^Bh"E_rh#`i$# FFabi0##fFbixhE.(PŠylU.("\Py`.(%\Q"ypu.((\Ryr.(+]Qzt.(.Qzv.(1H]Qz.(4h]Qzz.(7]rz|.):R{~.)=]$ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ @ @@ H@ @@@ @ cM1cNҕ2P2(dQ2 2HdRbE2܈dS–U2Te2dU"p2dVR2eW2(eXCV7Kos7koZ7 Cӧ\8 + ps^8 )p3`&8 Ipc68+ip3f'F8K cèh7V8 pjGp8pSP8"={={={={={>|>|>|>>|>|`3,ff#v32fg S35 gh +38Igi 3;!igj 3>$gkC3A*gls3D- m 3G0gnӛ4J3 ho$4MF9F+r3ë'P9I rc7f9Lkrœ#Pv9OrSg9R +#w9U *sS9[ Jsʃ9^+js˳9aK +s9dks9g sC~??~?~?~??????2Y◲2 he[2e\B2e]r2^Ә3 f_3)f`3&3 Ifac63# ifb +F3& cÙ V3)fd +qng8% +#pw8(JqSr8+ jqt8.KqC81kqsx84 +C|8: qsӪ~9= +r&9@Jrc69C +>|>|>>}>}>}>>}>}?~?6)hq3&&4S9Ihrc(64V< s*V4Y?ht`4\Bhu#.v4_EhvS24bK w44eN)ix4hQIiy84kTiiz:4nWi|Cӭ:j +ts:m*tУ3&:p +c6:s jt@:v tîV:| tf: t#: +u: *u: Ju: ??HHHHHHHHHHHHHHHHHHHHHHHHxwHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@ @@ @@ @@ @@ aaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-1999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH $@ d@@@$$A,dA4A?%HA eHCHE+HG;%IIKeIK[IMkIO{%JQeJSJUJW%KYeK[ۥK]K_%La eLcLe+Lg;%MiKeMk[MmkMo{%NqeNsNuNw%OyeO{ۥO}ObPPQ6Q6$mD64mɣFR6DmHS6S6+GPre+/@Gr+kGr%,/Gs,˹GsE-/Gs-Gt.+GPte.KH. +k Ht %/0Hu `e: +#0(ve< S0)e$= 0*e*? 0%+e6@I0)e:pWz@pW{A qW'{C cqW7{Dk:“qWG{F:qXW{Iqe//m"/q(/y4/:/@/F/L/R/ G .U +gN ǵn! ''*U-G 0"3X'\K*;cX0^k2ʓXG_XWaBXgb J;#Xd R̃Xe+Z;ͳYgKYj jYǸkr;C(Yt|/t|/t|/t|/t|/u|/u|/u|/u|/v|/v|/-TfMm&F.Ug@ n ' +XaJ XiLҺ8XlM:DXqOPXuP\XyRhX|S:tX́UX̅V +X̉YX͍[g{X/#rw{^/ Sr{d/ r{j/+r{v/Kr{|/ks{/ss{/s{/s|/ t|/ 36&9(<*?,B0H2K4N6QT @YUŝgNLYǸXY̝;dYU繢pY՝G;|YYݝ.Yg;NYǻʻ`Y'msv|nУv|pv}q w}s+3w0}tkғwG}vwW}w wg}z#xp}|Sx}} Ճx///HHHHHHHHHHHHHHHHHHHHHHHHwwHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @@ @@ @@ @@ @@aaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaaHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-2999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH (8HXhx + + Ȉ ؈ (8HXhxȉ؉ +)*9:IJYZijyz + ɘ ٘   +)*9:IJYZijyzəٙ +;K[k{ + ˸ ۸븨 +;K[k{˹۹빩@@AAB(BC8CDHDEXEFhFGxGHHIIJJKKLȌLM،MNNOOPPQQR(RS8STHTUXUVhVWxWXXYYZZ[[\ȍ\]؍]^^__ @ +A)B*9C:IDJYEZiFjyGzHIJKɜLٜMNO P +Q)R*9S:ITJYUZiVjyWzXYZ[ɝ\ٝ]^_@ AB+C;DKE[FkG{HIJKL˼MۼN뼬OP QR+S;TKU[VkW{XYZ[\˽]۽^뽭_  !!"("#8#$H$%X%&h&'x'(())**++,Ȋ,-؊-..//00112(23834H45X56h67x78899::;;<ȋ<=؋=>>?? +!)"*9#:I$JY%Zi&jy'z()*+ɚ,ٚ-./ 0 +1)2*93:I4JY5Zi6jy7z89:;ɛ<ٛ=>? !"+#;$K%[&k'{()*+,˺-ۺ.뺪/0 12+3;4K5[6k7{89:;<˻=ۻ>뻫?``aab(bc8cdHdeXefhfgxghhiijjkklȎlm؎mnnooppqqr(rs8stHtuXuvhvwxwxxyyzz{{|ȏ|}؏}~~ ` +a)b*9c:IdJYeZifjygzhijkɞlٞmno p +q)r*9s:ItJYuZivjywzxyz{ɟ|ٟ}~` ab+c;dKe[fkg{hijkl˾m۾n뾮op qr+s;tKu[vkw{xyz{|˿}ۿ~뿯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH g g HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-2048-deterministic-fixturevalue-2049-deterministic-fixturevalue-2050-deterministic-fixturevalue-2052-deterministic-fixturevalue-2053-deterministic-fixturevalue-2054-deterministic-fixturevalue-2055-deterministic-fixturevalue-2056-deterministic-fixturevalue-2057-deterministic-fixturevalue-2059-deterministic-fixturevalue-2060-deterministic-fixturevalue-2061-deterministic-fixturevalue-2062-deterministic-fixturevalue-2063-deterministic-fixturevalue-2064-deterministic-fixturevalue-2066-deterministic-fixturevalue-2067-deterministic-fixturevalue-2068-deterministic-fixturevalue-2069-deterministic-fixturevalue-2070-deterministic-fixturevalue-2071-deterministic-fixturevalue-2073-deterministic-fixturevalue-2074-deterministic-fixturevalue-2075-deterministic-fixturevalue-2076-deterministic-fixturevalue-2077-deterministic-fixturevalue-2078-deterministic-fixturevalue-2080-deterministic-fixturevalue-2081-deterministic-fixturevalue-2082-deterministic-fixturevalue-2083-deterministic-fixturevalue-2084-deterministic-fixturevalue-2085-deterministic-fixturevalue-2087-deterministic-fixturevalue-2088-deterministic-fixturevalue-2089-deterministic-fixturevalue-2090-deterministic-fixturevalue-2091-deterministic-fixturevalue-2092-deterministic-fixturevalue-2094-deterministic-fixturevalue-2095-deterministic-fixturevalue-2096-deterministic-fixturevalue-2097-deterministic-fixturevalue-2098-deterministic-fixturevalue-2099-deterministic-fixturevalue-2101-deterministic-fixturevalue-2102-deterministic-fixturevalue-2103-deterministic-fixturevalue-2104-deterministic-fixturevalue-2105-deterministic-fixturevalue-2106-deterministic-fixturevalue-2108-deterministic-fixturevalue-2109-deterministic-fixturevalue-2110-deterministic-fixturevalue-2111-deterministic-fixturevalue-2112-deterministic-fixturevalue-2113-deterministic-fixturevalue-2115-deterministic-fixturevalue-2116-deterministic-fixturevalue-2117-deterministic-fixturevalue-2118-deterministic-fixturevalue-2119-deterministic-fixturevalue-2120-deterministic-fixturevalue-2122-deterministic-fixturevalue-2123-deterministic-fixturevalue-2124-deterministic-fixturevalue-2125-deterministic-fixturevalue-2126-deterministic-fixturevalue-2127-deterministic-fixturevalue-2129-deterministic-fixturevalue-2130-deterministic-fixturevalue-2131-deterministic-fixturevalue-2132-deterministic-fixturevalue-2133-deterministic-fixturevalue-2134-deterministic-fixturevalue-2136-deterministic-fixturevalue-2137-deterministic-fixturevalue-2138-deterministic-fixturevalue-2139-deterministic-fixturevalue-2140-deterministic-fixturevalue-2141-deterministic-fixturevalue-2143-deterministic-fixturevalue-2144-deterministic-fixturevalue-2145-deterministic-fixturevalue-2146-deterministic-fixturevalue-2147-deterministic-fixturevalue-2148-deterministic-fixturevalue-2150-deterministic-fixturevalue-2151-deterministic-fixturevalue-2152-deterministic-fixturevalue-2153-deterministic-fixturevalue-2154-deterministic-fixturevalue-2155-deterministic-fixturevalue-2157-deterministic-fixturevalue-2158-deterministic-fixturevalue-2159-deterministic-fixturevalue-2160-deterministic-fixturevalue-2161-deterministic-fixturevalue-2162-deterministic-fixturevalue-2164-deterministic-fixturevalue-2165-deterministic-fixturevalue-2166-deterministic-fixturevalue-2167-deterministic-fixturevalue-2168-deterministic-fixturevalue-2169-deterministic-fixturevalue-2171-deterministic-fixturevalue-2172-deterministic-fixturevalue-2173-deterministic-fixturevalue-2174-deterministic-fixturevalue-2175-deterministic-fixture$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-2176-deterministic-fixturevalue-2178-deterministic-fixturevalue-2179-deterministic-fixturevalue-2180-deterministic-fixturevalue-2181-deterministic-fixturevalue-2182-deterministic-fixturevalue-2183-deterministic-fixturevalue-2185-deterministic-fixturevalue-2186-deterministic-fixturevalue-2187-deterministic-fixturevalue-2188-deterministic-fixturevalue-2189-deterministic-fixturevalue-2190-deterministic-fixturevalue-2192-deterministic-fixturevalue-2193-deterministic-fixturevalue-2194-deterministic-fixturevalue-2195-deterministic-fixturevalue-2196-deterministic-fixturevalue-2197-deterministic-fixturevalue-2199-deterministic-fixturevalue-2200-deterministic-fixturevalue-2201-deterministic-fixturevalue-2202-deterministic-fixturevalue-2203-deterministic-fixturevalue-2204-deterministic-fixturevalue-2206-deterministic-fixturevalue-2207-deterministic-fixturevalue-2208-deterministic-fixturevalue-2209-deterministic-fixturevalue-2210-deterministic-fixturevalue-2211-deterministic-fixturevalue-2213-deterministic-fixturevalue-2214-deterministic-fixturevalue-2215-deterministic-fixturevalue-2216-deterministic-fixturevalue-2217-deterministic-fixturevalue-2218-deterministic-fixturevalue-2220-deterministic-fixturevalue-2221-deterministic-fixturevalue-2222-deterministic-fixturevalue-2223-deterministic-fixturevalue-2224-deterministic-fixturevalue-2225-deterministic-fixturevalue-2227-deterministic-fixturevalue-2228-deterministic-fixturevalue-2229-deterministic-fixturevalue-2230-deterministic-fixturevalue-2231-deterministic-fixturevalue-2232-deterministic-fixturevalue-2234-deterministic-fixturevalue-2235-deterministic-fixturevalue-2236-deterministic-fixturevalue-2237-deterministic-fixturevalue-2238-deterministic-fixturevalue-2239-deterministic-fixturevalue-2241-deterministic-fixturevalue-2242-deterministic-fixturevalue-2243-deterministic-fixturevalue-2244-deterministic-fixturevalue-2245-deterministic-fixturevalue-2246-deterministic-fixturevalue-2248-deterministic-fixturevalue-2249-deterministic-fixturevalue-2250-deterministic-fixturevalue-2251-deterministic-fixturevalue-2252-deterministic-fixturevalue-2253-deterministic-fixturevalue-2255-deterministic-fixturevalue-2256-deterministic-fixturevalue-2257-deterministic-fixturevalue-2258-deterministic-fixturevalue-2259-deterministic-fixturevalue-2260-deterministic-fixturevalue-2262-deterministic-fixturevalue-2263-deterministic-fixturevalue-2264-deterministic-fixturevalue-2265-deterministic-fixturevalue-2266-deterministic-fixturevalue-2267-deterministic-fixturevalue-2269-deterministic-fixturevalue-2270-deterministic-fixturevalue-2271-deterministic-fixturevalue-2272-deterministic-fixturevalue-2273-deterministic-fixturevalue-2274-deterministic-fixturevalue-2276-deterministic-fixturevalue-2277-deterministic-fixturevalue-2278-deterministic-fixturevalue-2279-deterministic-fixturevalue-2280-deterministic-fixturevalue-2281-deterministic-fixturevalue-2283-deterministic-fixturevalue-2284-deterministic-fixturevalue-2285-deterministic-fixturevalue-2286-deterministic-fixturevalue-2287-deterministic-fixturevalue-2288-deterministic-fixturevalue-2290-deterministic-fixturevalue-2291-deterministic-fixturevalue-2292-deterministic-fixturevalue-2293-deterministic-fixturevalue-2294-deterministic-fixturevalue-2295-deterministic-fixturevalue-2297-deterministic-fixturevalue-2298-deterministic-fixturevalue-2299-deterministic-fixturevalue-2300-deterministic-fixturevalue-2301-deterministic-fixturevalue-2302-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-2304-deterministic-fixturevalue-2305-deterministic-fixturevalue-2306-deterministic-fixturevalue-2307-deterministic-fixturevalue-2308-deterministic-fixturevalue-2309-deterministic-fixturevalue-2311-deterministic-fixturevalue-2312-deterministic-fixturevalue-2313-deterministic-fixturevalue-2314-deterministic-fixturevalue-2315-deterministic-fixturevalue-2316-deterministic-fixturevalue-2318-deterministic-fixturevalue-2319-deterministic-fixturevalue-2320-deterministic-fixturevalue-2321-deterministic-fixturevalue-2322-deterministic-fixturevalue-2323-deterministic-fixturevalue-2325-deterministic-fixturevalue-2326-deterministic-fixturevalue-2327-deterministic-fixturevalue-2328-deterministic-fixturevalue-2329-deterministic-fixturevalue-2330-deterministic-fixturevalue-2332-deterministic-fixturevalue-2333-deterministic-fixturevalue-2334-deterministic-fixturevalue-2335-deterministic-fixturevalue-2336-deterministic-fixturevalue-2337-deterministic-fixturevalue-2339-deterministic-fixturevalue-2340-deterministic-fixturevalue-2341-deterministic-fixturevalue-2342-deterministic-fixturevalue-2343-deterministic-fixturevalue-2344-deterministic-fixturevalue-2346-deterministic-fixturevalue-2347-deterministic-fixturevalue-2348-deterministic-fixturevalue-2349-deterministic-fixturevalue-2350-deterministic-fixturevalue-2351-deterministic-fixturevalue-2353-deterministic-fixturevalue-2354-deterministic-fixturevalue-2355-deterministic-fixturevalue-2356-deterministic-fixturevalue-2357-deterministic-fixturevalue-2358-deterministic-fixturevalue-2360-deterministic-fixturevalue-2361-deterministic-fixturevalue-2362-deterministic-fixturevalue-2363-deterministic-fixturevalue-2364-deterministic-fixturevalue-2365-deterministic-fixturevalue-2367-deterministic-fixturevalue-2368-deterministic-fixturevalue-2369-deterministic-fixturevalue-2370-deterministic-fixturevalue-2371-deterministic-fixturevalue-2372-deterministic-fixturevalue-2374-deterministic-fixturevalue-2375-deterministic-fixturevalue-2376-deterministic-fixturevalue-2377-deterministic-fixturevalue-2378-deterministic-fixturevalue-2379-deterministic-fixturevalue-2381-deterministic-fixturevalue-2382-deterministic-fixturevalue-2383-deterministic-fixturevalue-2384-deterministic-fixturevalue-2385-deterministic-fixturevalue-2386-deterministic-fixturevalue-2388-deterministic-fixturevalue-2389-deterministic-fixturevalue-2390-deterministic-fixturevalue-2391-deterministic-fixturevalue-2392-deterministic-fixturevalue-2393-deterministic-fixturevalue-2395-deterministic-fixturevalue-2396-deterministic-fixturevalue-2397-deterministic-fixturevalue-2398-deterministic-fixturevalue-2399-deterministic-fixturevalue-2400-deterministic-fixturevalue-2402-deterministic-fixturevalue-2403-deterministic-fixturevalue-2404-deterministic-fixturevalue-2405-deterministic-fixturevalue-2406-deterministic-fixturevalue-2407-deterministic-fixturevalue-2409-deterministic-fixturevalue-2410-deterministic-fixturevalue-2411-deterministic-fixturevalue-2412-deterministic-fixturevalue-2413-deterministic-fixturevalue-2414-deterministic-fixturevalue-2416-deterministic-fixturevalue-2417-deterministic-fixturevalue-2418-deterministic-fixturevalue-2419-deterministic-fixturevalue-2420-deterministic-fixturevalue-2421-deterministic-fixturevalue-2423-deterministic-fixturevalue-2424-deterministic-fixturevalue-2425-deterministic-fixturevalue-2426-deterministic-fixturevalue-2427-deterministic-fixturevalue-2428-deterministic-fixturevalue-2430-deterministic-fixturevalue-2431-deterministic-fixture$Dd$DDd$Dd$Dd$DDd$Dd$Dd $ D D d  + +$ +D +d + + + + + + $ D d  $ D D d   $ D d $Dd$DDdvalue-2432-deterministic-fixturevalue-2433-deterministic-fixturevalue-2434-deterministic-fixturevalue-2435-deterministic-fixturevalue-2437-deterministic-fixturevalue-2438-deterministic-fixturevalue-2439-deterministic-fixturevalue-2440-deterministic-fixturevalue-2441-deterministic-fixturevalue-2442-deterministic-fixturevalue-2444-deterministic-fixturevalue-2445-deterministic-fixturevalue-2446-deterministic-fixturevalue-2447-deterministic-fixturevalue-2448-deterministic-fixturevalue-2449-deterministic-fixturevalue-2451-deterministic-fixturevalue-2452-deterministic-fixturevalue-2453-deterministic-fixturevalue-2454-deterministic-fixturevalue-2455-deterministic-fixturevalue-2456-deterministic-fixturevalue-2458-deterministic-fixturevalue-2459-deterministic-fixturevalue-2460-deterministic-fixturevalue-2461-deterministic-fixturevalue-2462-deterministic-fixturevalue-2463-deterministic-fixturevalue-2465-deterministic-fixturevalue-2466-deterministic-fixturevalue-2467-deterministic-fixturevalue-2468-deterministic-fixturevalue-2469-deterministic-fixturevalue-2470-deterministic-fixturevalue-2472-deterministic-fixturevalue-2473-deterministic-fixturevalue-2474-deterministic-fixturevalue-2475-deterministic-fixturevalue-2476-deterministic-fixturevalue-2477-deterministic-fixturevalue-2479-deterministic-fixturevalue-2480-deterministic-fixturevalue-2481-deterministic-fixturevalue-2482-deterministic-fixturevalue-2483-deterministic-fixturevalue-2484-deterministic-fixturevalue-2486-deterministic-fixturevalue-2487-deterministic-fixturevalue-2488-deterministic-fixturevalue-2489-deterministic-fixturevalue-2490-deterministic-fixturevalue-2491-deterministic-fixturevalue-2493-deterministic-fixturevalue-2494-deterministic-fixturevalue-2495-deterministic-fixturevalue-2496-deterministic-fixturevalue-2497-deterministic-fixturevalue-2498-deterministic-fixturevalue-2500-deterministic-fixturevalue-2501-deterministic-fixturevalue-2502-deterministic-fixturevalue-2503-deterministic-fixturevalue-2504-deterministic-fixturevalue-2505-deterministic-fixturevalue-2507-deterministic-fixturevalue-2508-deterministic-fixturevalue-2509-deterministic-fixturevalue-2510-deterministic-fixturevalue-2511-deterministic-fixturevalue-2512-deterministic-fixturevalue-2514-deterministic-fixturevalue-2515-deterministic-fixturevalue-2516-deterministic-fixturevalue-2517-deterministic-fixturevalue-2518-deterministic-fixturevalue-2519-deterministic-fixturevalue-2521-deterministic-fixturevalue-2522-deterministic-fixturevalue-2523-deterministic-fixturevalue-2524-deterministic-fixturevalue-2525-deterministic-fixturevalue-2526-deterministic-fixturevalue-2528-deterministic-fixturevalue-2529-deterministic-fixturevalue-2530-deterministic-fixturevalue-2531-deterministic-fixturevalue-2532-deterministic-fixturevalue-2533-deterministic-fixturevalue-2535-deterministic-fixturevalue-2536-deterministic-fixturevalue-2537-deterministic-fixturevalue-2538-deterministic-fixturevalue-2539-deterministic-fixturevalue-2540-deterministic-fixturevalue-2542-deterministic-fixturevalue-2543-deterministic-fixturevalue-2544-deterministic-fixturevalue-2545-deterministic-fixturevalue-2546-deterministic-fixturevalue-2547-deterministic-fixturevalue-2549-deterministic-fixturevalue-2550-deterministic-fixturevalue-2551-deterministic-fixturevalue-2552-deterministic-fixturevalue-2553-deterministic-fixturevalue-2554-deterministic-fixturevalue-2556-deterministic-fixturevalue-2557-deterministic-fixturevalue-2558-deterministic-fixturevalue-2559-deterministic-fixture$DDd$Dd$Dd$DDd$Dd$Dd$DDd  $ D d  +$ +D +d + + + + + + $ D D d   $ D d  $ D d $DDd$Ddvalue-2560-deterministic-fixturevalue-2561-deterministic-fixturevalue-2563-deterministic-fixturevalue-2564-deterministic-fixturevalue-2565-deterministic-fixturevalue-2566-deterministic-fixturevalue-2567-deterministic-fixturevalue-2568-deterministic-fixturevalue-2570-deterministic-fixturevalue-2571-deterministic-fixturevalue-2572-deterministic-fixturevalue-2573-deterministic-fixturevalue-2574-deterministic-fixturevalue-2575-deterministic-fixturevalue-2577-deterministic-fixturevalue-2578-deterministic-fixturevalue-2579-deterministic-fixturevalue-2580-deterministic-fixturevalue-2581-deterministic-fixturevalue-2582-deterministic-fixturevalue-2584-deterministic-fixturevalue-2585-deterministic-fixturevalue-2586-deterministic-fixturevalue-2587-deterministic-fixturevalue-2588-deterministic-fixturevalue-2589-deterministic-fixturevalue-2591-deterministic-fixturevalue-2592-deterministic-fixturevalue-2593-deterministic-fixturevalue-2594-deterministic-fixturevalue-2595-deterministic-fixturevalue-2596-deterministic-fixturevalue-2598-deterministic-fixturevalue-2599-deterministic-fixturevalue-2600-deterministic-fixturevalue-2601-deterministic-fixturevalue-2602-deterministic-fixturevalue-2603-deterministic-fixturevalue-2605-deterministic-fixturevalue-2606-deterministic-fixturevalue-2607-deterministic-fixturevalue-2608-deterministic-fixturevalue-2609-deterministic-fixturevalue-2610-deterministic-fixturevalue-2612-deterministic-fixturevalue-2613-deterministic-fixturevalue-2614-deterministic-fixturevalue-2615-deterministic-fixturevalue-2616-deterministic-fixturevalue-2617-deterministic-fixturevalue-2619-deterministic-fixturevalue-2620-deterministic-fixturevalue-2621-deterministic-fixturevalue-2622-deterministic-fixturevalue-2623-deterministic-fixturevalue-2624-deterministic-fixturevalue-2626-deterministic-fixturevalue-2627-deterministic-fixturevalue-2628-deterministic-fixturevalue-2629-deterministic-fixturevalue-2630-deterministic-fixturevalue-2631-deterministic-fixturevalue-2633-deterministic-fixturevalue-2634-deterministic-fixturevalue-2635-deterministic-fixturevalue-2636-deterministic-fixturevalue-2637-deterministic-fixturevalue-2638-deterministic-fixturevalue-2640-deterministic-fixturevalue-2641-deterministic-fixturevalue-2642-deterministic-fixturevalue-2643-deterministic-fixturevalue-2644-deterministic-fixturevalue-2645-deterministic-fixturevalue-2647-deterministic-fixturevalue-2648-deterministic-fixturevalue-2649-deterministic-fixturevalue-2650-deterministic-fixturevalue-2651-deterministic-fixturevalue-2652-deterministic-fixturevalue-2654-deterministic-fixturevalue-2655-deterministic-fixturevalue-2656-deterministic-fixturevalue-2657-deterministic-fixturevalue-2658-deterministic-fixturevalue-2659-deterministic-fixturevalue-2661-deterministic-fixturevalue-2662-deterministic-fixturevalue-2663-deterministic-fixturevalue-2664-deterministic-fixturevalue-2665-deterministic-fixturevalue-2666-deterministic-fixturevalue-2668-deterministic-fixturevalue-2669-deterministic-fixturevalue-2670-deterministic-fixturevalue-2671-deterministic-fixturevalue-2672-deterministic-fixturevalue-2673-deterministic-fixturevalue-2675-deterministic-fixturevalue-2676-deterministic-fixturevalue-2677-deterministic-fixturevalue-2678-deterministic-fixturevalue-2679-deterministic-fixturevalue-2680-deterministic-fixturevalue-2682-deterministic-fixturevalue-2683-deterministic-fixturevalue-2684-deterministic-fixturevalue-2685-deterministic-fixturevalue-2686-deterministic-fixturevalue-2687-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-2689-deterministic-fixturevalue-2690-deterministic-fixturevalue-2691-deterministic-fixturevalue-2692-deterministic-fixturevalue-2693-deterministic-fixturevalue-2694-deterministic-fixturevalue-2696-deterministic-fixturevalue-2697-deterministic-fixturevalue-2698-deterministic-fixturevalue-2699-deterministic-fixturevalue-2700-deterministic-fixturevalue-2701-deterministic-fixturevalue-2703-deterministic-fixturevalue-2704-deterministic-fixturevalue-2705-deterministic-fixturevalue-2706-deterministic-fixturevalue-2707-deterministic-fixturevalue-2708-deterministic-fixturevalue-2710-deterministic-fixturevalue-2711-deterministic-fixturevalue-2712-deterministic-fixturevalue-2713-deterministic-fixturevalue-2714-deterministic-fixturevalue-2715-deterministic-fixturevalue-2717-deterministic-fixturevalue-2718-deterministic-fixturevalue-2719-deterministic-fixturevalue-2720-deterministic-fixturevalue-2721-deterministic-fixturevalue-2722-deterministic-fixturevalue-2724-deterministic-fixturevalue-2725-deterministic-fixturevalue-2726-deterministic-fixturevalue-2727-deterministic-fixturevalue-2728-deterministic-fixturevalue-2729-deterministic-fixturevalue-2731-deterministic-fixturevalue-2732-deterministic-fixturevalue-2733-deterministic-fixturevalue-2734-deterministic-fixturevalue-2735-deterministic-fixturevalue-2736-deterministic-fixturevalue-2738-deterministic-fixturevalue-2739-deterministic-fixturevalue-2740-deterministic-fixturevalue-2741-deterministic-fixturevalue-2742-deterministic-fixturevalue-2743-deterministic-fixturevalue-2745-deterministic-fixturevalue-2746-deterministic-fixturevalue-2747-deterministic-fixturevalue-2748-deterministic-fixturevalue-2749-deterministic-fixturevalue-2750-deterministic-fixturevalue-2752-deterministic-fixturevalue-2753-deterministic-fixturevalue-2754-deterministic-fixturevalue-2755-deterministic-fixturevalue-2756-deterministic-fixturevalue-2757-deterministic-fixturevalue-2759-deterministic-fixturevalue-2760-deterministic-fixturevalue-2761-deterministic-fixturevalue-2762-deterministic-fixturevalue-2763-deterministic-fixturevalue-2764-deterministic-fixturevalue-2766-deterministic-fixturevalue-2767-deterministic-fixturevalue-2768-deterministic-fixturevalue-2769-deterministic-fixturevalue-2770-deterministic-fixturevalue-2771-deterministic-fixturevalue-2773-deterministic-fixturevalue-2774-deterministic-fixturevalue-2775-deterministic-fixturevalue-2776-deterministic-fixturevalue-2777-deterministic-fixturevalue-2778-deterministic-fixturevalue-2780-deterministic-fixturevalue-2781-deterministic-fixturevalue-2782-deterministic-fixturevalue-2783-deterministic-fixturevalue-2784-deterministic-fixturevalue-2785-deterministic-fixturevalue-2787-deterministic-fixturevalue-2788-deterministic-fixturevalue-2789-deterministic-fixturevalue-2790-deterministic-fixturevalue-2791-deterministic-fixturevalue-2792-deterministic-fixturevalue-2794-deterministic-fixturevalue-2795-deterministic-fixturevalue-2796-deterministic-fixturevalue-2797-deterministic-fixturevalue-2798-deterministic-fixturevalue-2799-deterministic-fixturevalue-2801-deterministic-fixturevalue-2802-deterministic-fixturevalue-2803-deterministic-fixturevalue-2804-deterministic-fixturevalue-2805-deterministic-fixturevalue-2806-deterministic-fixturevalue-2808-deterministic-fixturevalue-2809-deterministic-fixturevalue-2810-deterministic-fixturevalue-2811-deterministic-fixturevalue-2812-deterministic-fixturevalue-2813-deterministic-fixturevalue-2815-deterministic-fixture$Dd$Ddd$$Dd$Dd$Ddd$$Dd$Dd $ D d d  +$ +$ +D +d + + + + + + $ D d  $ D d d  $ $ D d $Dd$Dddvalue-2816-deterministic-fixturevalue-2817-deterministic-fixturevalue-2818-deterministic-fixturevalue-2819-deterministic-fixturevalue-2820-deterministic-fixturevalue-2822-deterministic-fixturevalue-2823-deterministic-fixturevalue-2824-deterministic-fixturevalue-2825-deterministic-fixturevalue-2826-deterministic-fixturevalue-2827-deterministic-fixturevalue-2829-deterministic-fixturevalue-2830-deterministic-fixturevalue-2831-deterministic-fixturevalue-2832-deterministic-fixturevalue-2833-deterministic-fixturevalue-2834-deterministic-fixturevalue-2836-deterministic-fixturevalue-2837-deterministic-fixturevalue-2838-deterministic-fixturevalue-2839-deterministic-fixturevalue-2840-deterministic-fixturevalue-2841-deterministic-fixturevalue-2843-deterministic-fixturevalue-2844-deterministic-fixturevalue-2845-deterministic-fixturevalue-2846-deterministic-fixturevalue-2847-deterministic-fixturevalue-2848-deterministic-fixturevalue-2850-deterministic-fixturevalue-2851-deterministic-fixturevalue-2852-deterministic-fixturevalue-2853-deterministic-fixturevalue-2854-deterministic-fixturevalue-2855-deterministic-fixturevalue-2857-deterministic-fixturevalue-2858-deterministic-fixturevalue-2859-deterministic-fixturevalue-2860-deterministic-fixturevalue-2861-deterministic-fixturevalue-2862-deterministic-fixturevalue-2864-deterministic-fixturevalue-2865-deterministic-fixturevalue-2866-deterministic-fixturevalue-2867-deterministic-fixturevalue-2868-deterministic-fixturevalue-2869-deterministic-fixturevalue-2871-deterministic-fixturevalue-2872-deterministic-fixturevalue-2873-deterministic-fixturevalue-2874-deterministic-fixturevalue-2875-deterministic-fixturevalue-2876-deterministic-fixturevalue-2878-deterministic-fixturevalue-2879-deterministic-fixturevalue-2880-deterministic-fixturevalue-2881-deterministic-fixturevalue-2882-deterministic-fixturevalue-2883-deterministic-fixturevalue-2885-deterministic-fixturevalue-2886-deterministic-fixturevalue-2887-deterministic-fixturevalue-2888-deterministic-fixturevalue-2889-deterministic-fixturevalue-2890-deterministic-fixturevalue-2892-deterministic-fixturevalue-2893-deterministic-fixturevalue-2894-deterministic-fixturevalue-2895-deterministic-fixturevalue-2896-deterministic-fixturevalue-2897-deterministic-fixturevalue-2899-deterministic-fixturevalue-2900-deterministic-fixturevalue-2901-deterministic-fixturevalue-2902-deterministic-fixturevalue-2903-deterministic-fixturevalue-2904-deterministic-fixturevalue-2906-deterministic-fixturevalue-2907-deterministic-fixturevalue-2908-deterministic-fixturevalue-2909-deterministic-fixturevalue-2910-deterministic-fixturevalue-2911-deterministic-fixturevalue-2913-deterministic-fixturevalue-2914-deterministic-fixturevalue-2915-deterministic-fixturevalue-2916-deterministic-fixturevalue-2917-deterministic-fixturevalue-2918-deterministic-fixturevalue-2920-deterministic-fixturevalue-2921-deterministic-fixturevalue-2922-deterministic-fixturevalue-2923-deterministic-fixturevalue-2924-deterministic-fixturevalue-2925-deterministic-fixturevalue-2927-deterministic-fixturevalue-2928-deterministic-fixturevalue-2929-deterministic-fixturevalue-2930-deterministic-fixturevalue-2931-deterministic-fixturevalue-2932-deterministic-fixturevalue-2934-deterministic-fixturevalue-2935-deterministic-fixturevalue-2936-deterministic-fixturevalue-2937-deterministic-fixturevalue-2938-deterministic-fixturevalue-2939-deterministic-fixturevalue-2941-deterministic-fixturevalue-2942-deterministic-fixturevalue-2943-deterministic-fixture$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-2944-deterministic-fixturevalue-2945-deterministic-fixturevalue-2946-deterministic-fixturevalue-2948-deterministic-fixturevalue-2949-deterministic-fixturevalue-2950-deterministic-fixturevalue-2951-deterministic-fixturevalue-2952-deterministic-fixturevalue-2953-deterministic-fixturevalue-2955-deterministic-fixturevalue-2956-deterministic-fixturevalue-2957-deterministic-fixturevalue-2958-deterministic-fixturevalue-2959-deterministic-fixturevalue-2960-deterministic-fixturevalue-2962-deterministic-fixturevalue-2963-deterministic-fixturevalue-2964-deterministic-fixturevalue-2965-deterministic-fixturevalue-2966-deterministic-fixturevalue-2967-deterministic-fixturevalue-2969-deterministic-fixturevalue-2970-deterministic-fixturevalue-2971-deterministic-fixturevalue-2972-deterministic-fixturevalue-2973-deterministic-fixturevalue-2974-deterministic-fixturevalue-2976-deterministic-fixturevalue-2977-deterministic-fixturevalue-2978-deterministic-fixturevalue-2979-deterministic-fixturevalue-2980-deterministic-fixturevalue-2981-deterministic-fixturevalue-2983-deterministic-fixturevalue-2984-deterministic-fixturevalue-2985-deterministic-fixturevalue-2986-deterministic-fixturevalue-2987-deterministic-fixturevalue-2988-deterministic-fixturevalue-2990-deterministic-fixturevalue-2991-deterministic-fixturevalue-2992-deterministic-fixturevalue-2993-deterministic-fixturevalue-2994-deterministic-fixturevalue-2995-deterministic-fixturevalue-2997-deterministic-fixturevalue-2998-deterministic-fixturevalue-2999-deterministic-fixturevalue-3000-deterministic-fixturevalue-3001-deterministic-fixturevalue-3002-deterministic-fixturevalue-3004-deterministic-fixturevalue-3005-deterministic-fixturevalue-3006-deterministic-fixturevalue-3007-deterministic-fixturevalue-3008-deterministic-fixturevalue-3009-deterministic-fixturevalue-3011-deterministic-fixturevalue-3012-deterministic-fixturevalue-3013-deterministic-fixturevalue-3014-deterministic-fixturevalue-3015-deterministic-fixturevalue-3016-deterministic-fixturevalue-3018-deterministic-fixturevalue-3019-deterministic-fixturevalue-3020-deterministic-fixturevalue-3021-deterministic-fixturevalue-3022-deterministic-fixturevalue-3023-deterministic-fixturevalue-3025-deterministic-fixturevalue-3026-deterministic-fixturevalue-3027-deterministic-fixturevalue-3028-deterministic-fixturevalue-3029-deterministic-fixturevalue-3030-deterministic-fixturevalue-3032-deterministic-fixturevalue-3033-deterministic-fixturevalue-3034-deterministic-fixturevalue-3035-deterministic-fixturevalue-3036-deterministic-fixturevalue-3037-deterministic-fixturevalue-3039-deterministic-fixturevalue-3040-deterministic-fixturevalue-3041-deterministic-fixturevalue-3042-deterministic-fixturevalue-3043-deterministic-fixturevalue-3044-deterministic-fixturevalue-3046-deterministic-fixturevalue-3047-deterministic-fixturevalue-3048-deterministic-fixturevalue-3049-deterministic-fixturevalue-3050-deterministic-fixturevalue-3051-deterministic-fixturevalue-3053-deterministic-fixturevalue-3054-deterministic-fixturevalue-3055-deterministic-fixturevalue-3056-deterministic-fixturevalue-3057-deterministic-fixturevalue-3058-deterministic-fixturevalue-3060-deterministic-fixturevalue-3061-deterministic-fixturevalue-3062-deterministic-fixturevalue-3063-deterministic-fixturevalue-3064-deterministic-fixturevalue-3065-deterministic-fixturevalue-3067-deterministic-fixturevalue-3068-deterministic-fixturevalue-3069-deterministic-fixturevalue-3070-deterministic-fixturevalue-3071-deterministic-fixtureJJ HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"D$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$!@@@ @@ @!@@ H@ @@ @@ @H@  H]xi ~ uX hP p ( +#  H+  +;0 hCPBFdȋ!ҌBGDdЋ!լGtd苡CHd!Hԣd CId!,I4d LCJdd8!lJdX CKdh!K$dxcF|LҖFڂ5PّӦF򈵍\QԶ@G +dG"5lޑֽG:5pQ#HR5|@HjͣH5HQ&#I5.JEH0JH2 JK40 JNXh@ JQ8 JT: JW< JZ H J`!B +Jc"D0 +JfXqe8!lR}eHڡ CSĉeX!Se`CT$ex!TTe CUe! UemCVe!VDeWte 6 dL  L6 R$M6 dMM*6!M0R%$N66)dN<NB61NHRK! ` K$ (K'&2K*X6J$K-FHb ,K0z 4K3Xfh$P8K6vȪ&pDK9(*TK?\KBXKLdSCMd!MekCNe!sN)e{CO5e!OAeơCPMe!PYeΡCQee!CRTcĵ ф5,񑴥#5 c5l5D،赬Qt#5c 5,4 LRdF#IFJiV$IHJl%JJov&J*LJrކ'JBNJuߖ(JZPJxক)KrRJ{+KVJƕ,KXJ֕-KZJ.L\hDP +f" p + f(D +%f" +1fX+D +=f" 3 If!;D0 UfX""h!CP af&!p mf*"("SD yf,""[İ fX5NXԩ9T Y=Z -!Y4(` M%ZdI8l &ZMHr)[ĪQ`x+[$x~-\TY .]]-1]a$O +6dKƖO"6lK֖O:⶟tKRPR6|KdPjⶠKP6KPⶡKR&Q6K6dQⶢKVQ6KfQ6K"Ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd@@ @!@@@ @@ @!@@ H@ @@ @iePxRjnԔ{>'jqդ|?Gktִ} AgRkw~AkBzԀBkrICl DSl҆!P30xf:(4x|R1Ԙ215JgR@ +ˇRT2U˧R3eJ"4u +RTRS5'S5JGS6 +T+V+ V +VI" + $+!V0&+)VI<(+1V H,S+œ9VT!+`0)+ȜQVIx2,+YV ĞDMpoBPN=uo\O=yphP>}ptQp2$RG>qb 0Sg>qDS>qTT>q)dUr"1tV>yT(ޞE+ݜVÍI+ ؁DSM+朙VFT+VHY+VIJ]+rR9V'?r够WG?rI崟XsQğY?sBYПZ?sra[?t\?tq(\t@]'@t2 L^G@ubDXQV +yT(BWy(CQXy(DQY +yT( DQZz(EQ\zԢ(E]Jz(Q^ +zT(FQ_z(GQ`zԣ( GaJz+WUV+"WeV+"*WuV+RȂW+:WԂ+BWGW+JWgW+ūW+BbWիW+rjWW+rW2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 2@ H@ @@@ @!@ UޕB VǢРJW  +X Y(2٠%JZHb 5 +[hUɒ⠒E[ U\ `J]" u +^ĉTi-ЉVi-[܉X-[Zj-[j. +\^j.  bj .\d .\$j.*\@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3097-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3098-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3099-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3100-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3101-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3102-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3103-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3104-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3105-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3106-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3107-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3108-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3109-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3110-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3111-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3112-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3113-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3114-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3115-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3116-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3117-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3118-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3119-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3120-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3121-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3122-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3123-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3124-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3125-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3126-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3127-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3128-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3129-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3130-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3131-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3132-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3133-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3134-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3135-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3136-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3137-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3138-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3139-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3140-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3141-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3142-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3143-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3144-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3145-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3146-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3147-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3148-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3149-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3150-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3151-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3152-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3153-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3154-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3155-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3156-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3157-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3158-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3159-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3160-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3161-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3162-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3163-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3164-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3165-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3166-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3167-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3168-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3169-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3170-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3171-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3172-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3173-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3174-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3175-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3176-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3177-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3178-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3179-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3180-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3181-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3182-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3183-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3184-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3185-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3186-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3187-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3188-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3189-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3190-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3191-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3192-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3193-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3194-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3195-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3196-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3197-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3198-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3199-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3200-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3201-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3202-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3203-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3204-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3205-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3206-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3207-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3208-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3209-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3210-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3211-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3212-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3213-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3214-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3215-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3216-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3217-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3218-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3219-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3220-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3221-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3222-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3223-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3224-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3225-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3226-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3227-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3228-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3229-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3230-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3231-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3232-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3233-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3234-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3235-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3236-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3237-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3238-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3239-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3240-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3241-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3242-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3243-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3244-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3245-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3246-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3247-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3248-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3249-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3250-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3251-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3252-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3253-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3254-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3255-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3256-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3257-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3258-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3259-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3260-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3261-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3262-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3263-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3264-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3265-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3266-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3267-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3268-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3269-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3270-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3271-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3272-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3273-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3274-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3275-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3276-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3277-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3278-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3279-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3280-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3281-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3282-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3283-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3284-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3285-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3286-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3287-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3288-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3289-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3290-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3291-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3292-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3293-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3294-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3295-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3296-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3297-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3298-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3299-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3300-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3301-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3302-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3303-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3304-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3305-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3306-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3307-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3308-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3309-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3310-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3311-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3312-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3313-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3314-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3315-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3316-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3317-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3318-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3319-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3320-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3321-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3322-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3323-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3324-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3325-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3326-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3327-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3328-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3329-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3330-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3331-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3332-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3333-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3334-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3335-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3336-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3337-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3338-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3339-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3340-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3341-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3342-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3343-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3344-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3345-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3346-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3347-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3348-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3349-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3350-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3351-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3352-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3353-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3354-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3355-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3356-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3357-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3358-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3359-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3360-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3361-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3362-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3363-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3364-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3365-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3366-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3367-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3368-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3369-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3370-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3371-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3372-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3373-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3374-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3375-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3376-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3377-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3378-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3379-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3380-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3381-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3382-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3383-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3384-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3385-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3386-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3387-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3388-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3389-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3390-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3391-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3392-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3393-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3394-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3395-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3396-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3397-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3398-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3399-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3400-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3401-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3402-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3403-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3404-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3405-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3406-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3407-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3408-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3409-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3410-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3411-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3412-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3413-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3414-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3415-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3416-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3417-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3418-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3419-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3420-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3421-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3422-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3423-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3424-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3425-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3426-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3427-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3428-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3429-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3430-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3431-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3432-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3433-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3434-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3435-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3436-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3437-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3438-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3439-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3440-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3441-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3442-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3443-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3444-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3445-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3446-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3447-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3448-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3449-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3450-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3451-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3452-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3453-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3454-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3455-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3456-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3457-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3458-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3459-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3460-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3461-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3462-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3463-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3464-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3465-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3466-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3467-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3468-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3469-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3470-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3471-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3472-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3473-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3474-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3475-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3476-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3477-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3478-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3479-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3480-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3481-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3482-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3483-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3484-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3485-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3486-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3487-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3488-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3489-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3490-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3491-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3492-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3493-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3494-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3495-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3496-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3497-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3498-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3499-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3500-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3501-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3502-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3503-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3504-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3505-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3506-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3507-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3508-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3509-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3510-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3511-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3512-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3513-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3514-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3515-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3516-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3517-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3518-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3519-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3520-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3521-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3522-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3523-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3524-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3525-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3526-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3527-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3528-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3529-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3530-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3531-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3532-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3533-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3534-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3535-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3536-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3537-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3538-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3539-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3540-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3541-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3542-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3543-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3544-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3545-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3546-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3547-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3548-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3549-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3550-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3551-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3552-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3553-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3554-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3555-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3556-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3557-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3558-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3559-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3560-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3561-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3562-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3563-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3564-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3565-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3566-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3567-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3568-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3569-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3570-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3571-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3572-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3573-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3574-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3575-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3576-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3577-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3578-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3579-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3580-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3581-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3582-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3583-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3584-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3585-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3586-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3587-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3588-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3589-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3590-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3591-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3592-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3593-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3594-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3595-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3596-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3597-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3598-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3599-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3600-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3601-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3602-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3603-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3604-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3605-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3606-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3607-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3608-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3609-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3610-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3611-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3612-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3613-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3614-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3615-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3616-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3617-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3618-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3619-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3620-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3621-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3622-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3623-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3624-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3625-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3626-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3627-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3628-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3629-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3630-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3631-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3632-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3633-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3634-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3635-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3636-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3637-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3638-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3639-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3640-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3641-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3642-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3643-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3644-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3645-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3646-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3647-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3648-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3649-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3650-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3651-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3652-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3653-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3654-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3655-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3656-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3657-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3658-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3659-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3660-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3661-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3662-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3663-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3664-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3665-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3666-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3667-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3668-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3669-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3670-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3671-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3672-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3673-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3674-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3675-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3676-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3677-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3678-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3679-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3680-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3681-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3682-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3683-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3684-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3685-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3686-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3687-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3688-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3689-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3690-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3691-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3692-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3693-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3694-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3695-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3696-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3697-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3698-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3699-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3700-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3701-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3702-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3703-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3704-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3705-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3706-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3707-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3708-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3709-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3710-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3711-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3712-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3713-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3714-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3715-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3716-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3717-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3718-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3719-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3720-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3721-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3722-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3723-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3724-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3725-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3726-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3727-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3728-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3729-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3730-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3731-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3732-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3733-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3734-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3735-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3736-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3737-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3738-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3739-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3740-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3741-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3742-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3743-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3744-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3745-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3746-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3747-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3748-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3749-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3750-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3751-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3752-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3753-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3754-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3755-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3756-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3757-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3758-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3759-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3760-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3761-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3762-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3763-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3764-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3765-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3766-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3767-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3768-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3769-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3770-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3771-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3772-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3773-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3774-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3775-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3776-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3777-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3778-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3779-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3780-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3781-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3782-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3783-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3784-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3785-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3786-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3787-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3788-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3789-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3790-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3791-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3792-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3793-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3794-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3795-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3796-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3797-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3798-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3799-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3800-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3801-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3802-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3803-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3804-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3805-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3806-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3807-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3808-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3809-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3810-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3811-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3812-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3813-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3814-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3815-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3816-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3817-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3818-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3819-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3820-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3821-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3822-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3823-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3824-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3825-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3826-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3827-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3828-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3829-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3830-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3831-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3832-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3833-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3834-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3835-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3836-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3837-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3838-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3839-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3840-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3841-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3842-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3843-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3844-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3845-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3846-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3847-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3848-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3849-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3850-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3851-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3852-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3853-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3854-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3855-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3856-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3857-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3858-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3859-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3860-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3861-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3862-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3863-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3864-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3865-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3866-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3867-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3868-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3869-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3870-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3871-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3872-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3873-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3874-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3875-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3876-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3877-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3878-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3879-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3880-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3881-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3882-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3883-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3884-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3885-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3886-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3887-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3888-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3889-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3890-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3891-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3892-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3893-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3894-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3895-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3896-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3897-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3898-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3899-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3900-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3901-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3902-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3903-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3904-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3905-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3906-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3907-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3908-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3909-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3910-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3911-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3912-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3913-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3914-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3915-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3916-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3917-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3918-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3919-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3920-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3921-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3922-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3923-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3924-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3925-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3926-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3927-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3928-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3929-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3930-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3931-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3932-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3933-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3934-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3935-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3936-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3937-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3938-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3939-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3940-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3941-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3942-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3943-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3944-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3945-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3946-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3947-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3948-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3949-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3950-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3951-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3952-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3953-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3954-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3955-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3956-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3957-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3958-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3959-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3960-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3961-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3962-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3963-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3964-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3965-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3966-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3967-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3968-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3969-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3970-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3971-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3972-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3973-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3974-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3975-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3976-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3977-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3978-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3979-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3980-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3981-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3982-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3983-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3984-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3985-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3986-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3987-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3988-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3989-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3990-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3991-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3992-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3993-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3994-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3995-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3996-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3997-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3998-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-3999-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4000-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4001-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4002-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4003-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4004-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4005-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4006-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4007-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4008-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4009-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4010-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4011-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4012-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4013-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4014-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4015-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4016-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4017-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4018-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4019-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4020-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4021-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4022-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4023-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4024-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4025-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4026-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4027-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4028-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4029-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4030-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4031-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4032-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4033-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4034-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4035-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4036-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4037-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4038-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4039-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4040-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4041-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4042-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4043-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4044-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4045-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4046-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4047-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4048-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4049-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4050-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4051-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4052-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4053-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4054-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4055-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4056-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4057-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4058-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4059-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4060-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4061-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4062-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4063-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4064-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4065-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4066-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4067-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4068-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4069-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4070-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4071-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4072-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4073-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4074-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4075-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4076-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4077-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4078-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4079-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4080-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4081-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4082-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4083-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4084-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4085-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4086-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4087-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4088-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4089-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4090-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4091-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4092-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4093-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4094-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4095-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH ,<L\l| + +  ,<L\l| -.=>MN]^mn}~ +  -.=>MN]^mn}~/?O_o + /?O_o@ @AAB,BCMDN]E^mFn}G~HIJKLMNO PQ-R.=S>MTN]U^mVn}W~XYZ[\]^_@AB/C?DOE_FoGHIJKLMNOPQR/S?TOU_VoWXYZ[\]^_ !!","#<#$L$%\%&l&'|'(())**++,,--..//0 0112,23<34L45\56l67|78899::;;<<==>>?? !-".=#>M$N]%^m&n}'~()*+,-./ 01-2.=3>M4N]5^m6n}7~89:;<=>? !"/#?$O%_&o'()*+,-./012/3?4O5_6o789:;<=>?` `aab,bcMdN]e^mfn}g~hijklmno pq-r.=s>MtN]u^mvn}w~xyz{|}~`ab/c?dOe_foghijklmnopqr/s?tOu_vowxyz{|}~HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHg g ` HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-3072-deterministic-fixturevalue-3074-deterministic-fixturevalue-3075-deterministic-fixturevalue-3076-deterministic-fixturevalue-3077-deterministic-fixturevalue-3078-deterministic-fixturevalue-3079-deterministic-fixturevalue-3081-deterministic-fixturevalue-3082-deterministic-fixturevalue-3083-deterministic-fixturevalue-3084-deterministic-fixturevalue-3085-deterministic-fixturevalue-3086-deterministic-fixturevalue-3088-deterministic-fixturevalue-3089-deterministic-fixturevalue-3090-deterministic-fixturevalue-3091-deterministic-fixturevalue-3092-deterministic-fixturevalue-3093-deterministic-fixturevalue-3095-deterministic-fixturevalue-3096-deterministic-fixturevalue-3097-deterministic-fixturevalue-3098-deterministic-fixturevalue-3099-deterministic-fixturevalue-3100-deterministic-fixturevalue-3102-deterministic-fixturevalue-3103-deterministic-fixturevalue-3104-deterministic-fixturevalue-3105-deterministic-fixturevalue-3106-deterministic-fixturevalue-3107-deterministic-fixturevalue-3109-deterministic-fixturevalue-3110-deterministic-fixturevalue-3111-deterministic-fixturevalue-3112-deterministic-fixturevalue-3113-deterministic-fixturevalue-3114-deterministic-fixturevalue-3116-deterministic-fixturevalue-3117-deterministic-fixturevalue-3118-deterministic-fixturevalue-3119-deterministic-fixturevalue-3120-deterministic-fixturevalue-3121-deterministic-fixturevalue-3123-deterministic-fixturevalue-3124-deterministic-fixturevalue-3125-deterministic-fixturevalue-3126-deterministic-fixturevalue-3127-deterministic-fixturevalue-3128-deterministic-fixturevalue-3130-deterministic-fixturevalue-3131-deterministic-fixturevalue-3132-deterministic-fixturevalue-3133-deterministic-fixturevalue-3134-deterministic-fixturevalue-3135-deterministic-fixturevalue-3137-deterministic-fixturevalue-3138-deterministic-fixturevalue-3139-deterministic-fixturevalue-3140-deterministic-fixturevalue-3141-deterministic-fixturevalue-3142-deterministic-fixturevalue-3144-deterministic-fixturevalue-3145-deterministic-fixturevalue-3146-deterministic-fixturevalue-3147-deterministic-fixturevalue-3148-deterministic-fixturevalue-3149-deterministic-fixturevalue-3151-deterministic-fixturevalue-3152-deterministic-fixturevalue-3153-deterministic-fixturevalue-3154-deterministic-fixturevalue-3155-deterministic-fixturevalue-3156-deterministic-fixturevalue-3158-deterministic-fixturevalue-3159-deterministic-fixturevalue-3160-deterministic-fixturevalue-3161-deterministic-fixturevalue-3162-deterministic-fixturevalue-3163-deterministic-fixturevalue-3165-deterministic-fixturevalue-3166-deterministic-fixturevalue-3167-deterministic-fixturevalue-3168-deterministic-fixturevalue-3169-deterministic-fixturevalue-3170-deterministic-fixturevalue-3172-deterministic-fixturevalue-3173-deterministic-fixturevalue-3174-deterministic-fixturevalue-3175-deterministic-fixturevalue-3176-deterministic-fixturevalue-3177-deterministic-fixturevalue-3179-deterministic-fixturevalue-3180-deterministic-fixturevalue-3181-deterministic-fixturevalue-3182-deterministic-fixturevalue-3183-deterministic-fixturevalue-3184-deterministic-fixturevalue-3186-deterministic-fixturevalue-3187-deterministic-fixturevalue-3188-deterministic-fixturevalue-3189-deterministic-fixturevalue-3190-deterministic-fixturevalue-3191-deterministic-fixturevalue-3193-deterministic-fixturevalue-3194-deterministic-fixturevalue-3195-deterministic-fixturevalue-3196-deterministic-fixturevalue-3197-deterministic-fixturevalue-3198-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-3200-deterministic-fixturevalue-3201-deterministic-fixturevalue-3202-deterministic-fixturevalue-3203-deterministic-fixturevalue-3204-deterministic-fixturevalue-3205-deterministic-fixturevalue-3207-deterministic-fixturevalue-3208-deterministic-fixturevalue-3209-deterministic-fixturevalue-3210-deterministic-fixturevalue-3211-deterministic-fixturevalue-3212-deterministic-fixturevalue-3214-deterministic-fixturevalue-3215-deterministic-fixturevalue-3216-deterministic-fixturevalue-3217-deterministic-fixturevalue-3218-deterministic-fixturevalue-3219-deterministic-fixturevalue-3221-deterministic-fixturevalue-3222-deterministic-fixturevalue-3223-deterministic-fixturevalue-3224-deterministic-fixturevalue-3225-deterministic-fixturevalue-3226-deterministic-fixturevalue-3228-deterministic-fixturevalue-3229-deterministic-fixturevalue-3230-deterministic-fixturevalue-3231-deterministic-fixturevalue-3232-deterministic-fixturevalue-3233-deterministic-fixturevalue-3235-deterministic-fixturevalue-3236-deterministic-fixturevalue-3237-deterministic-fixturevalue-3238-deterministic-fixturevalue-3239-deterministic-fixturevalue-3240-deterministic-fixturevalue-3242-deterministic-fixturevalue-3243-deterministic-fixturevalue-3244-deterministic-fixturevalue-3245-deterministic-fixturevalue-3246-deterministic-fixturevalue-3247-deterministic-fixturevalue-3249-deterministic-fixturevalue-3250-deterministic-fixturevalue-3251-deterministic-fixturevalue-3252-deterministic-fixturevalue-3253-deterministic-fixturevalue-3254-deterministic-fixturevalue-3256-deterministic-fixturevalue-3257-deterministic-fixturevalue-3258-deterministic-fixturevalue-3259-deterministic-fixturevalue-3260-deterministic-fixturevalue-3261-deterministic-fixturevalue-3263-deterministic-fixturevalue-3264-deterministic-fixturevalue-3265-deterministic-fixturevalue-3266-deterministic-fixturevalue-3267-deterministic-fixturevalue-3268-deterministic-fixturevalue-3270-deterministic-fixturevalue-3271-deterministic-fixturevalue-3272-deterministic-fixturevalue-3273-deterministic-fixturevalue-3274-deterministic-fixturevalue-3275-deterministic-fixturevalue-3277-deterministic-fixturevalue-3278-deterministic-fixturevalue-3279-deterministic-fixturevalue-3280-deterministic-fixturevalue-3281-deterministic-fixturevalue-3282-deterministic-fixturevalue-3284-deterministic-fixturevalue-3285-deterministic-fixturevalue-3286-deterministic-fixturevalue-3287-deterministic-fixturevalue-3288-deterministic-fixturevalue-3289-deterministic-fixturevalue-3291-deterministic-fixturevalue-3292-deterministic-fixturevalue-3293-deterministic-fixturevalue-3294-deterministic-fixturevalue-3295-deterministic-fixturevalue-3296-deterministic-fixturevalue-3298-deterministic-fixturevalue-3299-deterministic-fixturevalue-3300-deterministic-fixturevalue-3301-deterministic-fixturevalue-3302-deterministic-fixturevalue-3303-deterministic-fixturevalue-3305-deterministic-fixturevalue-3306-deterministic-fixturevalue-3307-deterministic-fixturevalue-3308-deterministic-fixturevalue-3309-deterministic-fixturevalue-3310-deterministic-fixturevalue-3312-deterministic-fixturevalue-3313-deterministic-fixturevalue-3314-deterministic-fixturevalue-3315-deterministic-fixturevalue-3316-deterministic-fixturevalue-3317-deterministic-fixturevalue-3319-deterministic-fixturevalue-3320-deterministic-fixturevalue-3321-deterministic-fixturevalue-3322-deterministic-fixturevalue-3323-deterministic-fixturevalue-3324-deterministic-fixturevalue-3326-deterministic-fixturevalue-3327-deterministic-fixture$Dd$DDd$Dd$Dd$DDd$Dd$Dd $ D D d  + +$ +D +d + + + + + + $ D d  $ D D d   $ D d $Dd$DDdvalue-3328-deterministic-fixturevalue-3329-deterministic-fixturevalue-3330-deterministic-fixturevalue-3331-deterministic-fixturevalue-3333-deterministic-fixturevalue-3334-deterministic-fixturevalue-3335-deterministic-fixturevalue-3336-deterministic-fixturevalue-3337-deterministic-fixturevalue-3338-deterministic-fixturevalue-3340-deterministic-fixturevalue-3341-deterministic-fixturevalue-3342-deterministic-fixturevalue-3343-deterministic-fixturevalue-3344-deterministic-fixturevalue-3345-deterministic-fixturevalue-3347-deterministic-fixturevalue-3348-deterministic-fixturevalue-3349-deterministic-fixturevalue-3350-deterministic-fixturevalue-3351-deterministic-fixturevalue-3352-deterministic-fixturevalue-3354-deterministic-fixturevalue-3355-deterministic-fixturevalue-3356-deterministic-fixturevalue-3357-deterministic-fixturevalue-3358-deterministic-fixturevalue-3359-deterministic-fixturevalue-3361-deterministic-fixturevalue-3362-deterministic-fixturevalue-3363-deterministic-fixturevalue-3364-deterministic-fixturevalue-3365-deterministic-fixturevalue-3366-deterministic-fixturevalue-3368-deterministic-fixturevalue-3369-deterministic-fixturevalue-3370-deterministic-fixturevalue-3371-deterministic-fixturevalue-3372-deterministic-fixturevalue-3373-deterministic-fixturevalue-3375-deterministic-fixturevalue-3376-deterministic-fixturevalue-3377-deterministic-fixturevalue-3378-deterministic-fixturevalue-3379-deterministic-fixturevalue-3380-deterministic-fixturevalue-3382-deterministic-fixturevalue-3383-deterministic-fixturevalue-3384-deterministic-fixturevalue-3385-deterministic-fixturevalue-3386-deterministic-fixturevalue-3387-deterministic-fixturevalue-3389-deterministic-fixturevalue-3390-deterministic-fixturevalue-3391-deterministic-fixturevalue-3392-deterministic-fixturevalue-3393-deterministic-fixturevalue-3394-deterministic-fixturevalue-3396-deterministic-fixturevalue-3397-deterministic-fixturevalue-3398-deterministic-fixturevalue-3399-deterministic-fixturevalue-3400-deterministic-fixturevalue-3401-deterministic-fixturevalue-3403-deterministic-fixturevalue-3404-deterministic-fixturevalue-3405-deterministic-fixturevalue-3406-deterministic-fixturevalue-3407-deterministic-fixturevalue-3408-deterministic-fixturevalue-3410-deterministic-fixturevalue-3411-deterministic-fixturevalue-3412-deterministic-fixturevalue-3413-deterministic-fixturevalue-3414-deterministic-fixturevalue-3415-deterministic-fixturevalue-3417-deterministic-fixturevalue-3418-deterministic-fixturevalue-3419-deterministic-fixturevalue-3420-deterministic-fixturevalue-3421-deterministic-fixturevalue-3422-deterministic-fixturevalue-3424-deterministic-fixturevalue-3425-deterministic-fixturevalue-3426-deterministic-fixturevalue-3427-deterministic-fixturevalue-3428-deterministic-fixturevalue-3429-deterministic-fixturevalue-3431-deterministic-fixturevalue-3432-deterministic-fixturevalue-3433-deterministic-fixturevalue-3434-deterministic-fixturevalue-3435-deterministic-fixturevalue-3436-deterministic-fixturevalue-3438-deterministic-fixturevalue-3439-deterministic-fixturevalue-3440-deterministic-fixturevalue-3441-deterministic-fixturevalue-3442-deterministic-fixturevalue-3443-deterministic-fixturevalue-3445-deterministic-fixturevalue-3446-deterministic-fixturevalue-3447-deterministic-fixturevalue-3448-deterministic-fixturevalue-3449-deterministic-fixturevalue-3450-deterministic-fixturevalue-3452-deterministic-fixturevalue-3453-deterministic-fixturevalue-3454-deterministic-fixturevalue-3455-deterministic-fixture$DDd$Dd$Dd$DDd$Dd$Dd$DDd  $ D d  +$ +D +d + + + + + + $ D D d   $ D d  $ D d $DDd$Ddvalue-3456-deterministic-fixturevalue-3457-deterministic-fixturevalue-3459-deterministic-fixturevalue-3460-deterministic-fixturevalue-3461-deterministic-fixturevalue-3462-deterministic-fixturevalue-3463-deterministic-fixturevalue-3464-deterministic-fixturevalue-3466-deterministic-fixturevalue-3467-deterministic-fixturevalue-3468-deterministic-fixturevalue-3469-deterministic-fixturevalue-3470-deterministic-fixturevalue-3471-deterministic-fixturevalue-3473-deterministic-fixturevalue-3474-deterministic-fixturevalue-3475-deterministic-fixturevalue-3476-deterministic-fixturevalue-3477-deterministic-fixturevalue-3478-deterministic-fixturevalue-3480-deterministic-fixturevalue-3481-deterministic-fixturevalue-3482-deterministic-fixturevalue-3483-deterministic-fixturevalue-3484-deterministic-fixturevalue-3485-deterministic-fixturevalue-3487-deterministic-fixturevalue-3488-deterministic-fixturevalue-3489-deterministic-fixturevalue-3490-deterministic-fixturevalue-3491-deterministic-fixturevalue-3492-deterministic-fixturevalue-3494-deterministic-fixturevalue-3495-deterministic-fixturevalue-3496-deterministic-fixturevalue-3497-deterministic-fixturevalue-3498-deterministic-fixturevalue-3499-deterministic-fixturevalue-3501-deterministic-fixturevalue-3502-deterministic-fixturevalue-3503-deterministic-fixturevalue-3504-deterministic-fixturevalue-3505-deterministic-fixturevalue-3506-deterministic-fixturevalue-3508-deterministic-fixturevalue-3509-deterministic-fixturevalue-3510-deterministic-fixturevalue-3511-deterministic-fixturevalue-3512-deterministic-fixturevalue-3513-deterministic-fixturevalue-3515-deterministic-fixturevalue-3516-deterministic-fixturevalue-3517-deterministic-fixturevalue-3518-deterministic-fixturevalue-3519-deterministic-fixturevalue-3520-deterministic-fixturevalue-3522-deterministic-fixturevalue-3523-deterministic-fixturevalue-3524-deterministic-fixturevalue-3525-deterministic-fixturevalue-3526-deterministic-fixturevalue-3527-deterministic-fixturevalue-3529-deterministic-fixturevalue-3530-deterministic-fixturevalue-3531-deterministic-fixturevalue-3532-deterministic-fixturevalue-3533-deterministic-fixturevalue-3534-deterministic-fixturevalue-3536-deterministic-fixturevalue-3537-deterministic-fixturevalue-3538-deterministic-fixturevalue-3539-deterministic-fixturevalue-3540-deterministic-fixturevalue-3541-deterministic-fixturevalue-3543-deterministic-fixturevalue-3544-deterministic-fixturevalue-3545-deterministic-fixturevalue-3546-deterministic-fixturevalue-3547-deterministic-fixturevalue-3548-deterministic-fixturevalue-3550-deterministic-fixturevalue-3551-deterministic-fixturevalue-3552-deterministic-fixturevalue-3553-deterministic-fixturevalue-3554-deterministic-fixturevalue-3555-deterministic-fixturevalue-3557-deterministic-fixturevalue-3558-deterministic-fixturevalue-3559-deterministic-fixturevalue-3560-deterministic-fixturevalue-3561-deterministic-fixturevalue-3562-deterministic-fixturevalue-3564-deterministic-fixturevalue-3565-deterministic-fixturevalue-3566-deterministic-fixturevalue-3567-deterministic-fixturevalue-3568-deterministic-fixturevalue-3569-deterministic-fixturevalue-3571-deterministic-fixturevalue-3572-deterministic-fixturevalue-3573-deterministic-fixturevalue-3574-deterministic-fixturevalue-3575-deterministic-fixturevalue-3576-deterministic-fixturevalue-3578-deterministic-fixturevalue-3579-deterministic-fixturevalue-3580-deterministic-fixturevalue-3581-deterministic-fixturevalue-3582-deterministic-fixturevalue-3583-deterministic-fixture$Dd$Dd$DDd$Dd$Dd$DDd$Dd $ D d  +$ +D +D +d + + + + +  $ D d  $ D d  $ D D d $Dd$Ddvalue-3585-deterministic-fixturevalue-3586-deterministic-fixturevalue-3587-deterministic-fixturevalue-3588-deterministic-fixturevalue-3589-deterministic-fixturevalue-3590-deterministic-fixturevalue-3592-deterministic-fixturevalue-3593-deterministic-fixturevalue-3594-deterministic-fixturevalue-3595-deterministic-fixturevalue-3596-deterministic-fixturevalue-3597-deterministic-fixturevalue-3599-deterministic-fixturevalue-3600-deterministic-fixturevalue-3601-deterministic-fixturevalue-3602-deterministic-fixturevalue-3603-deterministic-fixturevalue-3604-deterministic-fixturevalue-3606-deterministic-fixturevalue-3607-deterministic-fixturevalue-3608-deterministic-fixturevalue-3609-deterministic-fixturevalue-3610-deterministic-fixturevalue-3611-deterministic-fixturevalue-3613-deterministic-fixturevalue-3614-deterministic-fixturevalue-3615-deterministic-fixturevalue-3616-deterministic-fixturevalue-3617-deterministic-fixturevalue-3618-deterministic-fixturevalue-3620-deterministic-fixturevalue-3621-deterministic-fixturevalue-3622-deterministic-fixturevalue-3623-deterministic-fixturevalue-3624-deterministic-fixturevalue-3625-deterministic-fixturevalue-3627-deterministic-fixturevalue-3628-deterministic-fixturevalue-3629-deterministic-fixturevalue-3630-deterministic-fixturevalue-3631-deterministic-fixturevalue-3632-deterministic-fixturevalue-3634-deterministic-fixturevalue-3635-deterministic-fixturevalue-3636-deterministic-fixturevalue-3637-deterministic-fixturevalue-3638-deterministic-fixturevalue-3639-deterministic-fixturevalue-3641-deterministic-fixturevalue-3642-deterministic-fixturevalue-3643-deterministic-fixturevalue-3644-deterministic-fixturevalue-3645-deterministic-fixturevalue-3646-deterministic-fixturevalue-3648-deterministic-fixturevalue-3649-deterministic-fixturevalue-3650-deterministic-fixturevalue-3651-deterministic-fixturevalue-3652-deterministic-fixturevalue-3653-deterministic-fixturevalue-3655-deterministic-fixturevalue-3656-deterministic-fixturevalue-3657-deterministic-fixturevalue-3658-deterministic-fixturevalue-3659-deterministic-fixturevalue-3660-deterministic-fixturevalue-3662-deterministic-fixturevalue-3663-deterministic-fixturevalue-3664-deterministic-fixturevalue-3665-deterministic-fixturevalue-3666-deterministic-fixturevalue-3667-deterministic-fixturevalue-3669-deterministic-fixturevalue-3670-deterministic-fixturevalue-3671-deterministic-fixturevalue-3672-deterministic-fixturevalue-3673-deterministic-fixturevalue-3674-deterministic-fixturevalue-3676-deterministic-fixturevalue-3677-deterministic-fixturevalue-3678-deterministic-fixturevalue-3679-deterministic-fixturevalue-3680-deterministic-fixturevalue-3681-deterministic-fixturevalue-3683-deterministic-fixturevalue-3684-deterministic-fixturevalue-3685-deterministic-fixturevalue-3686-deterministic-fixturevalue-3687-deterministic-fixturevalue-3688-deterministic-fixturevalue-3690-deterministic-fixturevalue-3691-deterministic-fixturevalue-3692-deterministic-fixturevalue-3693-deterministic-fixturevalue-3694-deterministic-fixturevalue-3695-deterministic-fixturevalue-3697-deterministic-fixturevalue-3698-deterministic-fixturevalue-3699-deterministic-fixturevalue-3700-deterministic-fixturevalue-3701-deterministic-fixturevalue-3702-deterministic-fixturevalue-3704-deterministic-fixturevalue-3705-deterministic-fixturevalue-3706-deterministic-fixturevalue-3707-deterministic-fixturevalue-3708-deterministic-fixturevalue-3709-deterministic-fixturevalue-3711-deterministic-fixture$Dd$Ddd$$Dd$Dd$Ddd$$Dd$Dd $ D d d  +$ +$ +D +d + + + + + + $ D d  $ D d d  $ $ D d $Dd$Dddvalue-3712-deterministic-fixturevalue-3713-deterministic-fixturevalue-3714-deterministic-fixturevalue-3715-deterministic-fixturevalue-3716-deterministic-fixturevalue-3718-deterministic-fixturevalue-3719-deterministic-fixturevalue-3720-deterministic-fixturevalue-3721-deterministic-fixturevalue-3722-deterministic-fixturevalue-3723-deterministic-fixturevalue-3725-deterministic-fixturevalue-3726-deterministic-fixturevalue-3727-deterministic-fixturevalue-3728-deterministic-fixturevalue-3729-deterministic-fixturevalue-3730-deterministic-fixturevalue-3732-deterministic-fixturevalue-3733-deterministic-fixturevalue-3734-deterministic-fixturevalue-3735-deterministic-fixturevalue-3736-deterministic-fixturevalue-3737-deterministic-fixturevalue-3739-deterministic-fixturevalue-3740-deterministic-fixturevalue-3741-deterministic-fixturevalue-3742-deterministic-fixturevalue-3743-deterministic-fixturevalue-3744-deterministic-fixturevalue-3746-deterministic-fixturevalue-3747-deterministic-fixturevalue-3748-deterministic-fixturevalue-3749-deterministic-fixturevalue-3750-deterministic-fixturevalue-3751-deterministic-fixturevalue-3753-deterministic-fixturevalue-3754-deterministic-fixturevalue-3755-deterministic-fixturevalue-3756-deterministic-fixturevalue-3757-deterministic-fixturevalue-3758-deterministic-fixturevalue-3760-deterministic-fixturevalue-3761-deterministic-fixturevalue-3762-deterministic-fixturevalue-3763-deterministic-fixturevalue-3764-deterministic-fixturevalue-3765-deterministic-fixturevalue-3767-deterministic-fixturevalue-3768-deterministic-fixturevalue-3769-deterministic-fixturevalue-3770-deterministic-fixturevalue-3771-deterministic-fixturevalue-3772-deterministic-fixturevalue-3774-deterministic-fixturevalue-3775-deterministic-fixturevalue-3776-deterministic-fixturevalue-3777-deterministic-fixturevalue-3778-deterministic-fixturevalue-3779-deterministic-fixturevalue-3781-deterministic-fixturevalue-3782-deterministic-fixturevalue-3783-deterministic-fixturevalue-3784-deterministic-fixturevalue-3785-deterministic-fixturevalue-3786-deterministic-fixturevalue-3788-deterministic-fixturevalue-3789-deterministic-fixturevalue-3790-deterministic-fixturevalue-3791-deterministic-fixturevalue-3792-deterministic-fixturevalue-3793-deterministic-fixturevalue-3795-deterministic-fixturevalue-3796-deterministic-fixturevalue-3797-deterministic-fixturevalue-3798-deterministic-fixturevalue-3799-deterministic-fixturevalue-3800-deterministic-fixturevalue-3802-deterministic-fixturevalue-3803-deterministic-fixturevalue-3804-deterministic-fixturevalue-3805-deterministic-fixturevalue-3806-deterministic-fixturevalue-3807-deterministic-fixturevalue-3809-deterministic-fixturevalue-3810-deterministic-fixturevalue-3811-deterministic-fixturevalue-3812-deterministic-fixturevalue-3813-deterministic-fixturevalue-3814-deterministic-fixturevalue-3816-deterministic-fixturevalue-3817-deterministic-fixturevalue-3818-deterministic-fixturevalue-3819-deterministic-fixturevalue-3820-deterministic-fixturevalue-3821-deterministic-fixturevalue-3823-deterministic-fixturevalue-3824-deterministic-fixturevalue-3825-deterministic-fixturevalue-3826-deterministic-fixturevalue-3827-deterministic-fixturevalue-3828-deterministic-fixturevalue-3830-deterministic-fixturevalue-3831-deterministic-fixturevalue-3832-deterministic-fixturevalue-3833-deterministic-fixturevalue-3834-deterministic-fixturevalue-3835-deterministic-fixturevalue-3837-deterministic-fixturevalue-3838-deterministic-fixturevalue-3839-deterministic-fixture$Ddd$$Dd$Dd$Ddd$$Dd$Dd$Ddd $ $ D d  +$ +D +d + + + + + + $ D d d  $ $ D d  $ D d $Ddd$$Ddvalue-3840-deterministic-fixturevalue-3841-deterministic-fixturevalue-3842-deterministic-fixturevalue-3844-deterministic-fixturevalue-3845-deterministic-fixturevalue-3846-deterministic-fixturevalue-3847-deterministic-fixturevalue-3848-deterministic-fixturevalue-3849-deterministic-fixturevalue-3851-deterministic-fixturevalue-3852-deterministic-fixturevalue-3853-deterministic-fixturevalue-3854-deterministic-fixturevalue-3855-deterministic-fixturevalue-3856-deterministic-fixturevalue-3858-deterministic-fixturevalue-3859-deterministic-fixturevalue-3860-deterministic-fixturevalue-3861-deterministic-fixturevalue-3862-deterministic-fixturevalue-3863-deterministic-fixturevalue-3865-deterministic-fixturevalue-3866-deterministic-fixturevalue-3867-deterministic-fixturevalue-3868-deterministic-fixturevalue-3869-deterministic-fixturevalue-3870-deterministic-fixturevalue-3872-deterministic-fixturevalue-3873-deterministic-fixturevalue-3874-deterministic-fixturevalue-3875-deterministic-fixturevalue-3876-deterministic-fixturevalue-3877-deterministic-fixturevalue-3879-deterministic-fixturevalue-3880-deterministic-fixturevalue-3881-deterministic-fixturevalue-3882-deterministic-fixturevalue-3883-deterministic-fixturevalue-3884-deterministic-fixturevalue-3886-deterministic-fixturevalue-3887-deterministic-fixturevalue-3888-deterministic-fixturevalue-3889-deterministic-fixturevalue-3890-deterministic-fixturevalue-3891-deterministic-fixturevalue-3893-deterministic-fixturevalue-3894-deterministic-fixturevalue-3895-deterministic-fixturevalue-3896-deterministic-fixturevalue-3897-deterministic-fixturevalue-3898-deterministic-fixturevalue-3900-deterministic-fixturevalue-3901-deterministic-fixturevalue-3902-deterministic-fixturevalue-3903-deterministic-fixturevalue-3904-deterministic-fixturevalue-3905-deterministic-fixturevalue-3907-deterministic-fixturevalue-3908-deterministic-fixturevalue-3909-deterministic-fixturevalue-3910-deterministic-fixturevalue-3911-deterministic-fixturevalue-3912-deterministic-fixturevalue-3914-deterministic-fixturevalue-3915-deterministic-fixturevalue-3916-deterministic-fixturevalue-3917-deterministic-fixturevalue-3918-deterministic-fixturevalue-3919-deterministic-fixturevalue-3921-deterministic-fixturevalue-3922-deterministic-fixturevalue-3923-deterministic-fixturevalue-3924-deterministic-fixturevalue-3925-deterministic-fixturevalue-3926-deterministic-fixturevalue-3928-deterministic-fixturevalue-3929-deterministic-fixturevalue-3930-deterministic-fixturevalue-3931-deterministic-fixturevalue-3932-deterministic-fixturevalue-3933-deterministic-fixturevalue-3935-deterministic-fixturevalue-3936-deterministic-fixturevalue-3937-deterministic-fixturevalue-3938-deterministic-fixturevalue-3939-deterministic-fixturevalue-3940-deterministic-fixturevalue-3942-deterministic-fixturevalue-3943-deterministic-fixturevalue-3944-deterministic-fixturevalue-3945-deterministic-fixturevalue-3946-deterministic-fixturevalue-3947-deterministic-fixturevalue-3949-deterministic-fixturevalue-3950-deterministic-fixturevalue-3951-deterministic-fixturevalue-3952-deterministic-fixturevalue-3953-deterministic-fixturevalue-3954-deterministic-fixturevalue-3956-deterministic-fixturevalue-3957-deterministic-fixturevalue-3958-deterministic-fixturevalue-3959-deterministic-fixturevalue-3960-deterministic-fixturevalue-3961-deterministic-fixturevalue-3963-deterministic-fixturevalue-3964-deterministic-fixturevalue-3965-deterministic-fixturevalue-3966-deterministic-fixturevalue-3967-deterministic-fixture$$Dd$Dd$Ddd$$Dd$Dd$Ddd$$Dd $ D d  +$ +D +d +d + + + + + $ $ D d  $ D d  $ D d d $$Dd$Ddvalue-3968-deterministic-fixturevalue-3970-deterministic-fixturevalue-3971-deterministic-fixturevalue-3972-deterministic-fixturevalue-3973-deterministic-fixturevalue-3974-deterministic-fixturevalue-3975-deterministic-fixturevalue-3977-deterministic-fixturevalue-3978-deterministic-fixturevalue-3979-deterministic-fixturevalue-3980-deterministic-fixturevalue-3981-deterministic-fixturevalue-3982-deterministic-fixturevalue-3984-deterministic-fixturevalue-3985-deterministic-fixturevalue-3986-deterministic-fixturevalue-3987-deterministic-fixturevalue-3988-deterministic-fixturevalue-3989-deterministic-fixturevalue-3991-deterministic-fixturevalue-3992-deterministic-fixturevalue-3993-deterministic-fixturevalue-3994-deterministic-fixturevalue-3995-deterministic-fixturevalue-3996-deterministic-fixturevalue-3998-deterministic-fixturevalue-3999-deterministic-fixturevalue-4000-deterministic-fixturevalue-4001-deterministic-fixturevalue-4002-deterministic-fixturevalue-4003-deterministic-fixturevalue-4005-deterministic-fixturevalue-4006-deterministic-fixturevalue-4007-deterministic-fixturevalue-4008-deterministic-fixturevalue-4009-deterministic-fixturevalue-4010-deterministic-fixturevalue-4012-deterministic-fixturevalue-4013-deterministic-fixturevalue-4014-deterministic-fixturevalue-4015-deterministic-fixturevalue-4016-deterministic-fixturevalue-4017-deterministic-fixturevalue-4019-deterministic-fixturevalue-4020-deterministic-fixturevalue-4021-deterministic-fixturevalue-4022-deterministic-fixturevalue-4023-deterministic-fixturevalue-4024-deterministic-fixturevalue-4026-deterministic-fixturevalue-4027-deterministic-fixturevalue-4028-deterministic-fixturevalue-4029-deterministic-fixturevalue-4030-deterministic-fixturevalue-4031-deterministic-fixturevalue-4033-deterministic-fixturevalue-4034-deterministic-fixturevalue-4035-deterministic-fixturevalue-4036-deterministic-fixturevalue-4037-deterministic-fixturevalue-4038-deterministic-fixturevalue-4040-deterministic-fixturevalue-4041-deterministic-fixturevalue-4042-deterministic-fixturevalue-4043-deterministic-fixturevalue-4044-deterministic-fixturevalue-4045-deterministic-fixturevalue-4047-deterministic-fixturevalue-4048-deterministic-fixturevalue-4049-deterministic-fixturevalue-4050-deterministic-fixturevalue-4051-deterministic-fixturevalue-4052-deterministic-fixturevalue-4054-deterministic-fixturevalue-4055-deterministic-fixturevalue-4056-deterministic-fixturevalue-4057-deterministic-fixturevalue-4058-deterministic-fixturevalue-4059-deterministic-fixturevalue-4061-deterministic-fixturevalue-4062-deterministic-fixturevalue-4063-deterministic-fixturevalue-4064-deterministic-fixturevalue-4065-deterministic-fixturevalue-4066-deterministic-fixturevalue-4068-deterministic-fixturevalue-4069-deterministic-fixturevalue-4070-deterministic-fixturevalue-4071-deterministic-fixturevalue-4072-deterministic-fixturevalue-4073-deterministic-fixturevalue-4075-deterministic-fixturevalue-4076-deterministic-fixturevalue-4077-deterministic-fixturevalue-4078-deterministic-fixturevalue-4079-deterministic-fixturevalue-4080-deterministic-fixturevalue-4082-deterministic-fixturevalue-4083-deterministic-fixturevalue-4084-deterministic-fixturevalue-4085-deterministic-fixturevalue-4086-deterministic-fixturevalue-4087-deterministic-fixturevalue-4089-deterministic-fixturevalue-4090-deterministic-fixturevalue-4091-deterministic-fixturevalue-4092-deterministic-fixturevalue-4093-deterministic-fixturevalue-4094-deterministic-fixtureHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJJ HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"Ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$dd @@ @H@ @@ @!@@@ @@ @!@@ " sL# $ $X $) 3 $&%  c 6L&i +X $@' $V( f L)#$v + * XS * 421QjdŜ924QrdŨz=27Qƴ|A2:Rd~D2=Rd̖I2@RdؖM2CRd䖄Q2FR𖈌U2LSdX2OSda2RSd&uь):tuLI;t&v Y;Cuv̱;su&ьɣA5]$j @E5`j!L̟M5c$j" DXQ5fU"#Vǰ4U*#vǼ5鎙SV2$6 V:6)V%8IVJ&9 WR&9CWZ:sWb';ɏWr((<鏽Wz(4e@2y&eԌ@2|eL—A2&e ×A2&׌ėA2&eLŗB2&e Ɨ@B2eƘB2&e܌ǘB2eLȘC2&e ɘYC 5yk ӀI5cy6ӌi5ykFӘ 5ykVӤ5y"k`Ӱɦ5#z*kvӼ香5SzȠ 5z:kԠ 5zJkI5zRki5{!$ +)$ L,I'$ - XC*-s-$. 0$  0"X3 0 6$!1 (3Sw& Y>w|̺)>w}L ?w&~ iY?x&̽?Cx&Ҍ?sxLɥ@x& Y@xӢj#FT5i$j$HY5l$j%LJ]5o$& a5r$j' Nd5uj(L,Pi5x$j* 8TXm5~j+Dq5$,PXy5j-LhZ|5$j. t\X< X)&@=IcX0L>iX*FX?X+Vd@ X+fpAɐ#Yp|BSY,ȈB Y-ȔC)Y-ȬD Y.ȸEiZ2eɘC2&eʘC2L˘YD2&e̘ęD2&e͘D2eLΘE2&e ϘYE2ϘΙE2&eИE2eLјF2&e 5s{bk 5{jk駹5{rk( 5|zk4)53|&@I5c|k6L 5|kFX5|kPp5|f|ɨ5#}kԈ 5"Dd2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2D2D2D"D"L"L"L&L&H&H&H$H$I$I$ɒ$$ddd2d2DH@ @@ @@ @H@ @@ @!@@@ @@ @ie(Vx +@jnԖ{> +jqզ|M@Jkt(} AjZkwAkC}(րBksMC +l( D[lӆ("p308*p4޴| +Ҁ*1' +ܘc17Nj*2GˊҜû*3W̪ܙ3gN#*4w +\S4 +Ӝ**ܚ5NJ*6j\;v;( +v ;vM"[ ;v$$;(0;*vM<*;(2v H,[;(:vT.%;(l);ȨRvMx2-;(Zv DM +posPNʽuo\OypӜhP +}ptQ*p3&߀R +qc +6ߌSjq@ߘSqÝV߰Tq*f߼Uʾr#2V +\8*pKک8*=pL۩8*>pMݩ\8>Nݩ8*qPީܟ8?qQNߩ8*@qR੤\8@S੦8*AqTᩨܠ8*qUN⩪8*Bv*a; Je;v, +h;v8m;CvDq;svPu;\};vt +;w뀲*օ;3 +w0댲j։;G똲D +l(&EJlc6MF + +m(F G mÒ(PHmfMIn#(v J[nS܆J +n(ݖK*n(MLJo(޶ Mj@oA8?~E8p@̨H8pAبM8ÚpBQ8ƚpC𨆼U8ɛEY8ϛpF\8қpGe8՛pH,i8؛I8m8ۜpJ6ѪӜs*7ܛ*N\ӽ8 +Ԁ*9 +ܜ39'NJc*0j\:G׊Ԁþ*;Wتܝ;gN +#*< +Մ4[1;Ψbv͐65;(jv89;( @=;רzvʹ<@;(v@E;(vM̱BI;(v ر@Q;樢vFU;(vHX;쨲vMJ];(r:V*rBWJsYjsRY +sCZZssb[ʿtj\꿽tӟz4\ +t@] +t36L^ju@XqV@8BqW㩮ܡ8CXN婰8+qY橲\8 Dq[8+Eq\窸ܢ8Eq]N誺8+Fq^骼\8+q_骾8+Gq`8 GqaN֍;wW뤲 +;"wg밲֕;#*wp뼲֙;S2wȲ +ם;Բ*ס;Bw +;Rwj׭;Zwױ;C׵;jw +;rw2"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"" " " & &&&$$ $ $ $ $ $ $ $ 2$ 2 22"!@@ @@ H@ @@ʬUޝs,VǣЬNW@ӬX ,Y+3, NZKc,7[k@ɓ⬒G[,W\ 謓gN]#,^ Ĺ={йV=ܹX={Z={\> +|`> +| b >d>"|0f>*|<>2|Hj>@ / / _ / O / _/ / O / ^-O- ^ !- $ O'-^*-- 0O"g L⚚WgQ`iU#㪜wjYSj ]΃㺝k `㝠lKi8mkmDmqCPn tϣ\oy>C/|>F/|Ï>I|O>L/_>R/|>U/|ȏ>X|O>[/_>^|>a/|̏>d|O           ]ʃ^+ʳ,_ ,ak],aC,bsOc̣-d ^d  -e+3 7Ofk͓-GTl!>"B|`n$>%J|lp)>(R|x->+Z|v1>1x5>4j|z<>7z|A>:|~E>=̺I>@|غ _ O / @ / /O / @ / O 3-$6-&9O<-,^B.E-0HOK-4^N6Q-8T-Ohp }tq+3'rKc +7s ѓGsPtgu#2wvS:v+ҳBwKJxk嗢@>g|>j/Ϗ>m/}O>s/}>v/}>y/>|/}>/}>/}>/}>/   HHHHHHHHHHHHHHHHHHHHHHHHwwHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @@ @@ @@ @@ @@!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaaaAaa`a!aaaaaaaAaa`a!aaaaaaaAaa`a!aaaaHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @    +@ + + + @    @    @   @@@@@@@@@@@@@@@@@@ @   !@!!!"@"""#@###$@$$$%@%%%&@&&&'@'''(@((()@)))*@***+@+++,@,,,-@---.@.../@///0@0001@1112@2223@3334@4445@5556@6667@7778@8889@999:@:::;@;;;<@<<<=@===>@>>>?@???@@@@@A@AAAB@BBBC@CCCD@DDDE@EEEF@FFFG@GGGH@HHHI@IIIJ@JJJK@KKKL@LLLM@MMMN@NNNO@OOOP@PPPQ@QQQR@RRRS@SSST@TTTU@UUUV@VVVW@WWWX@XXXY@YYYZ@ZZZ[@[[[\@\\\]@]]]^@^^^_@___`@```a@aaab@bbbc@cccd@ddde@eeef@fffg@gggh@hhhi@iiij@jjjk@kkkl@lllm@mmmn@nnno@ooop@pppq@qqqr@rrrs@ssst@tttu@uuuv@vvvw@wwwx@xxxy@yyyz@zzz{@{{{|@|||}@}}}~@~~~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHblob-4096-deterministic-payloadHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH value-4096-deterministic-fixtureHHHHHHHHHHHHPHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  0HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH  greenblueredHHHHHHHHHHHHHHHHHHHHHHHHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + +id *int328 +Cname *string08R" +lance-encoding:compressionnone +items *list08 +item *int3208 +ecategory *dict:string:int8:false08BR. +&lance-encoding:dict-values-compressionnone +Bblob * large_binary08R +lance-encoding:blobtrue +)' +% +/lance.encodings.ColumnEncoding +I + +"75 +3 +/lance.encodings21.PageLayout +* 28HPL +  "75 +3 +/lance.encodings21.PageLayout +* 28HP(L + "75 +3 +/lance.encodings21.PageLayout +* 28HP(L + "75 +3 +/lance.encodings21.PageLayout +* 28HP(7"0. +, +/lance.encodings21.PageLayout  *2( +)' +% +/lance.encodings.ColumnEncoding +T + "A? += +/lance.encodings21.PageLayout +* + + 28HPW +  "A? += +/lance.encodings21.PageLayout +* + + 28HP(W + "A? += +/lance.encodings21.PageLayout +* + + 28HP(W + "A? += +/lance.encodings21.PageLayout +* + + 28HP(9 +4"*( +& +/lance.encodings21.PageLayout*( +)' +% +/lance.encodings.ColumnEncoding +h +  .0"RP +N +/lance.encodings21.PageLayout- ++ + +" + +" +* 28@HPk +  20"RP +N +/lance.encodings21.PageLayout- ++ + +" + +" +* 28@HP(k +  40"RP +N +/lance.encodings21.PageLayout- ++ + +" + +" +* 28@HP(k +  50"RP +N +/lance.encodings21.PageLayout- ++ + +" + +" +* 28@HP(\ + 0"EC +A +/lance.encodings21.PageLayout + + +* + 28@HP( +)' +% +/lance.encodings.ColumnEncoding +_ + $"IG +E +/lance.encodings21.PageLayout$ +"**" + + (28HPb + $"IG +E +/lance.encodings21.PageLayout$ +"**" + + (28HP(b + $"IG +E +/lance.encodings21.PageLayout$ +"**" + + (28HP(b + $"IG +E +/lance.encodings21.PageLayout$ +"**" + + (28HP(Y + $"B@ +> +/lance.encodings21.PageLayout + +" + + (28HP( +)' +% +/lance.encodings.ColumnEncoding +Z +"GE +C +/lance.encodings21.PageLayout"" + + b +@@ +28HP] +"GE +C +/lance.encodings21.PageLayout"" + + b +@@ +28HP(] +"GE +C +/lance.encodings21.PageLayout"" + + b +@@ +28HP(] +"GE +C +/lance.encodings21.PageLayout"" + + b +@@ +28HP(Y +"FD +B +/lance.encodings21.PageLayout!" + + b +@@ +28HP( ǚ`':atGǚsäLANC \ No newline at end of file diff --git a/rust/lance-index/src/scalar/lance_format.rs b/rust/lance-index/src/scalar/lance_format.rs index 76ebd0cf0da..81e36c4f6c1 100644 --- a/rust/lance-index/src/scalar/lance_format.rs +++ b/rust/lance-index/src/scalar/lance_format.rs @@ -12,9 +12,10 @@ use futures::TryStreamExt; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, Result, cache::LanceCache}; use lance_encoding::decoder::{DecoderPlugins, FilterExpression}; -use lance_encoding::version::LanceFileVersion; -use lance_file::previous::reader::FileReader as PreviousFileReader; -use lance_file::reader::{FileReader as CurrentFileReader, FileReaderOptions, ReaderProjection}; +use lance_file::reader::{FileReader as CurrentFileReader, FileReaderOptions}; +use lance_file::version::ConcreteFileVersion; +use lance_file::versions::v1::reader::FileReader as V1FileReader; +use lance_file::versions::{self, OpenedFileReader}; use lance_file::writer as current_writer; use lance_io::scheduler::{ScanScheduler, SchedulerConfig}; use lance_io::utils::CachedFileSize; @@ -41,7 +42,7 @@ pub struct LanceIndexStore { /// Cached file sizes (filename -> size in bytes) /// When set, used to avoid HEAD calls when opening files file_sizes: HashMap, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, /// Base I/O priority for all requests this store submits to `scheduler`. io_priority: u64, } @@ -68,7 +69,7 @@ impl LanceIndexStore { object_store, index_dir, metadata_cache, - LanceFileVersion::V2_0, + ConcreteFileVersion::V2_0, ) } @@ -77,7 +78,7 @@ impl LanceIndexStore { object_store: Arc, index_dir: Path, metadata_cache: Arc, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, ) -> Self { let scheduler = ScanScheduler::new( object_store.clone(), @@ -167,11 +168,11 @@ impl IndexWriter for LanceIndexWriter { } } -/// Newtype wrapper to allow implementing IndexReader for PreviousFileReader (a foreign type) -struct PreviousIndexReader(PreviousFileReader); +/// Newtype wrapper to allow implementing IndexReader for V1FileReader (a foreign type) +struct V1IndexReader(V1FileReader); #[async_trait] -impl IndexReader for PreviousIndexReader { +impl IndexReader for V1IndexReader { async fn read_record_batch(&self, offset: u64, _batch_size: u64) -> Result { self.0 .read_batch(offset as i32, ReadBatchParams::RangeFull, self.0.schema()) @@ -199,7 +200,7 @@ impl IndexReader for PreviousIndexReader { } fn schema(&self) -> &lance_core::datatypes::Schema { - PreviousFileReader::schema(&self.0) + V1FileReader::schema(&self.0) } } @@ -230,13 +231,16 @@ impl IndexReader for CurrentIndexReader { ))); } let projection = if let Some(projection) = projection { - ReaderProjection::from_column_names( + versions::reader_projection_from_column_names( self.0.metadata().version(), self.0.schema(), projection, )? } else { - ReaderProjection::from_whole_schema(self.0.schema(), self.0.metadata().version()) + versions::reader_projection_from_whole_schema( + self.0.schema(), + self.0.metadata().version(), + ) }; let batches = self .0 @@ -268,13 +272,16 @@ impl IndexReader for CurrentIndexReader { return empty_batch(); } let projection = if let Some(projection) = projection { - ReaderProjection::from_column_names( + versions::reader_projection_from_column_names( self.0.metadata().version(), self.0.schema(), projection, )? } else { - ReaderProjection::from_whole_schema(self.0.schema(), self.0.metadata().version()) + versions::reader_projection_from_whole_schema( + self.0.schema(), + self.0.metadata().version(), + ) }; // `DecodeBatchScheduler::schedule_ranges` requires sorted, // non-overlapping ranges; sort internally and permute the @@ -346,13 +353,16 @@ impl IndexReader for CurrentIndexReader { ))); } let projection = if let Some(projection) = projection { - ReaderProjection::from_column_names( + versions::reader_projection_from_column_names( self.0.metadata().version(), self.0.schema(), projection, )? } else { - ReaderProjection::from_whole_schema(self.0.schema(), self.0.metadata().version()) + versions::reader_projection_from_whole_schema( + self.0.schema(), + self.0.metadata().version(), + ) }; self.0 .read_stream_projected( @@ -408,13 +418,11 @@ impl IndexStore for LanceIndexStore { let path = self.index_file_path(name)?; let schema = schema.as_ref().try_into()?; let writer = self.object_store.create(&path).await?; - let writer = current_writer::FileWriter::try_new( + let writer = versions::create_writer( + self.format_version, writer, schema, - current_writer::FileWriterOptions { - format_version: Some(self.format_version), - ..Default::default() - }, + current_writer::FileWriterOptions::default(), )?; Ok(Box::new(LanceIndexWriter { path: name.to_string(), @@ -443,31 +451,24 @@ impl IndexStore for LanceIndexStore { .scheduler .open_file_with_priority(&path, self.io_priority, &cached_size) .await?; - match CurrentFileReader::try_open( + match versions::open_self_described_reader( file_scheduler, - None, Arc::::default(), &self.metadata_cache, FileReaderOptions::default(), ) - .await + .await? { - Ok(reader) => Ok(Arc::new(CurrentIndexReader(reader))), - Err(e) => { - // If the error is a version conflict we can try to read the file with v1 reader - if let Error::VersionConflict { .. } = e { - let path = self.index_file_path(name)?; - let file_reader = PreviousFileReader::try_new_self_described( - &self.object_store, - &path, - Some(&self.metadata_cache), - ) - .await?; - Ok(Arc::new(PreviousIndexReader(file_reader))) - } else { - Err(e) - } + OpenedFileReader::V1 { .. } => { + let reader = V1FileReader::try_new_self_described( + &self.object_store, + &path, + Some(&self.metadata_cache), + ) + .await?; + Ok(Arc::new(V1IndexReader(reader))) } + OpenedFileReader::Current(reader) => Ok(Arc::new(CurrentIndexReader(reader))), } } diff --git a/rust/lance-index/src/vector/bq/storage.rs b/rust/lance-index/src/vector/bq/storage.rs index 72fa8d4b056..f1d0cd2f421 100644 --- a/rust/lance-index/src/vector/bq/storage.rs +++ b/rust/lance-index/src/vector/bq/storage.rs @@ -22,7 +22,7 @@ use itertools::{Itertools, izip}; use lance_arrow::{ArrowFloatType, FixedSizeListArrayExt, FloatArray, RecordBatchExt}; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, ROW_ID, Result}; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_linalg::distance::{DistanceType, Dot, dot, l2::l2}; use lance_linalg::simd::{ self, @@ -464,7 +464,7 @@ impl QuantizerMetadata for RabitQuantizationMetadata { } } - async fn load(reader: &PreviousFileReader) -> Result { + async fn load(reader: &V1FileReader) -> Result { let metadata_str = reader .schema() .metadata @@ -2545,7 +2545,7 @@ impl QuantizerStorage for RabitQuantizationStorage { } async fn load_partition( - reader: &PreviousFileReader, + reader: &V1FileReader, range: std::ops::Range, distance_type: DistanceType, metadata: &Self::Metadata, diff --git a/rust/lance-index/src/vector/distributed/index_merger.rs b/rust/lance-index/src/vector/distributed/index_merger.rs index 19aa3fa177d..4afa946b2a1 100755 --- a/rust/lance-index/src/vector/distributed/index_merger.rs +++ b/rust/lance-index/src/vector/distributed/index_merger.rs @@ -39,8 +39,9 @@ use crate::{INDEX_AUXILIARY_FILE_NAME, INDEX_METADATA_SCHEMA_KEY}; use arrow_schema::{DataType, Field, Schema as ArrowSchema}; use bytes::Bytes; use lance_core::datatypes::Schema as LanceSchema; -use lance_encoding::version::LanceFileVersion; use lance_file::reader::{FileReader as V2Reader, FileReaderOptions as V2ReaderOptions}; +use lance_file::version::ConcreteFileVersion; +use lance_file::versions; use lance_file::writer::{FileWriter as V2Writer, FileWriter, FileWriterOptions}; use lance_io::scheduler::{ScanScheduler, SchedulerConfig}; use lance_io::utils::CachedFileSize; @@ -249,7 +250,7 @@ pub async fn init_writer_for_flat( d0: usize, item_type: &DataType, dt: DistanceType, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, ) -> Result { let arrow_schema = ArrowSchema::new(vec![ (*ROW_ID_FIELD).clone(), @@ -263,13 +264,11 @@ pub async fn init_writer_for_flat( ), ]); let writer = object_store.create(aux_out).await?; - let mut w = FileWriter::try_new( + let mut w = versions::create_writer( + format_version, writer, LanceSchema::try_from(&arrow_schema)?, - FileWriterOptions { - format_version: Some(format_version), - ..Default::default() - }, + FileWriterOptions::default(), )?; let meta_json = serde_json::to_string(&FlatMetadata { dim: d0 })?; init_writer_for_storage(&mut w, dt, &meta_json, "")?; @@ -285,7 +284,7 @@ pub async fn init_writer_for_pq( aux_out: &object_store::path::Path, dt: DistanceType, pm: &ProductQuantizationMetadata, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, ) -> Result { let num_bytes = if pm.nbits == 4 { pm.num_sub_vectors / 2 @@ -304,13 +303,11 @@ pub async fn init_writer_for_pq( ), ]); let writer = object_store.create(aux_out).await?; - let mut w = FileWriter::try_new( + let mut w = versions::create_writer( + format_version, writer, LanceSchema::try_from(&arrow_schema)?, - FileWriterOptions { - format_version: Some(format_version), - ..Default::default() - }, + FileWriterOptions::default(), )?; let mut pm_init = pm.clone(); let cb = pm_init @@ -332,7 +329,7 @@ pub async fn init_writer_for_sq( aux_out: &object_store::path::Path, dt: DistanceType, sq_meta: &ScalarQuantizationMetadata, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, ) -> Result { let d0 = sq_meta.dim; let arrow_schema = ArrowSchema::new(vec![ @@ -347,13 +344,11 @@ pub async fn init_writer_for_sq( ), ]); let writer = object_store.create(aux_out).await?; - let mut w = FileWriter::try_new( + let mut w = versions::create_writer( + format_version, writer, LanceSchema::try_from(&arrow_schema)?, - FileWriterOptions { - format_version: Some(format_version), - ..Default::default() - }, + FileWriterOptions::default(), )?; let meta_json = serde_json::to_string(sq_meta)?; init_writer_for_storage(&mut w, dt, &meta_json, SQ_METADATA_KEY)?; @@ -366,7 +361,7 @@ pub async fn init_writer_for_rq( aux_out: &object_store::path::Path, dt: DistanceType, rq_meta: &RabitQuantizationMetadata, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, ) -> Result { let mut fields = vec![ (*ROW_ID_FIELD).clone(), @@ -384,13 +379,11 @@ pub async fn init_writer_for_rq( } let arrow_schema = ArrowSchema::new(fields); let writer = object_store.create(aux_out).await?; - let mut w = FileWriter::try_new( + let mut w = versions::create_writer( + format_version, writer, LanceSchema::try_from(&arrow_schema)?, - FileWriterOptions { - format_version: Some(format_version), - ..Default::default() - }, + FileWriterOptions::default(), )?; let mut rq_meta_init = rq_meta.clone(); @@ -794,7 +787,7 @@ pub async fn merge_partial_vector_auxiliary_files( let mut dim: Option = None; let mut detected_index_type: Option = None; // Inherit file format version from the first shard (set on first iteration) - let mut format_version: Option = None; + let mut format_version: Option = None; // Prepare output path; we'll create writer once when we know schema let aux_out = target_dir.clone().join(INDEX_AUXILIARY_FILE_NAME); @@ -955,8 +948,9 @@ pub async fn merge_partial_vector_auxiliary_files( let idx_type = detected_index_type .ok_or_else(|| Error::index("Unable to detect index type".to_string()))?; - // Compute format version once; defaults to V2_0 if no shards processed yet - let fv = format_version.unwrap_or(LanceFileVersion::V2_0); + let fv = format_version.ok_or_else(|| { + Error::internal("first shard did not provide a concrete file version".to_string()) + })?; match idx_type { SupportedIvfIndexType::IvfSq => { @@ -1670,7 +1664,7 @@ mod tests { ]); let writer = store.create(aux_path).await?; - let mut v2w = V2Writer::try_new( + let mut v2w = versions::v2_1::create_writer( writer, lance_core::datatypes::Schema::try_from(&arrow_schema)?, V2WriterOptions::default(), @@ -1740,7 +1734,7 @@ mod tests { ]); let writer = store.create(aux_path).await?; - let mut v2w = V2Writer::try_new( + let mut v2w = versions::v2_1::create_writer( writer, lance_core::datatypes::Schema::try_from(&arrow_schema)?, V2WriterOptions::default(), @@ -2096,7 +2090,7 @@ mod tests { ]); let writer = store.create(aux_path).await?; - let mut v2w = V2Writer::try_new( + let mut v2w = versions::v2_1::create_writer( writer, lance_core::datatypes::Schema::try_from(&arrow_schema)?, V2WriterOptions::default(), @@ -2208,7 +2202,7 @@ mod tests { let arrow_schema = ArrowSchema::new(fields); let writer = store.create(aux_path).await?; - let mut v2w = V2Writer::try_new( + let mut v2w = versions::v2_1::create_writer( writer, lance_core::datatypes::Schema::try_from(&arrow_schema)?, V2WriterOptions::default(), diff --git a/rust/lance-index/src/vector/flat/index.rs b/rust/lance-index/src/vector/flat/index.rs index 89c28a6ffd7..3a17208d469 100644 --- a/rust/lance-index/src/vector/flat/index.rs +++ b/rust/lance-index/src/vector/flat/index.rs @@ -13,7 +13,7 @@ use arrow_array::{Array, ArrayRef, Float32Array, RecordBatch, UInt64Array}; use arrow_schema::{DataType, Field, Schema, SchemaRef}; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, ROW_ID_FIELD, Result}; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_linalg::distance::DistanceType; use serde::{Deserialize, Serialize}; @@ -329,7 +329,7 @@ pub struct FlatMetadata { #[async_trait::async_trait] impl QuantizerMetadata for FlatMetadata { - async fn load(_: &PreviousFileReader) -> Result { + async fn load(_: &V1FileReader) -> Result { unimplemented!("Flat will be used in new index builder which doesn't require this") } } diff --git a/rust/lance-index/src/vector/flat/storage.rs b/rust/lance-index/src/vector/flat/storage.rs index ff8756cbe41..23b3f2cadc9 100644 --- a/rust/lance-index/src/vector/flat/storage.rs +++ b/rust/lance-index/src/vector/flat/storage.rs @@ -19,7 +19,7 @@ use arrow_array::{ use arrow_schema::{DataType, SchemaRef}; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, ROW_ID, Result}; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_linalg::distance::hamming::hamming; use lance_linalg::distance::{Cosine, DistanceType, Dot, L2}; @@ -87,7 +87,7 @@ impl QuantizerStorage for FlatFloatStorage { } async fn load_partition( - _: &PreviousFileReader, + _: &V1FileReader, _: std::ops::Range, _: DistanceType, _: &Self::Metadata, @@ -249,7 +249,7 @@ impl QuantizerStorage for FlatBinStorage { } async fn load_partition( - _: &PreviousFileReader, + _: &V1FileReader, _: std::ops::Range, _: DistanceType, _: &Self::Metadata, diff --git a/rust/lance-index/src/vector/hnsw/builder.rs b/rust/lance-index/src/vector/hnsw/builder.rs index a020d011fba..e312817c567 100644 --- a/rust/lance-index/src/vector/hnsw/builder.rs +++ b/rust/lance-index/src/vector/hnsw/builder.rs @@ -1315,11 +1315,9 @@ mod tests { use arrow_schema::Schema; use lance_arrow::FixedSizeListArrayExt; use lance_core::deepsize::DeepSizeOf; - use lance_file::previous::{ - reader::FileReader as PreviousFileReader, - writer::{ - FileWriter as PreviousFileWriter, FileWriterOptions as PreviousFileWriterOptions, - }, + use lance_file::versions::v1::{ + reader::FileReader as V1FileReader, + writer::{FileWriter as V1FileWriter, FileWriterOptions as V1FileWriterOptions}, }; use lance_io::object_store::ObjectStore; use lance_linalg::distance::DistanceType; @@ -1366,10 +1364,10 @@ mod tests { DISTS_FIELD.clone(), ]); let schema = lance_core::datatypes::Schema::try_from(&schema).unwrap(); - let mut writer = PreviousFileWriter::::with_object_writer( + let mut writer = V1FileWriter::::with_object_writer( writer, schema, - &PreviousFileWriterOptions::default(), + &V1FileWriterOptions::default(), ) .unwrap(); let batch = builder.to_batch().unwrap(); @@ -1377,7 +1375,7 @@ mod tests { writer.write(&[batch]).await.unwrap(); writer.finish_with_metadata(&metadata).await.unwrap(); - let reader = PreviousFileReader::try_new_self_described(&object_store, &path, None) + let reader = V1FileReader::try_new_self_described(&object_store, &path, None) .await .unwrap(); let batch = reader @@ -1427,10 +1425,10 @@ mod tests { DISTS_FIELD.clone(), ]); let schema = lance_core::datatypes::Schema::try_from(&schema).unwrap(); - let mut writer = PreviousFileWriter::::with_object_writer( + let mut writer = V1FileWriter::::with_object_writer( writer, schema, - &PreviousFileWriterOptions::default(), + &V1FileWriterOptions::default(), ) .unwrap(); let batch = builder.to_batch().unwrap(); @@ -1438,7 +1436,7 @@ mod tests { writer.write(&[batch]).await.unwrap(); writer.finish_with_metadata(&metadata).await.unwrap(); - let reader = PreviousFileReader::try_new_self_described(&object_store, &path, None) + let reader = V1FileReader::try_new_self_described(&object_store, &path, None) .await .unwrap(); let batch = reader diff --git a/rust/lance-index/src/vector/hnsw/index.rs b/rust/lance-index/src/vector/hnsw/index.rs index dca9ea78955..4a5d23e63f2 100644 --- a/rust/lance-index/src/vector/hnsw/index.rs +++ b/rust/lance-index/src/vector/hnsw/index.rs @@ -16,7 +16,7 @@ use lance_arrow::RecordBatchExt; use lance_core::ROW_ID; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, Result, datatypes::Schema}; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_io::traits::Reader; use lance_linalg::distance::DistanceType; use lance_table::format::SelfDescribingFileReader; @@ -70,8 +70,7 @@ impl HNSWIndex { aux_reader: Arc, options: HNSWIndexOptions, ) -> Result { - let reader = - PreviousFileReader::try_new_self_described_from_reader(reader.clone(), None).await?; + let reader = V1FileReader::try_new_self_described_from_reader(reader.clone(), None).await?; let partition_metadata = match reader.schema().metadata.get(IVF_PARTITION_KEY) { Some(json) => { @@ -215,7 +214,7 @@ impl VectorIndex for HNSWIndex { VECTOR_ID_FIELD.clone(), ]))?; - let reader = PreviousFileReader::try_new_from_reader( + let reader = V1FileReader::try_new_from_reader( reader.path(), reader.clone(), None, @@ -247,7 +246,7 @@ impl VectorIndex for HNSWIndex { length: usize, partition_id: usize, ) -> Result> { - let reader = PreviousFileReader::try_new_self_described_from_reader(reader, None).await?; + let reader = V1FileReader::try_new_self_described_from_reader(reader, None).await?; let metadata = self.get_partition_metadata(partition_id)?; let storage = Arc::new(self.partition_storage.load_partition(partition_id).await?); diff --git a/rust/lance-index/src/vector/ivf/shuffler.rs b/rust/lance-index/src/vector/ivf/shuffler.rs index 175d5ecb9fb..57254a42f8c 100644 --- a/rust/lance-index/src/vector/ivf/shuffler.rs +++ b/rust/lance-index/src/vector/ivf/shuffler.rs @@ -32,10 +32,11 @@ use lance_core::utils::futures::StreamOnDropExt; use lance_core::utils::tokio::get_num_compute_intensive_cpus; use lance_core::{Error, ROW_ID, Result, datatypes::Schema}; use lance_encoding::decoder::{DecoderPlugins, FilterExpression}; -use lance_encoding::version::LanceFileVersion; -use lance_file::previous::reader::FileReader as PreviousFileReader; -use lance_file::previous::writer::FileWriter as PreviousFileWriter; use lance_file::reader::{FileReader as Lancev2FileReader, FileReaderOptions}; +use lance_file::version::ConcreteFileVersion; +use lance_file::versions; +use lance_file::versions::v1::reader::FileReader as V1FileReader; +use lance_file::versions::v1::writer::FileWriter as V1FileWriter; use lance_file::writer::FileWriterOptions; use lance_io::ReadBatchParams; use lance_io::object_store::ObjectStore; @@ -406,7 +407,7 @@ pub struct IvfShuffler { shuffle_output_root_filename: String, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, } /// Represents a range of batches in a file that should be shuffled @@ -446,11 +447,11 @@ impl IvfShuffler { unsorted_buffers: vec![], is_legacy, shuffle_output_root_filename, - format_version: LanceFileVersion::V2_0, + format_version: ConcreteFileVersion::V2_0, }) } - pub fn with_format_version(mut self, format_version: LanceFileVersion) -> Self { + pub fn with_format_version(mut self, format_version: ConcreteFileVersion) -> Self { self.format_version = format_version; self } @@ -496,7 +497,7 @@ impl IvfShuffler { info!("Writing unsorted data to disk at {}", path); info!("with schema: {:?}", schema); - let mut file_writer = PreviousFileWriter::::with_object_writer( + let mut file_writer = V1FileWriter::::with_object_writer( writer, Schema::try_from(schema.as_ref())?, &Default::default(), @@ -528,7 +529,7 @@ impl IvfShuffler { if self.is_legacy { let reader = - PreviousFileReader::try_new_self_described(&object_store, &path, None).await?; + V1FileReader::try_new_self_described(&object_store, &path, None).await?; total_batches.push(reader.num_batches()); } else { let scheduler_config = SchedulerConfig::max_bandwidth(&object_store); @@ -572,7 +573,7 @@ impl IvfShuffler { if self.is_legacy { let reader = - PreviousFileReader::try_new_self_described(&object_store, &path, None).await?; + V1FileReader::try_new_self_described(&object_store, &path, None).await?; let lance_schema = reader .schema() .project(&[PART_ID_COLUMN]) @@ -655,9 +656,8 @@ impl IvfShuffler { let mut _reader_handle = None; let mut stream = if self.is_legacy { - _reader_handle = Some( - PreviousFileReader::try_new_self_described(&object_store, &path, None).await?, - ); + _reader_handle = + Some(V1FileReader::try_new_self_described(&object_store, &path, None).await?); stream::iter(start..end) .map(|i| { @@ -806,13 +806,11 @@ impl IvfShuffler { true, )])); let lance_schema = Schema::try_from(sorted_file_schema.as_ref())?; - let mut file_writer = lance_file::writer::FileWriter::try_new( + let mut file_writer = versions::create_writer( + this.format_version, writer, lance_schema, - FileWriterOptions { - format_version: Some(this.format_version), - ..Default::default() - }, + FileWriterOptions::default(), )?; for partition_and_idx in shuffled.into_iter().enumerate() { diff --git a/rust/lance-index/src/vector/ivf/storage.rs b/rust/lance-index/src/vector/ivf/storage.rs index 5d58401bb12..0781ebc1ea5 100644 --- a/rust/lance-index/src/vector/ivf/storage.rs +++ b/rust/lance-index/src/vector/ivf/storage.rs @@ -8,8 +8,8 @@ use itertools::Itertools; use lance_arrow::FixedSizeListArrayExt; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, Result}; -use lance_file::previous::{ - reader::FileReader as PreviousFileReader, writer::FileWriter as PreviousFileWriter, +use lance_file::versions::v1::{ + reader::FileReader as V1FileReader, writer::FileWriter as V1FileWriter, }; use lance_io::{traits::WriteExt, utils::read_message}; use lance_linalg::distance::DistanceType; @@ -146,7 +146,7 @@ impl IvfModel { start..end } - pub async fn load(reader: &PreviousFileReader) -> Result { + pub async fn load(reader: &V1FileReader) -> Result { let schema = reader.schema(); let meta_str = schema .metadata @@ -167,7 +167,7 @@ impl IvfModel { } /// Write the IVF metadata to the lance file. - pub async fn write(&self, writer: &mut PreviousFileWriter) -> Result<()> { + pub async fn write(&self, writer: &mut V1FileWriter) -> Result<()> { let pb = PbIvf::try_from(self)?; let pos = writer.object_writer.write_protobuf(&pb).await?; let ivf_metadata = IvfMetadata { pb_position: pos }; @@ -286,14 +286,10 @@ mod tests { let schema = Schema::try_from(&arrow_schema).unwrap(); { - let mut writer = PreviousFileWriter::try_new( - &object_store, - &path, - schema.clone(), - &Default::default(), - ) - .await - .unwrap(); + let mut writer = + V1FileWriter::try_new(&object_store, &path, schema.clone(), &Default::default()) + .await + .unwrap(); // Write some dummy data let batch = RecordBatch::try_new( Arc::new(arrow_schema), @@ -305,7 +301,7 @@ mod tests { writer.finish().await.unwrap(); } - let reader = PreviousFileReader::try_new_self_described(&object_store, &path, None) + let reader = V1FileReader::try_new_self_described(&object_store, &path, None) .await .unwrap(); assert!(reader.schema().metadata.contains_key(IVF_METADATA_KEY)); diff --git a/rust/lance-index/src/vector/pq/storage.rs b/rust/lance-index/src/vector/pq/storage.rs index 4e98df9906d..cda5fc9c2ff 100644 --- a/rust/lance-index/src/vector/pq/storage.rs +++ b/rust/lance-index/src/vector/pq/storage.rs @@ -24,8 +24,8 @@ use bytes::{Bytes, BytesMut}; use lance_arrow::{FixedSizeListArrayExt, RecordBatchExt}; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, ROW_ID, Result}; -use lance_file::previous::{ - reader::FileReader as PreviousFileReader, writer::FileWriter as PreviousFileWriter, +use lance_file::versions::v1::{ + reader::FileReader as V1FileReader, writer::FileWriter as V1FileWriter, }; use lance_io::{object_store::ObjectStore, utils::read_message}; use lance_linalg::distance::{Cosine, DistanceType, Dot, L2}; @@ -126,7 +126,7 @@ impl QuantizerMetadata for ProductQuantizationMetadata { } } - async fn load(reader: &PreviousFileReader) -> Result { + async fn load(reader: &V1FileReader) -> Result { let metadata = reader .schema() .metadata @@ -386,7 +386,7 @@ impl ProductQuantizationStorage { path: &Path, frag_reuse_index: Option>, ) -> Result { - let reader = PreviousFileReader::try_new_self_described(object_store, path, None).await?; + let reader = V1FileReader::try_new_self_described(object_store, path, None).await?; let schema = reader.schema(); let metadata_str = schema @@ -469,7 +469,7 @@ impl ProductQuantizationStorage { /// pub async fn write_partition( &self, - writer: &mut PreviousFileWriter, + writer: &mut V1FileWriter, ) -> Result { let batch_size: usize = 10240; // TODO: make it configurable for offset in (0..self.batch.num_rows()).step_by(batch_size) { @@ -613,9 +613,9 @@ impl QuantizerStorage for ProductQuantizationStorage { /// /// Parameters /// ---------- - /// - *reader: &PreviousFileReader + /// - *reader: &V1FileReader async fn load_partition( - reader: &PreviousFileReader, + reader: &V1FileReader, range: std::ops::Range, distance_type: DistanceType, metadata: &Self::Metadata, diff --git a/rust/lance-index/src/vector/quantizer.rs b/rust/lance-index/src/vector/quantizer.rs index 6f4b191098b..f5bc7ce5c8e 100644 --- a/rust/lance-index/src/vector/quantizer.rs +++ b/rust/lance-index/src/vector/quantizer.rs @@ -15,7 +15,7 @@ use bytes::Bytes; use lance_arrow::RecordBatchExt; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, ROW_ID, Result}; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_io::traits::Reader; use lance_linalg::distance::DistanceType; use lance_table::format::SelfDescribingFileReader; @@ -223,7 +223,7 @@ pub trait QuantizerMetadata: Ok(None) } - async fn load(reader: &PreviousFileReader) -> Result; + async fn load(reader: &V1FileReader) -> Result; } #[async_trait::async_trait] @@ -277,7 +277,7 @@ pub trait QuantizerStorage: Clone + Sized + DeepSizeOf + VectorStore { } async fn load_partition( - reader: &PreviousFileReader, + reader: &V1FileReader, range: std::ops::Range, distance_type: DistanceType, metadata: &Self::Metadata, @@ -287,7 +287,7 @@ pub trait QuantizerStorage: Clone + Sized + DeepSizeOf + VectorStore { /// Loader to load partitioned [VectorStore] from disk. pub struct IvfQuantizationStorage { - reader: PreviousFileReader, + reader: V1FileReader, distance_type: DistanceType, quantizer: Quantizer, @@ -322,7 +322,7 @@ impl IvfQuantizationStorage { /// /// pub async fn open(reader: Arc) -> Result { - let reader = PreviousFileReader::try_new_self_described_from_reader(reader, None).await?; + let reader = V1FileReader::try_new_self_described_from_reader(reader, None).await?; let schema = reader.schema(); let metadata_str = schema diff --git a/rust/lance-index/src/vector/sq/storage.rs b/rust/lance-index/src/vector/sq/storage.rs index 785aadc289d..5011fbada46 100644 --- a/rust/lance-index/src/vector/sq/storage.rs +++ b/rust/lance-index/src/vector/sq/storage.rs @@ -15,7 +15,7 @@ use async_trait::async_trait; use lance_arrow::ArrowFloatType; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, ROW_ID, Result}; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_io::object_store::ObjectStore; use lance_linalg::distance::{DistanceType, dot_u8::dot_u8, l2_u8::l2_u8}; use lance_table::format::SelfDescribingFileReader; @@ -53,7 +53,7 @@ impl DeepSizeOf for ScalarQuantizationMetadata { #[async_trait] impl QuantizerMetadata for ScalarQuantizationMetadata { - async fn load(reader: &PreviousFileReader) -> Result { + async fn load(reader: &V1FileReader) -> Result { let metadata_str = reader .schema() .metadata @@ -215,7 +215,7 @@ impl ScalarQuantizationStorage { path: &Path, frag_reuse_index: Option>, ) -> Result { - let reader = PreviousFileReader::try_new_self_described(object_store, path, None).await?; + let reader = V1FileReader::try_new_self_described(object_store, path, None).await?; let schema = reader.schema(); let metadata_str = schema @@ -292,7 +292,7 @@ impl QuantizerStorage for ScalarQuantizationStorage { /// - *metric_type: metric type of the vectors /// - *metadata: scalar quantization metadata async fn load_partition( - reader: &PreviousFileReader, + reader: &V1FileReader, range: std::ops::Range, distance_type: DistanceType, metadata: &Self::Metadata, diff --git a/rust/lance-index/src/vector/utils.rs b/rust/lance-index/src/vector/utils.rs index 587e7dc3f15..dfae424f5a8 100644 --- a/rust/lance-index/src/vector/utils.rs +++ b/rust/lance-index/src/vector/utils.rs @@ -5,9 +5,8 @@ use arrow::compute::cast; use arrow_array::types::{Float16Type, Float32Type, Float64Type}; use arrow_array::{Array, ArrayRef, BooleanArray, FixedSizeListArray, cast::AsArray}; use arrow_schema::{DataType, Field}; -use lance_arrow::FixedSizeListArrayExt; +use lance_arrow::{FixedSizeListArrayExt, array_from_fixed_width_bytes}; use lance_core::{Error, Result}; -use lance_io::encodings::plain::bytes_to_array; use lance_linalg::distance::DistanceType; use prost::bytes; use std::sync::LazyLock; @@ -214,7 +213,7 @@ impl TryFrom<&pb::Tensor> for FixedSizeListArray { let num_rows = tensor.shape[0] as usize; let data = bytes::Bytes::from(tensor.data.clone()); - let flat_array = bytes_to_array( + let flat_array = array_from_fixed_width_bytes( &DataType::from(pb::tensor::DataType::try_from(tensor.data_type).unwrap()), data, dim * num_rows, diff --git a/rust/lance-index/src/vector/v3/shuffler.rs b/rust/lance-index/src/vector/v3/shuffler.rs index 4203d099d0b..85d0a7019ea 100644 --- a/rust/lance-index/src/vector/v3/shuffler.rs +++ b/rust/lance-index/src/vector/v3/shuffler.rs @@ -20,9 +20,10 @@ use lance_core::{ utils::tokio::{get_num_compute_intensive_cpus, spawn_cpu}, }; use lance_encoding::decoder::{DecoderPlugins, FilterExpression}; -use lance_encoding::version::LanceFileVersion; use lance_file::reader::{FileReader, FileReaderOptions}; -use lance_file::writer::{FileWriter, FileWriterOptions}; +use lance_file::version::ConcreteFileVersion; +use lance_file::versions; +use lance_file::writer::FileWriterOptions; use lance_io::{ ReadBatchParams, object_store::ObjectStore, @@ -71,7 +72,7 @@ pub struct IvfShuffler { object_store: Arc, output_dir: Path, num_partitions: usize, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, progress: Arc, } @@ -82,12 +83,12 @@ impl IvfShuffler { object_store: Arc::new(ObjectStore::local()), output_dir, num_partitions, - format_version: LanceFileVersion::V2_0, + format_version: ConcreteFileVersion::V2_0, progress: crate::progress::noop_progress(), } } - pub fn with_format_version(mut self, format_version: LanceFileVersion) -> Self { + pub fn with_format_version(mut self, format_version: ConcreteFileVersion) -> Self { self.format_version = format_version; self } @@ -122,13 +123,11 @@ impl Shuffler for IvfShuffler { let format_version = self.format_version; async move { let writer = object_store.create(&part_path).await?; - let file_writer = FileWriter::try_new( + let file_writer = versions::create_writer( + format_version, writer, lance_core::datatypes::Schema::try_from(&schema)?, - FileWriterOptions { - format_version: Some(format_version), - ..Default::default() - }, + FileWriterOptions::default(), )? .with_page_metadata_spill(object_store.clone(), spill_path); Result::Ok(file_writer) @@ -315,7 +314,7 @@ impl ShuffleReader for EmptyReader { pub fn create_ivf_shuffler( output_dir: Path, num_partitions: usize, - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, progress: Option>, ) -> Box { let use_legacy = std::env::var("LANCE_LEGACY_SHUFFLER") @@ -475,10 +474,10 @@ impl Shuffler for TwoFileShuffler { let data_path = self.output_dir.clone().join("shuffle_data.lance"); let spill_path = self.output_dir.clone().join("shuffle_data.spill"); let writer = self.object_store.create(&data_path).await?; - let mut file_writer = FileWriter::try_new( + let mut file_writer = versions::v2_1::create_writer( writer, lance_core::datatypes::Schema::try_from(&schema)?, - Default::default(), + FileWriterOptions::default(), )? .with_page_metadata_spill(self.object_store.clone(), spill_path); @@ -486,10 +485,10 @@ impl Shuffler for TwoFileShuffler { let offsets_path = self.output_dir.clone().join("shuffle_offsets.lance"); let spill_path = self.output_dir.clone().join("shuffle_offsets.spill"); let writer = self.object_store.create(&offsets_path).await?; - let mut offsets_writer = FileWriter::try_new( + let mut offsets_writer = versions::v2_1::create_writer( writer, lance_core::datatypes::Schema::try_from(offsets_schema.as_ref())?, - Default::default(), + FileWriterOptions::default(), )? .with_page_metadata_spill(self.object_store.clone(), spill_path); @@ -577,8 +576,8 @@ impl Shuffler for TwoFileShuffler { /// Returns `(total_rows_written, per_partition_row_counts)`. async fn flush_shuffle_batch( accumulated: Vec, - file_writer: &mut FileWriter, - offsets_writer: &mut FileWriter, + file_writer: &mut versions::v2_1::Writer, + offsets_writer: &mut versions::v2_1::Writer, offsets_schema: Arc, num_partitions: usize, global_row_count: u64, diff --git a/rust/lance-io/Cargo.toml b/rust/lance-io/Cargo.toml index 643fef8c14a..3ef704cdd81 100644 --- a/rust/lance-io/Cargo.toml +++ b/rust/lance-io/Cargo.toml @@ -20,14 +20,8 @@ lance-arrow.workspace = true lance-core.workspace = true lance-namespace.workspace = true arrow = { workspace = true, features = ["ffi"] } -arrow-arith.workspace = true arrow-array.workspace = true -arrow-buffer.workspace = true -arrow-cast.workspace = true -arrow-data.workspace = true arrow-schema.workspace = true -arrow-select.workspace = true -async-recursion.workspace = true async-trait.workspace = true aws-config = { workspace = true, optional = true } aws-credential-types = { workspace = true, optional = true } diff --git a/rust/lance-io/src/encodings.rs b/rust/lance-io/src/encodings.rs deleted file mode 100644 index de2737f2366..00000000000 --- a/rust/lance-io/src/encodings.rs +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -//! Data encodings -//! - -use arrow_array::{Array, ArrayRef, UInt32Array}; -use async_trait::async_trait; - -pub mod binary; -pub mod dictionary; -pub mod plain; - -use crate::ReadBatchParams; -use lance_core::Result; - -/// Encoder - Write an arrow array to the file. -#[async_trait] -pub trait Encoder { - /// Write an slice of Arrays, and returns the file offset of the beginning of the batch. - async fn encode(&mut self, array: &[&dyn Array]) -> Result; -} - -/// Decoder - Read Arrow Data. -#[async_trait] -pub trait Decoder: Send + AsyncIndex { - async fn decode(&self) -> Result; - - /// Take by indices. - async fn take(&self, indices: &UInt32Array) -> Result; -} - -#[async_trait] -pub trait AsyncIndex { - type Output: Send + Sync; - - async fn get(&self, index: IndexType) -> Self::Output; -} diff --git a/rust/lance-io/src/lib.rs b/rust/lance-io/src/lib.rs index 2ef686fd551..b6bdc404e39 100644 --- a/rust/lance-io/src/lib.rs +++ b/rust/lance-io/src/lib.rs @@ -11,7 +11,6 @@ use arrow_array::{PrimitiveArray, UInt32Array}; use lance_core::{Error, Result}; -pub mod encodings; pub mod ffi; pub mod local; pub mod object_reader; diff --git a/rust/lance-io/src/utils.rs b/rust/lance-io/src/utils.rs index a36aadec9d9..4b471d8b009 100644 --- a/rust/lance-io/src/utils.rs +++ b/rust/lance-io/src/utils.rs @@ -3,81 +3,17 @@ use std::{cmp::min, num::NonZero, sync::atomic::AtomicU64}; -use arrow_array::{ - ArrayRef, - types::{BinaryType, LargeBinaryType, LargeUtf8Type, Utf8Type}, -}; -use arrow_schema::DataType; use byteorder::{ByteOrder, LittleEndian}; use bytes::Bytes; -use lance_arrow::*; use lance_core::deepsize::DeepSizeOf; use prost::Message; use serde::{Deserialize, Serialize}; -use crate::{ReadBatchParams, traits::Reader}; -use crate::{ - encodings::{AsyncIndex, Decoder, binary::BinaryDecoder, plain::PlainDecoder}, - traits::ProtoStruct, -}; +use crate::traits::{ProtoStruct, Reader}; use lance_core::{Error, Result}; pub mod tracking_store; -/// Read a binary array from a [Reader]. -/// -pub async fn read_binary_array( - reader: &dyn Reader, - data_type: &DataType, - nullable: bool, - position: usize, - length: usize, - params: impl Into, -) -> Result { - use arrow_schema::DataType::*; - let decoder: Box> + Send> = match data_type { - Utf8 => Box::new(BinaryDecoder::::new( - reader, position, length, nullable, - )), - Binary => Box::new(BinaryDecoder::::new( - reader, position, length, nullable, - )), - LargeUtf8 => Box::new(BinaryDecoder::::new( - reader, position, length, nullable, - )), - LargeBinary => Box::new(BinaryDecoder::::new( - reader, position, length, nullable, - )), - _ => { - return Err(Error::invalid_input(format!( - "Unsupported binary type: {}", - data_type - ))); - } - }; - let fut = decoder.as_ref().get(params.into()); - fut.await -} - -/// Read a fixed stride array from disk. -/// -pub async fn read_fixed_stride_array( - reader: &dyn Reader, - data_type: &DataType, - position: usize, - length: usize, - params: impl Into, -) -> Result { - if !data_type.is_fixed_stride() { - return Err(Error::schema(format!( - "{data_type} is not a fixed stride type" - ))); - } - // TODO: support more than plain encoding here. - let decoder = PlainDecoder::new(reader, data_type, position, length)?; - decoder.get(params.into()).await -} - /// Read a protobuf message at file position 'pos'. /// /// We write protobuf by first writing the length of the message as a u32, diff --git a/rust/lance-table/src/format.rs b/rust/lance-table/src/format.rs index 5a5db7919d3..01c0ac33fa6 100644 --- a/rust/lance-table/src/format.rs +++ b/rust/lance-table/src/format.rs @@ -18,7 +18,7 @@ pub use index::{IndexFile, IndexMetadata, index_metadata_codec, list_index_files pub use manifest::{ BasePath, DETACHED_VERSION_MASK, DataStorageFormat, Manifest, SelfDescribingFileReader, - WriterVersion, is_detached_version, + WriterVersion, is_detached_version, populate_manifest_schema_dictionary, }; pub use transaction::Transaction; diff --git a/rust/lance-table/src/format/fragment.rs b/rust/lance-table/src/format/fragment.rs index 4929f54b7ff..b8888aa9082 100644 --- a/rust/lance-table/src/format/fragment.rs +++ b/rust/lance-table/src/format/fragment.rs @@ -7,8 +7,7 @@ use std::sync::Arc; use lance_core::Error; use lance_core::deepsize::DeepSizeOf; -use lance_file::format::{MAJOR_VERSION, MINOR_VERSION}; -use lance_file::version::LanceFileVersion; +use lance_file::version::ConcreteFileVersion; use lance_io::utils::CachedFileSize; use object_store::path::Path; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -104,15 +103,16 @@ impl<'de> Deserialize<'de> for DataFile { } impl DataFile { + /// Create a `DataFile` and encode its exact format version for manifest storage. pub fn new( path: impl Into, fields: Vec, column_indices: Vec, - file_major_version: u32, - file_minor_version: u32, + file_version: ConcreteFileVersion, file_size_bytes: Option>, base_id: Option, ) -> Self { + let (file_major_version, file_minor_version) = file_version.to_data_file_numbers(); Self { path: path.into(), fields: Arc::from(fields), @@ -124,12 +124,9 @@ impl DataFile { } } - /// Create a new `DataFile` with the expectation that fields and column_indices will be set later - pub fn new_unstarted( - path: impl Into, - file_major_version: u32, - file_minor_version: u32, - ) -> Self { + /// Create a new `DataFile` whose fields and column indices will be set later. + pub fn new_unstarted(path: impl Into, file_version: ConcreteFileVersion) -> Self { + let (file_major_version, file_minor_version) = file_version.to_data_file_numbers(); Self { path: path.into(), fields: Arc::from([]), @@ -146,15 +143,7 @@ impl DataFile { fields: Vec, base_id: Option, ) -> Self { - Self::new( - path, - fields, - vec![], - MAJOR_VERSION as u32, - MINOR_VERSION as u32, - None, - base_id, - ) + Self::new(path, fields, vec![], ConcreteFileVersion::V1, None, base_id) } pub fn new_legacy( @@ -169,8 +158,7 @@ impl DataFile { path, field_ids, vec![], - MAJOR_VERSION as u32, - MINOR_VERSION as u32, + ConcreteFileVersion::V1, file_size_bytes, base_id, ) @@ -180,12 +168,20 @@ impl DataFile { full_schema.project_by_ids(&self.fields, false) } - pub fn is_legacy_file(&self) -> bool { + fn uses_v1_data_file_encoding(&self) -> bool { self.file_major_version == 0 && self.file_minor_version < 3 } + /// Decode the exact file version stored in this `DataFile` metadata. + pub fn file_version(&self) -> Result { + ConcreteFileVersion::from_data_file_numbers( + self.file_major_version, + self.file_minor_version, + ) + } + pub fn validate(&self, base_path: &Path) -> Result<()> { - if self.is_legacy_file() { + if self.uses_v1_data_file_encoding() { if !self.fields.windows(2).all(|w| w[0] < w[1]) { return Err(Error::corrupt_file( base_path.clone().join(self.path.clone()), @@ -580,16 +576,14 @@ impl Fragment { path: impl Into, field_ids: Vec, column_indices: Vec, - version: &LanceFileVersion, + version: ConcreteFileVersion, file_size_bytes: Option>, ) -> Self { - let (major, minor) = version.to_numbers(); let data_file = DataFile::new( path, field_ids, column_indices, - major, - minor, + version, file_size_bytes, None, ); @@ -607,16 +601,14 @@ impl Fragment { path: impl Into, field_ids: Vec, column_indices: Vec, - version: &LanceFileVersion, + version: ConcreteFileVersion, file_size_bytes: Option>, ) { - let (major, minor) = version.to_numbers(); self.files.push(DataFile::new( path, field_ids, column_indices, - major, - minor, + version, file_size_bytes, None, )); @@ -628,17 +620,11 @@ impl Fragment { .push(DataFile::new_legacy(path, schema, None, None)); } - // True if this fragment is made up of legacy v1 files, false otherwise - pub fn has_legacy_files(&self) -> bool { - // If any file in a fragment is legacy then all files in the fragment must be - self.files[0].is_legacy_file() - } - // Helper method to infer the Lance version from a set of fragments // // Returns None if there are no data files // Returns an error if the data files have different versions - pub fn try_infer_version(fragments: &[Self]) -> Result> { + pub fn try_infer_version(fragments: &[Self]) -> Result> { // Otherwise we need to check the actual file versions // Determine version from first file let Some(sample_file) = fragments @@ -648,17 +634,11 @@ impl Fragment { else { return Ok(None); }; - let file_version = LanceFileVersion::try_from_major_minor( - sample_file.file_major_version, - sample_file.file_minor_version, - )?; + let file_version = sample_file.file_version()?; // Ensure all files match for frag in fragments { for file in &frag.files { - let this_file_version = LanceFileVersion::try_from_major_minor( - file.file_major_version, - file.file_minor_version, - )?; + let this_file_version = file.file_version()?; if file_version != this_file_version { return Err(Error::invalid_input(format!( "All data files must have the same version. Detected both {} and {}", @@ -760,6 +740,7 @@ mod tests { use arrow_schema::{ DataType, Field as ArrowField, Fields as ArrowFields, Schema as ArrowSchema, }; + use lance_file::format::{MAJOR_VERSION, MINOR_VERSION}; use object_store::path::Path; use roaring::RoaringBitmap; use serde_json::{Value, json}; @@ -910,6 +891,56 @@ mod tests { assert_eq!(fragment, fragment2); } + #[test] + fn infer_exact_file_version_and_reject_mixed_fragments() { + assert_eq!(Fragment::try_infer_version(&[]).unwrap(), None); + + let v2_0 = Fragment::new(0).with_file( + "v2_0.lance", + vec![0], + vec![0], + ConcreteFileVersion::V2_0, + None, + ); + assert_eq!( + ( + v2_0.files[0].file_major_version, + v2_0.files[0].file_minor_version + ), + (2, 0) + ); + let unstarted = DataFile::new_unstarted("unstarted.lance", ConcreteFileVersion::V2_0); + assert_eq!( + (unstarted.file_major_version, unstarted.file_minor_version), + (2, 0) + ); + let v2_0_second = Fragment::new(1).with_file( + "v2_0_second.lance", + vec![0], + vec![0], + ConcreteFileVersion::V2_0, + None, + ); + assert_eq!( + Fragment::try_infer_version(&[v2_0.clone(), v2_0_second]).unwrap(), + Some(ConcreteFileVersion::V2_0) + ); + + let v2_1 = Fragment::new(2).with_file( + "v2_1.lance", + vec![0], + vec![0], + ConcreteFileVersion::V2_1, + None, + ); + let error = Fragment::try_infer_version(&[v2_0, v2_1]).unwrap_err(); + assert!(matches!(error, Error::InvalidInput { .. })); + let message = error.to_string(); + assert!(message.contains("All data files must have the same version")); + assert!(message.contains("2.0")); + assert!(message.contains("2.1")); + } + #[test] fn test_to_json() { let mut fragment = Fragment::new(123); diff --git a/rust/lance-table/src/format/manifest.rs b/rust/lance-table/src/format/manifest.rs index 83c9643e600..b19dcef5ff3 100644 --- a/rust/lance-table/src/format/manifest.rs +++ b/rust/lance-table/src/format/manifest.rs @@ -4,9 +4,10 @@ use async_trait::async_trait; use chrono::prelude::*; use lance_core::deepsize::DeepSizeOf; -use lance_file::datatypes::{Fields, FieldsWithMeta, populate_schema_dictionary}; -use lance_file::previous::reader::FileReader as PreviousFileReader; -use lance_file::version::{LEGACY_FORMAT_VERSION, LanceFileVersion}; +use lance_file::datatypes::{Fields, FieldsWithMeta}; +use lance_file::version::{ConcreteFileVersion, stable_file_version}; +use lance_file::versions::v1::encoding::populate_schema_dictionaries; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_io::traits::{ProtoStruct, Reader}; use object_store::path::Path; use prost::Message; @@ -501,10 +502,6 @@ impl Manifest { pb_manifest.encode_to_vec() } - pub fn should_use_legacy_format(&self) -> bool { - self.data_storage_format.version == LEGACY_FORMAT_VERSION - } - /// Get the summary information of a manifest. /// /// This function calculates various statistics about the manifest, including: @@ -552,6 +549,27 @@ impl Manifest { } } +/// Populate dictionary values stored outside a v1 manifest. +/// +/// Current manifests carry dictionary values in the schema and require no work. +/// Keeping this decision here prevents dataset readers from reconstructing +/// manifest-format behavior from the data-file version. +pub async fn populate_manifest_schema_dictionary( + manifest: &mut Manifest, + reader: &dyn Reader, +) -> Result<()> { + match manifest.data_storage_format.version { + ConcreteFileVersion::V1 => { + populate_schema_dictionaries(&mut manifest.schema, reader).await?; + } + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => {} + } + Ok(()) +} + #[derive(Debug, Clone, PartialEq)] pub struct BasePath { pub id: u32, @@ -606,36 +624,39 @@ pub struct WriterVersion { #[derive(Debug, Clone, PartialEq, DeepSizeOf)] pub struct DataStorageFormat { pub file_format: String, - pub version: String, + pub version: ConcreteFileVersion, } const LANCE_FORMAT_NAME: &str = "lance"; impl DataStorageFormat { - pub fn new(version: LanceFileVersion) -> Self { + pub fn new(version: ConcreteFileVersion) -> Self { Self { file_format: LANCE_FORMAT_NAME.to_string(), - version: version.resolve().to_string(), + version, } } - pub fn lance_file_version(&self) -> Result { - self.version.parse::() + /// Return the exact file format version persisted by this manifest. + pub fn lance_file_format(&self) -> ConcreteFileVersion { + self.version } } impl Default for DataStorageFormat { fn default() -> Self { - Self::new(LanceFileVersion::default()) + Self::new(stable_file_version()) } } -impl From for DataStorageFormat { - fn from(pb: pb::manifest::DataStorageFormat) -> Self { - Self { +impl TryFrom for DataStorageFormat { + type Error = Error; + + fn try_from(pb: pb::manifest::DataStorageFormat) -> Result { + Ok(Self { file_format: pb.file_format, - version: pb.version, - } + version: ConcreteFileVersion::from_manifest_string(&pb.version)?, + }) } } @@ -891,13 +912,13 @@ impl TryFrom for Manifest { } else { // No fragments to inspect, best we can do is look at writer flags if has_deprecated_v2_feature_flag(p.writer_feature_flags) { - DataStorageFormat::new(LanceFileVersion::Stable) + DataStorageFormat::new(stable_file_version()) } else { - DataStorageFormat::new(LanceFileVersion::Legacy) + DataStorageFormat::new(ConcreteFileVersion::V1) } } } - Some(format) => DataStorageFormat::from(format), + Some(format) => DataStorageFormat::try_from(format)?, }; let schema = Schema::try_from(fields_with_meta)?; @@ -980,7 +1001,11 @@ impl From<&Manifest> for pb::Manifest { next_row_id: m.next_row_id, data_format: Some(pb::manifest::DataStorageFormat { file_format: m.data_storage_format.file_format.clone(), - version: m.data_storage_format.version.clone(), + version: m + .data_storage_format + .version + .to_manifest_string() + .to_string(), }), config: m.config.clone(), base_paths: m @@ -1028,7 +1053,7 @@ pub trait SelfDescribingFileReader { } #[async_trait] -impl SelfDescribingFileReader for PreviousFileReader { +impl SelfDescribingFileReader for V1FileReader { async fn try_new_self_described_from_reader( reader: Arc, cache: Option<&LanceCache>, @@ -1039,9 +1064,7 @@ impl SelfDescribingFileReader for PreviousFileReader { reader.path(), )))?; let mut manifest: Manifest = read_struct(reader.as_ref(), manifest_position).await?; - if manifest.should_use_legacy_format() { - populate_schema_dictionary(&mut manifest.schema, reader.as_ref()).await?; - } + populate_manifest_schema_dictionary(&mut manifest, reader.as_ref()).await?; let schema = manifest.schema; let max_field_id = schema.max_field_id().unwrap_or_default(); Self::try_new_from_reader( @@ -1060,6 +1083,7 @@ impl SelfDescribingFileReader for PreviousFileReader { #[cfg(test)] mod tests { + use crate::feature_flags::FLAG_USE_V2_FORMAT_DEPRECATED; use crate::format::{DataFile, DeletionFile, DeletionFileType}; use std::num::NonZero; @@ -1068,6 +1092,101 @@ mod tests { use arrow_schema::{Field as ArrowField, Schema as ArrowSchema}; use lance_core::datatypes::Field; + #[test] + fn old_empty_manifest_recovers_v1_or_current_stable() { + let old_manifest = pb::Manifest { + data_format: None, + ..Default::default() + }; + let recovered_v1 = Manifest::try_from(old_manifest.clone()).unwrap(); + assert_eq!( + recovered_v1.data_storage_format.lance_file_format(), + ConcreteFileVersion::V1 + ); + + let recovered_stable = Manifest::try_from(pb::Manifest { + writer_feature_flags: FLAG_USE_V2_FORMAT_DEPRECATED, + ..old_manifest + }) + .unwrap(); + assert_eq!( + recovered_stable.data_storage_format.lance_file_format(), + stable_file_version() + ); + } + + #[test] + fn manifest_persistence_rejects_selectors_and_public_aliases() { + for version in ["stable", "next", "legacy", "0.3"] { + let manifest = pb::Manifest { + data_format: Some(pb::manifest::DataStorageFormat { + file_format: LANCE_FORMAT_NAME.to_string(), + version: version.to_string(), + }), + ..Default::default() + }; + assert!(Manifest::try_from(manifest).is_err(), "accepted {version}"); + } + } + + #[test] + fn manifest_codec_writes_canonical_exact_string() { + let manifest = Manifest::new( + Schema::default(), + Arc::new(Vec::new()), + DataStorageFormat::new(ConcreteFileVersion::V2_0), + HashMap::new(), + ); + let encoded = pb::Manifest::from(&manifest); + assert_eq!(encoded.data_format.unwrap().version, "2.0"); + } + + #[test] + fn missing_format_infers_exact_version_and_rejects_mixed_files() { + let v2_0 = Fragment::new(0).with_file( + "v2_0.lance", + vec![0], + vec![0], + ConcreteFileVersion::V2_0, + None, + ); + let manifest = Manifest::new( + Schema::default(), + Arc::new(vec![v2_0.clone()]), + DataStorageFormat::new(ConcreteFileVersion::V1), + HashMap::new(), + ); + let mut encoded = pb::Manifest::from(&manifest); + encoded.data_format = None; + let recovered = Manifest::try_from(encoded).unwrap(); + assert_eq!( + recovered.data_storage_format.lance_file_format(), + ConcreteFileVersion::V2_0 + ); + + let v2_1 = Fragment::new(1).with_file( + "v2_1.lance", + vec![0], + vec![0], + ConcreteFileVersion::V2_1, + None, + ); + let mixed_manifest = Manifest::new( + Schema::default(), + Arc::new(vec![v2_0, v2_1]), + DataStorageFormat::new(ConcreteFileVersion::V2_0), + HashMap::new(), + ); + let mut encoded = pb::Manifest::from(&mixed_manifest); + encoded.data_format = None; + let error = Manifest::try_from(encoded).unwrap_err(); + assert!( + error + .to_string() + .contains("All data files must have the same version") + ); + } + #[test] fn test_writer_version() { let wv = WriterVersion::default(); @@ -1450,14 +1569,13 @@ mod tests { assert_eq!(real_data_summary.total_data_file_rows, 425); assert_eq!(real_data_summary.total_deletion_files, 0); - let file_version = LanceFileVersion::default(); // Step 4: write deletion files and verify summary let mut fragment_with_deletion = Fragment::new(0) .with_file( "data_with_deletion.lance", vec![0, 1], vec![0, 1], - &file_version, + stable_file_version(), NonZero::new(1000), ) .with_physical_rows(50); diff --git a/rust/lance-table/src/io/commit.rs b/rust/lance-table/src/io/commit.rs index d682bf3a4a2..3827a922566 100644 --- a/rust/lance-table/src/io/commit.rs +++ b/rust/lance-table/src/io/commit.rs @@ -2116,7 +2116,7 @@ mod tests { let mut manifest = Manifest::new( Schema::try_from(&arrow_schema).unwrap(), Arc::new(vec![]), - DataStorageFormat::new(LanceFileVersion::Stable), + DataStorageFormat::new(LanceFileVersion::Stable.resolve()), HashMap::new(), ); @@ -2175,7 +2175,7 @@ mod tests { let mut manifest = Manifest::new( Schema::try_from(&arrow_schema).unwrap(), Arc::new(vec![]), - DataStorageFormat::new(LanceFileVersion::Stable), + DataStorageFormat::new(LanceFileVersion::Stable.resolve()), HashMap::new(), ); diff --git a/rust/lance-table/src/io/manifest.rs b/rust/lance-table/src/io/manifest.rs index 6a6bfd2724c..b6df3c4a0a3 100644 --- a/rust/lance-table/src/io/manifest.rs +++ b/rust/lance-table/src/io/manifest.rs @@ -4,9 +4,11 @@ use async_trait::async_trait; use byteorder::{ByteOrder, LittleEndian}; use bytes::{Bytes, BytesMut}; -use lance_arrow::DataTypeExt; use lance_file::{ - previous::writer::ManifestProvider as PreviousManifestProvider, version::LanceFileVersion, + version::ConcreteFileVersion, + versions::v1::{ + encoding::write_schema_dictionaries, writer::ManifestProvider as V1ManifestProvider, + }, }; use object_store::ObjectStoreExt; use object_store::path::Path; @@ -17,7 +19,6 @@ use tracing::instrument; use lance_core::{Error, Result, datatypes::Schema}; use lance_io::{ - encodings::{Encoder, binary::BinaryEncoder, plain::PlainEncoder}, object_store::ObjectStore, traits::{WriteExt, Writer}, utils::read_message, @@ -188,45 +189,14 @@ pub async fn write_manifest( indices: Option>, transaction: Option, ) -> Result { - // Write dictionary values. - let max_field_id = manifest.schema.max_field_id().unwrap_or(-1); - let is_legacy_storage = manifest.should_use_legacy_format(); - for field_id in 0..max_field_id + 1 { - if let Some(field) = manifest.schema.mut_field_by_id(field_id) - && field.data_type().is_dictionary() - && is_legacy_storage - { - let dict_info = field.dictionary.as_mut().ok_or_else(|| { - Error::io(format!("Lance field {} misses dictionary info", field.name)) - })?; - - let value_arr = dict_info.values.as_ref().ok_or_else(|| { - Error::io(format!( - "Lance field {} is dictionary type, but misses the dictionary value array", - field.name - )) - })?; - - let data_type = value_arr.data_type(); - let pos = match data_type { - dt if dt.is_numeric() => { - let mut encoder = PlainEncoder::new(writer, dt); - encoder.encode(&[value_arr]).await? - } - dt if dt.is_binary_like() => { - let mut encoder = BinaryEncoder::new(writer); - encoder.encode(&[value_arr]).await? - } - _ => { - return Err(Error::schema(format!( - "Does not support {} as dictionary value type", - value_arr.data_type() - ))); - } - }; - dict_info.offset = pos; - dict_info.length = value_arr.len(); + match manifest.data_storage_format.version { + ConcreteFileVersion::V1 => { + write_schema_dictionaries(writer, &mut manifest.schema).await?; } + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => {} } do_write_manifest(writer, manifest, indices, transaction).await @@ -237,7 +207,7 @@ pub async fn write_manifest( pub struct ManifestDescribing {} #[async_trait] -impl PreviousManifestProvider for ManifestDescribing { +impl V1ManifestProvider for ManifestDescribing { async fn store_schema( object_writer: &mut dyn Writer, schema: &Schema, @@ -245,7 +215,7 @@ impl PreviousManifestProvider for ManifestDescribing { let mut manifest = Manifest::new( schema.clone(), Arc::new(vec![]), - DataStorageFormat::new(LanceFileVersion::Legacy), + DataStorageFormat::new(ConcreteFileVersion::V1), HashMap::new(), ); let pos = do_write_manifest(object_writer, &mut manifest, None, None).await?; @@ -261,8 +231,8 @@ mod test { use crate::format::SelfDescribingFileReader; use arrow_schema::{DataType, Field as ArrowField, Schema as ArrowSchema}; use lance_file::format::{MAGIC, MAJOR_VERSION, MINOR_VERSION}; - use lance_file::previous::{ - reader::FileReader as PreviousFileReader, writer::FileWriter as PreviousFileWriter, + use lance_file::versions::v1::{ + reader::FileReader as V1FileReader, writer::FileWriter as V1FileWriter, }; use rand::{Rng, distr::Alphanumeric}; use tokio::io::AsyncWriteExt; @@ -335,7 +305,7 @@ mod test { false, )])); let schema = Schema::try_from(arrow_schema.as_ref()).unwrap(); - let mut file_writer = PreviousFileWriter::::try_new( + let mut file_writer = V1FileWriter::::try_new( &store, &path, schema.clone(), @@ -355,7 +325,7 @@ mod test { file_writer.finish_with_metadata(&metadata).await.unwrap(); let reader = store.open(&path).await.unwrap(); - let reader = PreviousFileReader::try_new_self_described_from_reader(reader.into(), None) + let reader = V1FileReader::try_new_self_described_from_reader(reader.into(), None) .await .unwrap(); let schema = ArrowSchema::from(reader.schema()); diff --git a/rust/lance/benches/random_access.rs b/rust/lance/benches/random_access.rs index ef86f812ea4..6fb76f0783b 100644 --- a/rust/lance/benches/random_access.rs +++ b/rust/lance/benches/random_access.rs @@ -7,7 +7,7 @@ use arrow_array::{Float64Array, Int64Array, RecordBatch, RecordBatchIterator, St use arrow_schema::{DataType, Field, Schema as ArrowSchema}; use criterion::{Criterion, criterion_group, criterion_main}; use lance::dataset::{Dataset, ProjectionRequest, WriteParams}; -use lance_file::version::LanceFileVersion; +use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; use std::collections::HashMap; use tokio::runtime::Runtime; use uuid::Uuid; @@ -85,7 +85,12 @@ fn utf8_field_without_fsst(name: &str) -> Field { } fn utf8_field_for(version: LanceFileVersion, enable_fsst: bool, name: &str) -> Field { - if enable_fsst && version >= LanceFileVersion::V2_1 { + if enable_fsst + && matches!( + version.resolve(), + ConcreteFileVersion::V2_1 | ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 + ) + { Field::new(name, DataType::Utf8, false) } else { utf8_field_without_fsst(name) diff --git a/rust/lance/benches/s3_file_reader_diagnostics.rs b/rust/lance/benches/s3_file_reader_diagnostics.rs index 1762048d495..9f5e7c29976 100644 --- a/rust/lance/benches/s3_file_reader_diagnostics.rs +++ b/rust/lance/benches/s3_file_reader_diagnostics.rs @@ -574,7 +574,7 @@ fn projection_name(columns: &Option>) -> String { fn page_layout_kind(encoding: &PageEncoding) -> &'static str { match encoding { - PageEncoding::Legacy(_) => "legacy", + PageEncoding::Array(_) => "legacy", PageEncoding::Structural(layout) => match layout.layout.as_ref() { Some(pb21::page_layout::Layout::MiniBlockLayout(_)) => "miniblock", Some(pb21::page_layout::Layout::ConstantLayout(_)) => "constant", diff --git a/rust/lance/benches/take_blob.rs b/rust/lance/benches/take_blob.rs index 7f3483bffda..0da30d9f277 100644 --- a/rust/lance/benches/take_blob.rs +++ b/rust/lance/benches/take_blob.rs @@ -15,7 +15,7 @@ use lance::dataset::{Dataset, ProjectionRequest, ReadParams, WriteParams}; use lance_arrow::BLOB_META_KEY; use lance_encoding::decoder::DecoderConfig; use lance_file::reader::FileReaderOptions; -use lance_file::version::LanceFileVersion; +use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; #[cfg(target_os = "linux")] use lance_testing::pprof::{Output, PProfProfiler}; use tokio::runtime::Runtime; @@ -209,7 +209,10 @@ async fn write_blob_dataset( version: LanceFileVersion, cache_repetition_index: bool, ) -> Dataset { - let batches = if version >= LanceFileVersion::V2_2 { + let batches = if matches!( + version.resolve(), + ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 + ) { make_blob_v2_batches() } else { make_legacy_blob_batches() diff --git a/rust/lance/src/dataset.rs b/rust/lance/src/dataset.rs index 5f6d7b56ed5..1f35d05f6c1 100644 --- a/rust/lance/src/dataset.rs +++ b/rust/lance/src/dataset.rs @@ -5,7 +5,6 @@ //! use arrow_array::{RecordBatch, RecordBatchReader}; -use arrow_schema::DataType; use byteorder::{ByteOrder, LittleEndian}; use chrono::{Duration, prelude::*}; use futures::future::BoxFuture; @@ -27,9 +26,8 @@ use lance_core::utils::tracing::{ DATASET_DELETING_EVENT, DATASET_DROPPING_COLUMN_EVENT, TRACE_DATASET_EVENTS, }; use lance_datafusion::projection::ProjectionPlan; -use lance_file::datatypes::populate_schema_dictionary; use lance_file::reader::{FileReader, FileReaderOptions}; -use lance_file::version::LanceFileVersion; +use lance_file::versions as file_versions; use lance_index::{IndexType, progress::IndexBuildProgress}; use lance_io::object_store::{ ChainedWrappingObjectStore, LanceNamespaceStorageOptionsProvider, ObjectStore, @@ -44,7 +42,7 @@ use lance_io::utils::{ use lance_namespace::LanceNamespace; use lance_table::format::{ DataFile, DataStorageFormat, DeletionFile, Fragment, IndexMetadata, MAGIC, Manifest, RowIdMeta, - pb, + pb, populate_manifest_schema_dictionary, }; use lance_table::io::commit::{ CommitConfig, CommitError, CommitHandler, CommitLock, ManifestLocation, ManifestNamingScheme, @@ -94,6 +92,7 @@ pub mod transaction; pub mod udtf; pub mod updater; mod utils; +pub(crate) mod versions; pub mod write; pub(crate) use take::row_offsets_to_row_addresses; @@ -549,7 +548,7 @@ impl Dataset { ) .with_object_store(Arc::new(self.object_store.as_ref().clone())) .with_commit_handler(self.commit_handler.clone()) - .with_storage_format(self.manifest.data_storage_format.lance_file_version()?); + .with_exact_storage_format(self.manifest.data_storage_format.lance_file_format()); let dataset = builder.execute(transaction).await?; // Create BranchContents after shallow_clone @@ -773,9 +772,7 @@ impl Dataset { .await; } - if manifest.should_use_legacy_format() { - populate_schema_dictionary(&mut manifest.schema, object_reader.as_ref()).await?; - } + populate_manifest_schema_dictionary(&mut manifest, object_reader.as_ref()).await?; Ok(manifest) } @@ -1151,15 +1148,6 @@ impl Dataset { delta::DatasetDeltaBuilder::new(self.clone()) } - // TODO: Cache this - pub(crate) fn is_legacy_storage(&self) -> bool { - self.manifest - .data_storage_format - .lance_file_version() - .unwrap() - == LanceFileVersion::Legacy - } - pub async fn latest_manifest(&self) -> Result<(Arc, ManifestLocation)> { let location = self .commit_handler @@ -1180,7 +1168,7 @@ impl Dataset { return Ok((self.manifest.clone(), self.manifest_location.clone())); } let mut manifest = read_manifest(&self.object_store, &location.path, location.size).await?; - if manifest.schema.has_dictionary_types() && manifest.should_use_legacy_format() { + if manifest.schema.has_dictionary_types() { let reader = if let Some(size) = location.size { self.object_store .open_with_size(&location.path, size as usize) @@ -1188,7 +1176,7 @@ impl Dataset { } else { self.object_store.open(&location.path).await? }; - populate_schema_dictionary(&mut manifest.schema, reader.as_ref()).await?; + populate_manifest_schema_dictionary(&mut manifest, reader.as_ref()).await?; } let manifest_arc = Arc::new(manifest); self.metadata_cache @@ -2230,12 +2218,7 @@ impl Dataset { .await?; let file_metadata = FileReader::read_all_metadata(&file).await?; - let file_version = LanceFileVersion::try_from_major_minor( - file_metadata.major_version as u32, - file_metadata.minor_version as u32, - )?; - - let is_structural = file_version >= LanceFileVersion::V2_1; + let lance_file_format = file_metadata.version; let physical_columns = file_metadata.column_metadatas.len(); let has_footer_orphans = file_metadata.file_schema.fields.len() > physical_columns; let dataset_schema = self.schema(); @@ -2243,29 +2226,6 @@ impl Dataset { let mut column_names = Vec::new(); let mut consumed_top_level_fields = 0usize; - fn physical_column_count( - field: &lance_core::datatypes::Field, - is_structural: bool, - ) -> usize { - if !is_structural { - return 1 + field - .children - .iter() - .map(|child| physical_column_count(child, is_structural)) - .sum::(); - } - - if field.children.is_empty() || field.is_blob() || field.is_packed_struct() { - 1 - } else { - field - .children - .iter() - .map(|child| physical_column_count(child, is_structural)) - .sum() - } - } - fn field_contains_blob(field: &lance_core::datatypes::Field) -> bool { field.is_blob() || field.children.iter().any(field_contains_blob) } @@ -2302,38 +2262,6 @@ impl Dataset { } } - fn collect_columns( - field: &lance_core::datatypes::Field, - is_structural: bool, - fields: &mut Vec, - column_indices: &mut Vec, - curr_column_idx: &mut i32, - ) { - let contributes = !is_structural - || field.children.is_empty() - || field.is_blob() - || field.is_packed_struct(); - let recurse = !is_structural || (!field.is_blob() && !field.is_packed_struct()); - - if contributes { - fields.push(field.id); - column_indices.push(*curr_column_idx); - *curr_column_idx += 1; - } - - if recurse { - for child in &field.children { - collect_columns( - child, - is_structural, - fields, - column_indices, - curr_column_idx, - ); - } - } - } - fn validate_file_field_matches_dataset( dataset_field: &lance_core::datatypes::Field, file_field: &lance_core::datatypes::Field, @@ -2387,7 +2315,7 @@ impl Dataset { }; validate_file_field_matches_dataset(dataset_field, field, &field.name)?; - represented_columns += physical_column_count(field, is_structural); + represented_columns += file_versions::physical_column_count(lance_file_format, field); column_names.push(field.name.as_str()); consumed_top_level_fields = idx + 1; idx += 1; @@ -2420,23 +2348,17 @@ impl Dataset { let projected_ds_schema = self.schema().project(&column_names)?; - let mut fields = Vec::new(); - let mut column_indices = Vec::new(); - let mut curr_column_idx: i32 = 0; - for field in &projected_ds_schema.fields { - collect_columns( - field, - is_structural, - &mut fields, - &mut column_indices, - &mut curr_column_idx, - ); - } + let (fields, column_indices) = + file_versions::data_file_columns(lance_file_format, &projected_ds_schema); + let represented_dataset_columns = column_indices + .iter() + .filter(|column_index| **column_index >= 0) + .count(); - if curr_column_idx as usize != physical_columns { + if represented_dataset_columns != physical_columns { return Err(Error::invalid_input(format!( "Schema mismatch: dataset projection maps to {} physical columns but file has {} columns", - curr_column_idx, physical_columns + represented_dataset_columns, physical_columns ))); } @@ -2451,8 +2373,7 @@ impl Dataset { path, fields, column_indices, - file_metadata.major_version as u32, - file_metadata.minor_version as u32, + lance_file_format, file_size_nz, base_id, )) @@ -3162,7 +3083,7 @@ impl Dataset { ) .with_object_store(Arc::new(self.object_store.as_ref().clone())) .with_commit_handler(self.commit_handler.clone()) - .with_storage_format(self.manifest.data_storage_format.lance_file_version()?); + .with_exact_storage_format(self.manifest.data_storage_format.lance_file_format()); builder.execute(transaction).await } @@ -3281,7 +3202,7 @@ impl Dataset { .with_object_store(target_store.clone()) .with_source_store(src_ds.object_store.clone()) .with_commit_handler(self.commit_handler.clone()) - .with_storage_format(self.manifest.data_storage_format.lance_file_version()?); + .with_exact_storage_format(self.manifest.data_storage_format.lance_file_format()); let new_ds = builder.execute(txn).await?; Ok(new_ds) } @@ -3394,29 +3315,6 @@ impl Dataset { pub fn sql(&self, sql: &str) -> SqlQueryBuilder { SqlQueryBuilder::new(self.clone(), sql) } - - /// Returns true if Lance supports writing this datatype with nulls. - pub(crate) fn lance_supports_nulls(&self, datatype: &DataType) -> bool { - match self - .manifest() - .data_storage_format - .lance_file_version() - .unwrap_or(LanceFileVersion::Legacy) - .resolve() - { - LanceFileVersion::Legacy => matches!( - datatype, - DataType::Utf8 - | DataType::LargeUtf8 - | DataType::Binary - | DataType::List(_) - | DataType::FixedSizeBinary(_) - | DataType::FixedSizeList(_, _) - ), - LanceFileVersion::V2_0 => !matches!(datatype, DataType::Struct(..)), - _ => true, - } - } } pub(crate) struct NewTransactionResult<'a> { diff --git a/rust/lance/src/dataset/blob.rs b/rust/lance/src/dataset/blob.rs index 747ca7ba2ab..fb6de1a5cbc 100644 --- a/rust/lance/src/dataset/blob.rs +++ b/rust/lance/src/dataset/blob.rs @@ -3646,10 +3646,7 @@ mod tests { utils::tempfile::{TempDir, TempStrDir}, }; use lance_datagen::{BatchCount, RowCount, array}; - use lance_file::{ - version::LanceFileVersion, - writer::{FileWriter, FileWriterOptions}, - }; + use lance_file::{version::LanceFileVersion, writer::FileWriterOptions}; use uuid::Uuid; use super::{ @@ -4864,13 +4861,10 @@ mod tests { .unwrap(); let object_writer = dataset.object_store.create(&data_file_path).await.unwrap(); - let mut file_writer = FileWriter::try_new( + let mut file_writer = lance_file::versions::v2_2::create_writer( object_writer, crate::datatypes::Schema::try_from(append_schema.as_ref()).unwrap(), - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_2), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); file_writer.write_batch(&replacement_batch).await.unwrap(); @@ -5050,13 +5044,10 @@ mod tests { RecordBatch::try_new(replacement_schema.clone(), vec![info_array]).unwrap(); let object_writer = dataset.object_store.create(&data_file_path).await.unwrap(); - let mut file_writer = FileWriter::try_new( + let mut file_writer = lance_file::versions::v2_2::create_writer( object_writer, crate::datatypes::Schema::try_from(replacement_schema.as_ref()).unwrap(), - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_2), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); file_writer.write_batch(&replacement_batch).await.unwrap(); @@ -5157,13 +5148,10 @@ mod tests { RecordBatch::try_new(replacement_schema.clone(), vec![info_array]).unwrap(); let object_writer = dataset.object_store.create(&data_file_path).await.unwrap(); - let mut file_writer = FileWriter::try_new( + let mut file_writer = lance_file::versions::v2_2::create_writer( object_writer, crate::datatypes::Schema::try_from(replacement_schema.as_ref()).unwrap(), - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_2), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); file_writer.write_batch(&replacement_batch).await.unwrap(); diff --git a/rust/lance/src/dataset/builder.rs b/rust/lance/src/dataset/builder.rs index aecbf92f0d8..119c0289016 100644 --- a/rust/lance/src/dataset/builder.rs +++ b/rust/lance/src/dataset/builder.rs @@ -11,7 +11,6 @@ use crate::io::commit::namespace_manifest::LanceNamespaceExternalManifestStore; use crate::{Dataset, Error, Result, session::Session}; use futures::FutureExt; use lance_core::utils::tracing::{DATASET_LOADING_EVENT, TRACE_DATASET_EVENTS}; -use lance_file::datatypes::populate_schema_dictionary; use lance_file::reader::FileReaderOptions; use lance_io::object_store::{ DEFAULT_CLOUD_IO_PARALLELISM, LanceNamespaceStorageOptionsProvider, ObjectStore, @@ -20,7 +19,7 @@ use lance_io::object_store::{ use lance_namespace::LanceNamespace; use lance_namespace::models::DescribeTableRequest; use lance_table::{ - format::Manifest, + format::{Manifest, populate_manifest_schema_dictionary}, io::commit::external_manifest::ExternalManifestCommitHandler, io::commit::{CommitHandler, commit_handler_from_url}, }; @@ -838,9 +837,9 @@ impl DatasetBuilder { let location = commit_handler .resolve_version_location(&base_path, manifest.version, &object_store.inner) .await?; - if manifest.schema.has_dictionary_types() && manifest.should_use_legacy_format() { + if manifest.schema.has_dictionary_types() { let reader = object_store.open(&location.path).await?; - populate_schema_dictionary(&mut manifest.schema, reader.as_ref()).await?; + populate_manifest_schema_dictionary(&mut manifest, reader.as_ref()).await?; } (Arc::new(manifest), location) } else { diff --git a/rust/lance/src/dataset/fragment.rs b/rust/lance/src/dataset/fragment.rs index 74073c46d2b..69b4cf941e9 100644 --- a/rust/lance/src/dataset/fragment.rs +++ b/rust/lance/src/dataset/fragment.rs @@ -35,14 +35,11 @@ use lance_core::{ }; use lance_datafusion::utils::StreamingWriteSource; use lance_encoding::decoder::DecoderPlugins; -use lance_file::previous::reader::{ - FileReader as PreviousFileReader, read_batch as previous_read_batch, -}; use lance_file::reader::{ - CachedFileMetadata, FileMetadataIndex, FileReaderOptions, ProjectedFileReader, ReaderProjection, + CachedFileMetadata, FileMetadataIndex, FileReaderOptions, ProjectedFileReader, }; -use lance_file::version::LanceFileVersion; -use lance_file::{LanceEncodingsIo, determine_file_version}; +use lance_file::versions::v1::reader::{FileReader as V1FileReader, read_batch as v1_read_batch}; +use lance_file::{LanceEncodingsIo, determine_file_version, versions as file_versions}; use lance_io::ReadBatchParams; use lance_io::scheduler::{FileScheduler, ScanScheduler, SchedulerConfig}; use lance_io::utils::CachedFileSize; @@ -141,30 +138,13 @@ pub trait GenericFileReader: std::fmt::Debug + Send + Sync { /// Get storage statistics for this file (ignored by v1 reader) fn storage_stats(&self) -> Result>; - // Helper functions to fallback to the legacy implementation while we - // slowly migrate functionality over to the generic reader - // Clone the reader, this is needed because Box doesn't // implement Clone fn clone_box(&self) -> Box; - // Return true if the reader is a v1 reader - fn is_legacy(&self) -> bool; - // Return a reference to the legacy reader, panics if called on a v2 - // file. - fn as_legacy(&self) -> &PreviousFileReader { - self.as_legacy_opt() - .expect("legacy function called on v2 file") - } - // Return a reference to the legacy reader if this is a v1 reader and - // return None otherwise - fn as_legacy_opt(&self) -> Option<&PreviousFileReader>; - // Return a mutable reference to the legacy reader if this is a v1 reader - // and return None otherwise - fn as_legacy_opt_mut(&mut self) -> Option<&mut PreviousFileReader>; } fn ranges_to_tasks( - reader: &PreviousFileReader, + reader: &V1FileReader, ranges: Vec<(i32, Range)>, projection: Arc, ) -> ReadBatchTaskStream { @@ -175,7 +155,7 @@ fn ranges_to_tasks( let reader = reader.clone(); let projection = projection.clone(); let task = tokio::task::spawn(async move { - previous_read_batch( + v1_read_batch( &reader, &ReadBatchParams::Range(range.clone()), &projection, @@ -195,12 +175,12 @@ fn ranges_to_tasks( #[derive(Clone, Debug)] struct V1Reader { - reader: PreviousFileReader, + reader: V1FileReader, projection: Arc, } impl V1Reader { - fn new(reader: PreviousFileReader, projection: Arc) -> Self { + fn new(reader: V1FileReader, projection: Arc) -> Self { Self { reader, projection } } } @@ -313,18 +293,6 @@ impl GenericFileReader for V1Reader { fn clone_box(&self) -> Box { Box::new(self.clone()) } - - fn is_legacy(&self) -> bool { - true - } - - fn as_legacy_opt(&self) -> Option<&PreviousFileReader> { - Some(&self.reader) - } - - fn as_legacy_opt_mut(&mut self) -> Option<&mut PreviousFileReader> { - Some(&mut self.reader) - } } mod v2_adapter { @@ -368,7 +336,7 @@ mod v2_adapter { projection: Arc, ) -> BoxFuture<'_, Result> { async move { - let projection = ReaderProjection::from_field_ids( + let projection = file_versions::reader_projection_from_field_ids( self.reader.version(), projection.as_ref(), self.field_id_to_column_idx.as_ref(), @@ -398,7 +366,7 @@ mod v2_adapter { projection: Arc, ) -> BoxFuture<'_, Result> { async move { - let projection = ReaderProjection::from_field_ids( + let projection = file_versions::reader_projection_from_field_ids( self.reader.version(), projection.as_ref(), self.field_id_to_column_idx.as_ref(), @@ -427,7 +395,7 @@ mod v2_adapter { projection: Arc, ) -> BoxFuture<'_, Result> { async move { - let projection = ReaderProjection::from_field_ids( + let projection = file_versions::reader_projection_from_field_ids( self.reader.version(), projection.as_ref(), self.field_id_to_column_idx.as_ref(), @@ -459,7 +427,7 @@ mod v2_adapter { ) -> BoxFuture<'_, Result> { let indices = UInt32Array::from(indices.to_vec()); async move { - let projection = ReaderProjection::from_field_ids( + let projection = file_versions::reader_projection_from_field_ids( self.reader.version(), projection.as_ref(), self.field_id_to_column_idx.as_ref(), @@ -528,18 +496,6 @@ mod v2_adapter { fn clone_box(&self) -> Box { Box::new(self.clone()) } - - fn is_legacy(&self) -> bool { - false - } - - fn as_legacy_opt(&self) -> Option<&PreviousFileReader> { - None - } - - fn as_legacy_opt_mut(&mut self) -> Option<&mut PreviousFileReader> { - None - } } } @@ -638,18 +594,6 @@ impl GenericFileReader for NullReader { fn clone_box(&self) -> Box { Box::new(self.clone()) } - - fn is_legacy(&self) -> bool { - false - } - - fn as_legacy_opt(&self) -> Option<&PreviousFileReader> { - None - } - - fn as_legacy_opt_mut(&mut self) -> Option<&mut PreviousFileReader> { - None - } } #[derive(Debug, Default, Clone)] @@ -724,7 +668,7 @@ impl FragReadConfig { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum MetadataMode { +pub(crate) enum MetadataMode { LazyAllowed, Full, } @@ -781,68 +725,78 @@ impl FileFragment { let file_version = determine_file_version(dataset.object_store.as_ref(), &filepath, None).await?; - if file_version != dataset.manifest.data_storage_format.lance_file_version()? { - return Err(Error::invalid_input(format!( - "File version mismatch. Dataset version: {:?} Fragment version: {:?}", - dataset.manifest.data_storage_format.lance_file_version()?, - file_version - ))); - } + super::versions::create_fragment_from_file( + file_version, + dataset.manifest.data_storage_format.lance_file_format(), + filename, + dataset, + fragment_id, + physical_rows, + ) + .await + } - if file_version == LanceFileVersion::Legacy { - let fragment = Fragment::with_file_legacy( - fragment_id as u64, - filename, - dataset.schema(), - physical_rows, - ); - Ok(fragment) - } else { - // Load the file metadata, confirm the schema is compatible, and - // determine the column offsets - let mut frag = Fragment::new(fragment_id as u64); - let scheduler = ScanScheduler::new( - dataset.object_store.clone(), - SchedulerConfig::max_bandwidth(&dataset.object_store), - ); - let file_scheduler = scheduler - .open_file(&filepath, &CachedFileSize::unknown()) - .await?; - let reader = lance_file::reader::FileReader::try_open( - file_scheduler, - None, - Arc::::default(), - &dataset.metadata_cache.file_metadata_cache(&filepath), - dataset.file_reader_options.clone().unwrap_or_default(), - ) - .await?; - // If the schemas are not compatible we can't calculate field id offsets - reader - .schema() - .check_compatible(dataset.schema(), &SchemaCompareOptions::default())?; - let projection = lance_file::reader::ReaderProjection::from_whole_schema( - dataset.schema(), - reader.metadata().version(), - ); - let physical_rows = reader.metadata().num_rows as usize; - frag.physical_rows = Some(physical_rows); - frag.id = fragment_id as u64; + pub(crate) async fn create_from_v1_file( + filename: &str, + dataset: &Dataset, + fragment_id: usize, + physical_rows: Option, + ) -> Result { + Ok(Fragment::with_file_legacy( + fragment_id as u64, + filename, + dataset.schema(), + physical_rows, + )) + } - let column_indices = projection - .column_indices - .into_iter() - .map(|c| c as i32) - .collect(); + pub(crate) async fn create_from_current_file( + filename: &str, + dataset: &Dataset, + fragment_id: usize, + ) -> Result { + let filepath = dataset.data_dir().join(filename); + // Load the file metadata, confirm the schema is compatible, and + // determine the column offsets + let mut frag = Fragment::new(fragment_id as u64); + let scheduler = ScanScheduler::new( + dataset.object_store.clone(), + SchedulerConfig::max_bandwidth(&dataset.object_store), + ); + let file_scheduler = scheduler + .open_file(&filepath, &CachedFileSize::unknown()) + .await?; + let reader = lance_file::reader::FileReader::try_open( + file_scheduler, + None, + Arc::::default(), + &dataset.metadata_cache.file_metadata_cache(&filepath), + dataset.file_reader_options.clone().unwrap_or_default(), + ) + .await?; + // If the schemas are not compatible we can't calculate field id offsets + reader + .schema() + .check_compatible(dataset.schema(), &SchemaCompareOptions::default())?; + let projection = file_versions::reader_projection_from_whole_schema( + dataset.schema(), + reader.metadata().version(), + ); + frag.physical_rows = Some(reader.metadata().num_rows as usize); - frag.add_file( - filename, - dataset.schema().field_ids(), - column_indices, - &file_version, - None, - ); - Ok(frag) - } + let column_indices = projection + .column_indices + .into_iter() + .map(|column| column as i32) + .collect(); + frag.add_file( + filename, + dataset.schema().field_ids(), + column_indices, + reader.metadata().version(), + None, + ); + Ok(frag) } /// Returns storage stats as `(field_id, bytes_on_disk)` pairs for this fragment. @@ -981,25 +935,43 @@ impl FileFragment { Ok(reader) } - fn get_field_id_offset(data_file: &DataFile) -> u32 { - data_file.fields.first().copied().unwrap_or(0) as u32 + pub(crate) async fn open_v1_fragment_reader( + &self, + projection: &Schema, + read_config: &FragReadConfig, + ) -> Result { + let open_readers = async { + let mut readers = Vec::new(); + for data_file in &self.metadata.files { + if let Some(reader) = self.open_v1_reader(data_file, Some(projection)).await? { + readers.push(reader); + } + } + Result::Ok(readers) + }; + let deletion_vec_load = self.get_deletion_vector(); + let row_id_load = if self.dataset.manifest.uses_stable_row_ids() { + futures::future::Either::Left( + load_row_id_sequence(&self.dataset, &self.metadata).map_ok(Some), + ) + } else { + futures::future::Either::Right(futures::future::ready(Ok(None))) + }; + let (readers, deletion_vec, row_id_sequence) = + join!(open_readers, deletion_vec_load, row_id_load); + let mut reader = + V1FragmentReader::try_new(readers?, deletion_vec?, row_id_sequence?, self.id())?; + if read_config.with_row_id { + reader.with_row_id(); + } + if read_config.with_row_address { + reader.with_row_address(); + } + Ok(reader) } - fn should_try_indexed_metadata( - data_file: &DataFile, - projection: &ReaderProjection, - file_version: LanceFileVersion, - ) -> bool { - if !ProjectedFileReader::supports_projection(projection, file_version) { - return false; - } - let total_columns = data_file - .column_indices - .iter() - .filter(|column_index| **column_index >= 0) - .count(); - let selected_columns = projection.column_indices.len(); - selected_columns.saturating_mul(4) < total_columns + fn get_field_id_offset(data_file: &DataFile) -> u32 { + data_file.fields.first().copied().unwrap_or(0) as u32 } pub(super) async fn open_reader( @@ -1035,140 +1007,173 @@ impl FileFragment { metadata_mode: MetadataMode, ) -> BoxFuture<'a, Result>>> { async move { - let full_schema = self.dataset.schema(); - // The data file may contain fields that are not part of the dataset any longer, remove those - let data_file_schema = Arc::new(data_file.schema(full_schema)); - let projection = projection.unwrap_or(full_schema); - // Also remove any fields that are not part of the user's provided projection - let schema_per_file = - Arc::new(projection.intersection_ignore_types(data_file_schema.as_ref())?); - - if data_file.is_legacy_file() { - let max_field_id = data_file.fields.iter().max().unwrap(); - if !schema_per_file.fields.is_empty() { - let path = self - .dataset - .data_file_dir(data_file)? - .join(data_file.path.as_str()); - let object_store = self.dataset.object_store_for_data_file(data_file).await?; - let field_id_offset = Self::get_field_id_offset(data_file); - let reader = PreviousFileReader::try_new_with_fragment_id( - &object_store, - &path, - self.schema().clone(), - self.id() as u32, - field_id_offset as i32, - *max_field_id, - Some(&self.dataset.metadata_cache.file_metadata_cache(&path)), - ) + super::versions::open_file_reader( + data_file.file_version()?, + self, + data_file, + projection, + read_config, + metadata_mode, + ) + .await + } + .boxed() + } + + pub(crate) async fn open_v1_file_reader( + &self, + data_file: &DataFile, + projection: Option<&Schema>, + ) -> Result>> { + Ok(self + .open_v1_reader(data_file, projection) + .await? + .map(|reader| Box::new(reader) as Box)) + } + + async fn open_v1_reader( + &self, + data_file: &DataFile, + projection: Option<&Schema>, + ) -> Result> { + let full_schema = self.dataset.schema(); + let data_file_schema = Arc::new(data_file.schema(full_schema)); + let projection = projection.unwrap_or(full_schema); + let schema_per_file = + Arc::new(projection.intersection_ignore_types(data_file_schema.as_ref())?); + if schema_per_file.fields.is_empty() { + return Ok(None); + } + + let max_field_id = data_file.fields.iter().max().ok_or_else(|| { + Error::invalid_input(format!( + "Legacy data file {} does not contain any fields", + data_file.path + )) + })?; + let path = self + .dataset + .data_file_dir(data_file)? + .join(data_file.path.as_str()); + let object_store = self.dataset.object_store_for_data_file(data_file).await?; + let field_id_offset = Self::get_field_id_offset(data_file); + let reader = V1FileReader::try_new_with_fragment_id( + &object_store, + &path, + self.schema().clone(), + self.id() as u32, + field_id_offset as i32, + *max_field_id, + Some(&self.dataset.metadata_cache.file_metadata_cache(&path)), + ) + .await?; + let initialized_schema = reader.schema().project_by_schema( + schema_per_file.as_ref(), + OnMissing::Error, + OnTypeMismatch::Error, + )?; + Ok(Some(V1Reader::new(reader, Arc::new(initialized_schema)))) + } + + pub(crate) async fn open_current_file_reader( + &self, + data_file: &DataFile, + projection: Option<&Schema>, + read_config: &FragReadConfig, + metadata_mode: MetadataMode, + ) -> Result>> { + let full_schema = self.dataset.schema(); + let data_file_schema = Arc::new(data_file.schema(full_schema)); + let projection = projection.unwrap_or(full_schema); + let schema_per_file = + Arc::new(projection.intersection_ignore_types(data_file_schema.as_ref())?); + if schema_per_file.fields.is_empty() { + return Ok(None); + } + + let path = self + .dataset + .data_file_dir(data_file)? + .join(data_file.path.as_str()); + let (store_scheduler, reader_priority) = if let Some(base_id) = data_file.base_id { + // TODO: make object stores for non-default bases reuse the same scan scheduler + // currently we always create a new one + let object_store = self.dataset.object_store(Some(base_id)).await?; + let config = SchedulerConfig::max_bandwidth(&object_store); + ( + ScanScheduler::new(object_store, config), + read_config.reader_priority.unwrap_or(0), + ) + } else if let Some(scan_scheduler) = read_config.scan_scheduler.as_ref() { + ( + scan_scheduler.clone(), + read_config.reader_priority.unwrap_or(0), + ) + } else { + ( + ScanScheduler::new( + self.dataset.object_store.clone(), + SchedulerConfig::max_bandwidth(&self.dataset.object_store), + ), + 0, + ) + }; + let file_scheduler = store_scheduler + .open_file_with_priority(&path, reader_priority as u64, &data_file.file_size_bytes) + .await?; + let path = file_scheduler.reader().path().clone(); + let metadata_cache = self.dataset.metadata_cache.file_metadata_cache(&path); + let field_id_to_column_idx = Arc::new(BTreeMap::from_iter( + data_file + .fields + .iter() + .copied() + .zip(data_file.column_indices.iter().copied()) + .filter_map(|(field_id, column_index)| { + (column_index >= 0).then_some((field_id as u32, column_index as u32)) + }), + )); + let file_version = data_file.file_version()?; + let reader_projection = file_versions::reader_projection_from_field_ids( + file_version, + schema_per_file.as_ref(), + field_id_to_column_idx.as_ref(), + )?; + let file_reader_options = read_config + .file_reader_options + .clone() + .or_else(|| self.dataset.file_reader_options.clone()) + .unwrap_or_default(); + let prefer_indexed = metadata_mode == MetadataMode::LazyAllowed + && reader_projection.column_indices.len().saturating_mul(4) + < data_file + .column_indices + .iter() + .filter(|column_index| **column_index >= 0) + .count(); + let known_schema = self + .metadata + .physical_rows + .map(|num_rows| (data_file_schema.clone(), num_rows as u64)); + + let encodings_io = Arc::new( + LanceEncodingsIo::new(file_scheduler.clone()) + .with_read_chunk_size(file_reader_options.read_chunk_size), + ); + let reader = file_versions::open_projected_reader( + file_version, + &reader_projection, + prefer_indexed, + || async { + let metadata_index = self + .get_file_metadata_index(&file_scheduler, known_schema.clone()) .await?; - let initialized_schema = reader.schema().project_by_schema( - schema_per_file.as_ref(), - OnMissing::Error, - OnTypeMismatch::Error, - )?; - let reader = V1Reader::new(reader, Arc::new(initialized_schema)); - let reader: Box = Box::new(reader); - Ok(Some(reader)) - } else { - Ok(None) + if (reader_projection.column_indices.len() as u32).saturating_mul(4) + >= metadata_index.num_columns() + { + return Ok(None); } - } else if schema_per_file.fields.is_empty() { - Ok(None) - } else { - let path = self - .dataset - .data_file_dir(data_file)? - .join(data_file.path.as_str()); - let (store_scheduler, reader_priority) = if let Some(base_id) = data_file.base_id { - // TODO: make object stores for non-default bases reuse the same scan scheduler - // currently we always create a new one - let object_store = self.dataset.object_store(Some(base_id)).await?; - let config = SchedulerConfig::max_bandwidth(&object_store); - ( - ScanScheduler::new(object_store, config), - read_config.reader_priority.unwrap_or(0), - ) - } else if let Some(scan_scheduler) = read_config.scan_scheduler.as_ref() { - ( - scan_scheduler.clone(), - read_config.reader_priority.unwrap_or(0), - ) - } else { - ( - ScanScheduler::new( - self.dataset.object_store.clone(), - SchedulerConfig::max_bandwidth(&self.dataset.object_store), - ), - 0, - ) - }; - let file_scheduler = store_scheduler - .open_file_with_priority( - &path, - reader_priority as u64, - &data_file.file_size_bytes, - ) - .await?; - let path = file_scheduler.reader().path().clone(); - let metadata_cache = self.dataset.metadata_cache.file_metadata_cache(&path); - let field_id_to_column_idx = Arc::new(BTreeMap::from_iter( - data_file - .fields - .iter() - .copied() - .zip(data_file.column_indices.iter().copied()) - .filter_map(|(field_id, column_index)| { - if column_index < 0 { - None - } else { - Some((field_id as u32, column_index as u32)) - } - }), - )); - let file_version = LanceFileVersion::try_from_major_minor( - data_file.file_major_version, - data_file.file_minor_version, - )?; - let reader_projection = ReaderProjection::from_field_ids( - file_version, - schema_per_file.as_ref(), - field_id_to_column_idx.as_ref(), - )?; - let file_reader_options = read_config - .file_reader_options - .clone() - .or_else(|| self.dataset.file_reader_options.clone()) - .unwrap_or_default(); - let metadata_index = if metadata_mode == MetadataMode::LazyAllowed - && Self::should_try_indexed_metadata( - data_file, - &reader_projection, - file_version, - ) { - let known_schema = self - .metadata - .physical_rows - .map(|num_rows| (data_file_schema.clone(), num_rows as u64)); - let metadata_index = self - .get_file_metadata_index(&file_scheduler, known_schema) - .await?; - if (reader_projection.column_indices.len() as u32).saturating_mul(4) - < metadata_index.num_columns() - { - Some(metadata_index) - } else { - None - } - } else { - None - }; - - let encodings_io = Arc::new( - LanceEncodingsIo::new(file_scheduler.clone()) - .with_read_chunk_size(file_reader_options.read_chunk_size), - ); - let reader = if let Some(metadata_index) = metadata_index { + Ok(Some( ProjectedFileReader::try_open_with_metadata_index( encodings_io.clone(), path.clone(), @@ -1178,32 +1183,31 @@ impl FileFragment { &metadata_cache, file_reader_options.clone(), ) - .await? - } else { - let file_metadata = self.get_file_metadata(&file_scheduler).await?; - ProjectedFileReader::try_open_with_file_metadata( - encodings_io, - path.clone(), - None, - Arc::::default(), - file_metadata, - &metadata_cache, - file_reader_options, - ) - .await? - }; - let reader = v2_adapter::Reader::new( - Arc::new(reader), - schema_per_file, - field_id_to_column_idx, - reader_priority, - file_scheduler, - ); - let reader: Box = Box::new(reader); - Ok(Some(reader)) - } - } - .boxed() + .await?, + )) + }, + || async { + let file_metadata = self.get_file_metadata(&file_scheduler).await?; + ProjectedFileReader::try_open_with_file_metadata( + encodings_io.clone(), + path.clone(), + None, + Arc::::default(), + file_metadata, + &metadata_cache, + file_reader_options.clone(), + ) + .await + }, + ) + .await?; + Ok(Some(Box::new(v2_adapter::Reader::new( + Arc::new(reader), + schema_per_file, + field_id_to_column_idx, + reader_priority, + file_scheduler, + )))) } async fn open_readers( @@ -1445,14 +1449,15 @@ impl FileFragment { } } - if self.metadata.files.iter().any(|f| f.is_legacy_file()) - != self.metadata.files.iter().all(|f| f.is_legacy_file()) - { + if let Err(error) = Fragment::try_infer_version(std::slice::from_ref(&self.metadata)) { + let first_file = self.metadata.files.first().ok_or_else(|| { + Error::internal("mixed file versions reported for an empty fragment") + })?; return Err(Error::corrupt_file( self.dataset - .data_file_dir(&self.metadata.files[0])? - .join(self.metadata.files[0].path.as_str()), - "Fragment contains a mix of v1 and v2 data files".to_string(), + .data_file_dir(first_file)? + .join(first_file.path.as_str()), + format!("Fragment contains mixed file versions: {error}"), )); } @@ -1695,7 +1700,7 @@ impl FileFragment { if row_offsets.len() > 1 && Self::row_ids_contiguous(row_offsets) { let range = (row_offsets[0] as usize)..(row_offsets[row_offsets.len() - 1] as usize + 1); - reader.legacy_read_range_as_batch(range).await + reader.read_range_as_batch(range).await } else { // FIXME, change this method to streams reader.take_as_batch(row_offsets, None).await @@ -2194,26 +2199,227 @@ impl CacheKey for FileMetadataCacheKey { fn type_name() -> &'static str { "FileMetadata" } -} +} + +#[derive(Debug, Clone)] +struct FileMetadataIndexCacheKey; + +impl CacheKey for FileMetadataIndexCacheKey { + type ValueType = FileMetadataIndex; + + fn key(&self) -> std::borrow::Cow<'_, str> { + "metadata_index".into() + } + + fn type_name() -> &'static str { + "FileMetadataIndex" + } +} + +impl From for Fragment { + fn from(fragment: FileFragment) -> Self { + fragment.metadata + } +} + +/// Typed v1-only read operations used by the legacy pushdown path. +/// +/// Keeping the previous readers here avoids exposing legacy downcasts through +/// [`GenericFileReader`]. Modern readers never implement or simulate these +/// row-group and page-statistics operations. +#[derive(Clone, Debug)] +pub(crate) struct V1FragmentReader { + readers: Vec, + deletion_vec: Option>, + row_id_sequence: Option>, + fragment_id: usize, + with_row_id: bool, + with_row_addr: bool, + make_deletions_null: bool, +} + +impl V1FragmentReader { + fn try_new( + readers: Vec, + deletion_vec: Option>, + row_id_sequence: Option>, + fragment_id: usize, + ) -> Result { + let first_reader = readers.first().ok_or_else(|| { + Error::invalid_input("Cannot create a v1 fragment reader without data files") + })?; + let num_batches = first_reader.reader.num_batches(); + if readers + .iter() + .any(|reader| reader.reader.num_batches() != num_batches) + { + return Err(Error::invalid_input( + "Cannot create a v1 fragment reader from data files with different numbers of batches" + .to_string(), + )); + } + Ok(Self { + readers, + deletion_vec, + row_id_sequence, + fragment_id, + with_row_id: false, + with_row_addr: false, + make_deletions_null: false, + }) + } + + pub(crate) fn with_row_id(&mut self) -> &mut Self { + self.with_row_id = true; + self + } + + pub(crate) fn with_row_address(&mut self) -> &mut Self { + self.with_row_addr = true; + self + } + + pub(crate) fn with_make_deletions_null(&mut self) -> &mut Self { + self.make_deletions_null = true; + self + } + + pub(crate) fn num_batches(&self) -> usize { + self.readers[0].reader.num_batches() + } + + pub(crate) fn num_rows_in_batch(&self, batch_id: u32) -> Option { + let reader = &self.readers[0].reader; + (batch_id < reader.num_batches() as u32) + .then(|| reader.num_rows_in_batch(batch_id as i32) as u32) + } + + pub(crate) async fn read_page_stats( + &self, + projection: Option<&Schema>, + ) -> Result> { + let mut stats_batches = Vec::new(); + for reader in &self.readers { + let schema = match projection { + Some(projection) => Arc::new(reader.projection.intersection(projection)?), + None => reader.projection.clone(), + }; + if let Some(stats_batch) = reader.reader.read_page_stats(&schema.field_ids()).await? { + stats_batches.push(stats_batch); + } + } + + if stats_batches.is_empty() { + Ok(None) + } else { + Ok(Some(merge_batches(&stats_batches)?)) + } + } -#[derive(Debug, Clone)] -struct FileMetadataIndexCacheKey; + pub(crate) async fn read_batch_projected( + &self, + batch_id: usize, + params: impl Into + Clone, + projection: &Schema, + ) -> Result { + let first_reader = &self.readers[0].reader; + // All batches have the same size in v1, except for the last one. + let batch_offset = batch_id * first_reader.num_rows_in_batch(0); + let rows_in_batch = first_reader.num_rows_in_batch(batch_id as i32); -impl CacheKey for FileMetadataIndexCacheKey { - type ValueType = FileMetadataIndex; + let batches = if !projection.fields.is_empty() { + let read_tasks = self.readers.iter().map(|reader| { + let projection = reader.projection.intersection(projection); + let params = params.clone(); + let reader = &reader.reader; - fn key(&self) -> std::borrow::Cow<'_, str> { - "metadata_index".into() - } + async move { + let projection = projection?; + if projection.fields.is_empty() { + Result::Ok(None) + } else { + Ok(Some( + reader + .read_batch(batch_id as i32, params, &projection) + .await?, + )) + } + } + }); + try_join_all(read_tasks) + .await? + .into_iter() + .flatten() + .collect::>() + } else { + let expected_rows = params + .clone() + .into() + .slice(0, rows_in_batch) + .unwrap() + .to_offsets()? + .len(); + vec![RecordBatch::from(StructArray::new_empty_fields( + expected_rows, + None, + ))] + }; - fn type_name() -> &'static str { - "FileMetadataIndex" - } -} + let params = params.into(); + let result = merge_batches(&batches)?; + let file_params = match params { + ReadBatchParams::Indices(indices) => ReadBatchParams::Indices( + indices + .values() + .iter() + .map(|i| *i + batch_offset as u32) + .collect(), + ), + ReadBatchParams::Ranges(_) => { + return Err(Error::internal( + "ReadBatchParams::Ranges should not be used in v1 files".to_string(), + )); + } + ReadBatchParams::RangeFull => { + ReadBatchParams::Range(batch_offset..(batch_offset + rows_in_batch)) + } + ReadBatchParams::RangeFrom(start) => { + ReadBatchParams::Range((start.start + batch_offset)..(batch_offset + rows_in_batch)) + } + ReadBatchParams::RangeTo(end) => { + ReadBatchParams::Range(batch_offset..(end.end + batch_offset)) + } + ReadBatchParams::Range(range) => { + ReadBatchParams::Range((range.start + batch_offset)..(range.end + batch_offset)) + } + }; + let result = lance_table::utils::stream::apply_row_id_and_deletes( + result, + 0, + self.fragment_id as u32, + &RowIdAndDeletesConfig { + params: file_params, + deletion_vector: self.deletion_vec.clone(), + row_id_sequence: self.row_id_sequence.clone(), + with_row_id: self.with_row_id, + with_row_addr: self.with_row_addr, + with_row_last_updated_at_version: false, + with_row_created_at_version: false, + last_updated_at_sequence: None, + created_at_sequence: None, + make_deletions_null: self.make_deletions_null, + total_num_rows: first_reader.len() as u32, + }, + )?; -impl From for Fragment { - fn from(fragment: FileFragment) -> Self { - fragment.metadata + let mut output_schema = ArrowSchema::from(projection); + if self.with_row_id { + output_schema = output_schema.try_with_column(ROW_ID_FIELD.clone())?; + } + if self.with_row_addr { + output_schema = output_schema.try_with_column(ROW_ADDR_FIELD.clone())?; + } + Ok(result.project_by_schema(&output_schema)?) } } @@ -2352,21 +2558,6 @@ impl FragmentReader { num_physical_rows: usize, fragment: Arc, ) -> Result { - if let Some(legacy_reader) = readers.first().and_then(|reader| reader.as_legacy_opt()) { - let num_batches = legacy_reader.num_batches(); - for reader in readers.iter().skip(1) { - if let Some(other_legacy) = reader.as_legacy_opt() { - if other_legacy.num_batches() != num_batches { - return Err(Error::invalid_input("Cannot create FragmentReader from data files with different number of batches" - .to_string())); - } - } else { - return Err(Error::invalid_input( - "Cannot mix legacy and non-legacy readers".to_string(), - )); - } - } - } Ok(Self { readers, output_schema, @@ -2452,195 +2643,6 @@ impl FragmentReader { self } - /// TODO: This method is relied upon by the v1 pushdown mechanism and will need to stay - /// in place until v1 is removed. v2 uses a different mechanism for pushdown and so there - /// is little benefit in updating the v1 pushdown node. - pub(crate) fn legacy_num_batches(&self) -> usize { - let legacy_reader = self.readers[0].as_legacy(); - let num_batches = legacy_reader.num_batches(); - assert!( - self.readers - .iter() - .all(|r| r.as_legacy().num_batches() == num_batches), - "Data files have varying number of batches, which is not yet supported." - ); - num_batches - } - - /// TODO: This method is relied upon by the v1 pushdown mechanism and will need to stay - /// in place until v1 is removed. v2 uses a different mechanism for pushdown and so there - /// is little benefit in updating the v1 pushdown node. - /// - /// This method is also used by the updater. Even though the updater has been updated to - /// use streams, the updater still needs to know the batch size in v1 so that it can create - /// files with the same batch size. - pub(crate) fn legacy_num_rows_in_batch(&self, batch_id: u32) -> Option { - if let Some(legacy_reader) = self.readers.first().and_then(|r| r.as_legacy_opt()) { - if batch_id < legacy_reader.num_batches() as u32 { - Some(legacy_reader.num_rows_in_batch(batch_id as i32) as u32) - } else { - None - } - } else { - None - } - } - - /// Read the page statistics of the fragment for the specified fields. - /// - /// TODO: This method is relied upon by the v1 pushdown mechanism and will need to stay - /// in place until v1 is removed. v2 uses a different mechanism for pushdown and so there - /// is little benefit in updating the v1 pushdown node. - pub(crate) async fn legacy_read_page_stats( - &self, - projection: Option<&Schema>, - ) -> Result> { - let mut stats_batches = vec![]; - for reader in self.readers.iter() { - let schema = match projection { - Some(projection) => Arc::new(reader.projection().intersection(projection)?), - None => reader.projection().clone(), - }; - let reader = reader.as_legacy(); - if let Some(stats_batch) = reader.read_page_stats(&schema.field_ids()).await? { - stats_batches.push(stats_batch); - } - } - - if stats_batches.is_empty() { - Ok(None) - } else { - Ok(Some(merge_batches(&stats_batches)?)) - } - } - - /// Read a batch of rows from the fragment, with a subset of columns. - /// - /// Note: the projection must be a subset of the schema the reader was created with. - /// Otherwise incorrect data will be returned. - /// - /// TODO: This method is relied upon by the v1 pushdown mechanism and will need to stay - /// in place until v1 is removed. v2 uses a different mechanism for pushdown and so there - /// is little benefit in updating the v1 pushdown node. - pub(crate) async fn legacy_read_batch_projected( - &self, - batch_id: usize, - params: impl Into + Clone, - projection: &Schema, - ) -> Result { - let first_reader = self.readers[0].as_legacy(); - // All batches have the same size in v1, except for the last one. - let batch_offset = batch_id * first_reader.num_rows_in_batch(0); - let rows_in_batch = first_reader.num_rows_in_batch(batch_id as i32); - - let batches = if !projection.fields.is_empty() { - let read_tasks = self.readers.iter().map(|reader| { - let projection = reader.projection().intersection(projection); - let params = params.clone(); - - let reader = reader.as_legacy(); - - async move { - // Apply ? inside the task to keep read_tasks a simple iter of futures - // for try_join_all - let projection = projection?; - if projection.fields.is_empty() { - // The projection caused one of the data files to become - // irrelevant and so we can skip it - Result::Ok(None) - } else { - Ok(Some( - reader - .read_batch(batch_id as i32, params, &projection) - .await?, - )) - } - } - }); - let results = try_join_all(read_tasks).await?; - results.into_iter().flatten().collect::>() - } else { - // If we are selecting no columns, we can assume we are just getting - // the row ids. If this is the case, we need to generate an empty - // batch with the correct number of rows. - let expected_rows = params - .clone() - .into() - .slice(0, rows_in_batch) - .unwrap() - .to_offsets()? - .len(); - vec![RecordBatch::from(StructArray::new_empty_fields( - expected_rows, - None, - ))] - }; - - let params = params.into(); - let result = merge_batches(&batches)?; - - // Need to apply deletions and row ids. - // In order to apply deletions we need to change the parameters to be - // relative to the file, not the batch. - let file_params = match params { - ReadBatchParams::Indices(indices) => ReadBatchParams::Indices( - indices - .values() - .iter() - .map(|i| *i + batch_offset as u32) - .collect(), - ), - ReadBatchParams::Ranges(_) => { - return Err(Error::internal( - "ReadBatchParams::Ranges should not be used in v1 files".to_string(), - )); - } - ReadBatchParams::RangeFull => { - ReadBatchParams::Range(batch_offset..(batch_offset + rows_in_batch)) - } - ReadBatchParams::RangeFrom(start) => { - ReadBatchParams::Range((start.start + batch_offset)..(batch_offset + rows_in_batch)) - } - ReadBatchParams::RangeTo(end) => { - ReadBatchParams::Range(batch_offset..(end.end + batch_offset)) - } - ReadBatchParams::Range(range) => { - ReadBatchParams::Range((range.start + batch_offset)..(range.end + batch_offset)) - } - }; - let result = lance_table::utils::stream::apply_row_id_and_deletes( - result, - 0, - self.fragment_id as u32, - &RowIdAndDeletesConfig { - params: file_params, - deletion_vector: self.deletion_vec.clone(), - row_id_sequence: self.row_id_sequence.clone(), - with_row_id: self.with_row_id, - with_row_addr: self.with_row_addr, - with_row_last_updated_at_version: self.with_row_last_updated_at_version, - with_row_created_at_version: self.with_row_created_at_version, - last_updated_at_sequence: self.last_updated_at_sequence.clone(), - created_at_sequence: self.created_at_sequence.clone(), - make_deletions_null: self.make_deletions_null, - total_num_rows: first_reader.len() as u32, - }, - )?; - - let output_schema = { - let mut output_schema = ArrowSchema::from(projection); - if self.with_row_id { - output_schema = output_schema.try_with_column(ROW_ID_FIELD.clone())?; - } - if self.with_row_addr { - output_schema = output_schema.try_with_column(ROW_ADDR_FIELD.clone())?; - } - output_schema - }; - - Ok(result.project_by_schema(&output_schema)?) - } - /// Merge data overlay values onto a stream of base batches. /// /// Runs on physical rows in read order, *before* deletion filtering, so each @@ -2989,11 +2991,8 @@ impl FragmentReader { ) } - // Legacy function that reads a range of data and concatenates the results - // into a single batch - // - // TODO: Move away from this by changing callers to support consuming a stream - pub async fn legacy_read_range_as_batch(&self, range: Range) -> Result { + /// Reads a range and concatenates the result into one batch. + pub async fn read_range_as_batch(&self, range: Range) -> Result { let batches = self .take_range( range.start as u32..range.end as u32, @@ -3134,7 +3133,7 @@ mod tests { use lance_core::ROW_ID; use lance_core::utils::tempfile::TempStrDir; use lance_datagen::{RowCount, array, gen_batch}; - use lance_file::version::LanceFileVersion; + use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; use lance_file::writer::FileWriterOptions; use lance_io::{assert_io_eq, assert_io_lt, object_store::ObjectStore}; use pretty_assertions::assert_eq; @@ -3229,7 +3228,7 @@ mod tests { use arrow_schema::{DataType, Field as ArrowField, Fields, Schema as ArrowSchema}; use lance_core::datatypes::Schema; use lance_file::version::LanceFileVersion; - use lance_file::writer::{FileWriter, FileWriterOptions}; + use lance_file::writer::FileWriterOptions; use lance_io::utils::CachedFileSize; use lance_table::format::DataFile; use lance_table::format::overlay::{DataOverlayFile, OverlayCoverage}; @@ -3298,22 +3297,20 @@ mod tests { let filename = format!("{name}.lance"); let path = Path::from(format!("data/{filename}")); let obj_writer = dataset.object_store.create(&path).await.unwrap(); - let mut writer = FileWriter::try_new( + let file_version = version.resolve(); + let mut writer = lance_file::versions::create_writer( + file_version, obj_writer, overlay_schema, - FileWriterOptions { - format_version: Some(version), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); - let (major, minor) = writer.version().to_numbers(); for (column_index, array) in columns.into_iter().enumerate() { writer.write_column(column_index, array).await.unwrap(); } let summary = writer.finish().await.unwrap(); - let mut data_file = DataFile::new_unstarted(filename, major, minor); + let mut data_file = DataFile::new_unstarted(filename, file_version); data_file.fields = writer .field_id_to_column_indices() .iter() @@ -5138,19 +5135,20 @@ mod tests { dataset.delete("i >= 0 and i < 15").await.unwrap(); let fragment = &dataset.get_fragments()[0]; - let mut reader = fragment - .open( + let read_config = FragReadConfig::default().with_row_id(true); + + if data_storage_version == LanceFileVersion::Legacy { + let mut reader = crate::dataset::versions::open_v1_fragment_reader( + fragment, dataset.schema(), - FragReadConfig::default().with_row_id(true), + &read_config, ) .await .unwrap(); - reader.with_make_deletions_null(); - - if data_storage_version == LanceFileVersion::Legacy { + reader.with_make_deletions_null(); // The first batch is entirely deleted, deleted rows will be marked null with null row ids. let batch1 = reader - .legacy_read_batch_projected(0, .., dataset.schema()) + .read_batch_projected(0, .., dataset.schema()) .await .unwrap(); assert_eq!( @@ -5161,7 +5159,7 @@ mod tests { // The second batch is partially deleted, so the deleted rows will be // marked null with null row ids. let batch2 = reader - .legacy_read_batch_projected(1, .., dataset.schema()) + .read_batch_projected(1, .., dataset.schema()) .await .unwrap(); assert_eq!( @@ -5171,7 +5169,7 @@ mod tests { // The final batch is not deleted, so it will be returned as-is. let batch3 = reader - .legacy_read_batch_projected(2, .., dataset.schema()) + .read_batch_projected(2, .., dataset.schema()) .await .unwrap(); assert_eq!( @@ -5179,6 +5177,8 @@ mod tests { &UInt64Array::from_iter_values(20..30) ); } else { + let mut reader = fragment.open(dataset.schema(), read_config).await.unwrap(); + reader.with_make_deletions_null(); let to_batches = |range: Range| { let batch_size = range.len() as u32; let fut = reader.take_range(range, batch_size); @@ -5720,7 +5720,7 @@ mod tests { .unwrap(); let (object_store, base_path) = ObjectStore::from_uri(test_uri).await.unwrap(); - let file_reader = PreviousFileReader::try_new_with_fragment_id( + let file_reader = V1FileReader::try_new_with_fragment_id( &object_store, &base_path .clone() @@ -5871,7 +5871,7 @@ mod tests { FragReadConfig::default().with_row_id(true), ) .await?; - let batch = reader.legacy_read_range_as_batch(0..20).await?; + let batch = reader.read_range_as_batch(0..20).await?; let expected_data = RecordBatch::try_new( Arc::new(ArrowSchema::new(vec![ROW_ID_FIELD.clone()])), @@ -5911,8 +5911,12 @@ mod tests { let store = ObjectStore::local(); let file_path = dataset.data_dir().join("some_file.lance"); let object_writer = store.create(&file_path).await.unwrap(); - let mut file_writer = - lance_file::writer::FileWriter::new_lazy(object_writer, FileWriterOptions::default()); + let mut file_writer = lance_file::versions::create_lazy_writer( + LanceFileVersion::Stable.resolve(), + object_writer, + FileWriterOptions::default(), + ) + .unwrap(); file_writer.write_batch(&new_data).await.unwrap(); file_writer.finish().await.unwrap(); @@ -5927,6 +5931,23 @@ mod tests { LanceFileVersion::Stable.resolve() ); + let mismatched_path = dataset.data_dir().join("mismatched_file.lance"); + let object_writer = store.create(&mismatched_path).await.unwrap(); + let mut mismatched_writer = lance_file::versions::create_lazy_writer( + lance_file::version::ConcreteFileVersion::V2_0, + object_writer, + FileWriterOptions::default(), + ) + .unwrap(); + mismatched_writer.write_batch(&new_data).await.unwrap(); + mismatched_writer.finish().await.unwrap(); + + let err = FileFragment::create_from_file("mismatched_file.lance", &dataset, 1, Some(128)) + .await + .unwrap_err(); + assert!(matches!(err, Error::InvalidInput { .. })); + assert!(err.to_string().contains("File version mismatch")); + let op = Operation::Append { fragments: vec![frag], }; @@ -6079,23 +6100,28 @@ mod tests { base_id: None, }; - let full_struct = - ReaderProjection::from_column_names(LanceFileVersion::V2_1, &schema, &["s"]).unwrap(); + let full_struct = file_versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, + &schema, + &["s"], + ) + .unwrap(); assert_eq!(full_struct.column_indices.len(), 2); - assert!(!FileFragment::should_try_indexed_metadata( - &data_file, - &full_struct, - LanceFileVersion::V2_1 - )); + let valid_column_count = data_file + .column_indices + .iter() + .filter(|column_index| **column_index >= 0) + .count(); + assert!(full_struct.column_indices.len().saturating_mul(4) >= valid_column_count); - let partial_struct = - ReaderProjection::from_column_names(LanceFileVersion::V2_1, &schema, &["s.x"]).unwrap(); + let partial_struct = file_versions::reader_projection_from_column_names( + ConcreteFileVersion::V2_1, + &schema, + &["s.x"], + ) + .unwrap(); assert_eq!(partial_struct.column_indices.len(), 1); - assert!(FileFragment::should_try_indexed_metadata( - &data_file, - &partial_struct, - LanceFileVersion::V2_1 - )); + assert!(partial_struct.column_indices.len().saturating_mul(4) < valid_column_count); } #[tokio::test] diff --git a/rust/lance/src/dataset/fragment/session.rs b/rust/lance/src/dataset/fragment/session.rs index de50255bb72..77e4125bbc4 100644 --- a/rust/lance/src/dataset/fragment/session.rs +++ b/rust/lance/src/dataset/fragment/session.rs @@ -74,7 +74,7 @@ impl FragmentSession { if row_offsets.len() > 1 && FileFragment::row_ids_contiguous(row_offsets) { let range = (row_offsets[0] as usize)..(row_offsets[row_offsets.len() - 1] as usize + 1); - self.reader.legacy_read_range_as_batch(range).await + self.reader.read_range_as_batch(range).await } else { self.reader.take_as_batch(row_offsets, None).await } @@ -89,7 +89,7 @@ mod tests { use arrow_schema::{DataType, Field as ArrowField, Schema as ArrowSchema}; use lance_core::ROW_ADDR; use lance_core::utils::tempfile::TempStrDir; - use lance_encoding::version::LanceFileVersion; + use lance_file::version::LanceFileVersion; use rstest::rstest; use std::sync::Arc; diff --git a/rust/lance/src/dataset/fragment/write.rs b/rust/lance/src/dataset/fragment/write.rs index 2ab3bd1d92b..39b639694aa 100644 --- a/rust/lance/src/dataset/fragment/write.rs +++ b/rust/lance/src/dataset/fragment/write.rs @@ -8,10 +8,13 @@ use lance_core::Error; use lance_core::datatypes::Schema; use lance_datafusion::chunker::{break_stream, chunk_stream}; use lance_datafusion::utils::StreamingWriteSource; -use lance_file::previous::writer::FileWriter as PreviousFileWriter; +#[cfg(test)] use lance_file::version::LanceFileVersion; -use lance_file::writer::FileWriterOptions; +use lance_file::version::stable_file_version; +use lance_file::versions::v1::writer::FileWriter as V1FileWriter; +use lance_file::writer::FileWriter; use lance_io::object_store::ObjectStore; +use lance_io::traits::Writer; use lance_io::utils::CachedFileSize; use lance_table::format::{DataFile, Fragment}; use lance_table::io::manifest::ManifestDescribing; @@ -22,7 +25,7 @@ use uuid::Uuid; use crate::Result; use crate::dataset::builder::DatasetBuilder; use crate::dataset::utils::SchemaAdapter; -use crate::dataset::write::{do_write_fragments, validate_and_resolve_target_bases_with_primary}; +use crate::dataset::write::validate_and_resolve_target_bases_with_primary; use crate::dataset::{DATA_DIR, Dataset, ReadParams, WriteMode, WriteParams}; /// Generates a filename optimized for S3 throughput using a UUID-based approach. @@ -113,7 +116,18 @@ impl<'a> FragmentCreateBuilder<'a> { // the single-fragment create path must do the same or the raw UTF-8 string bytes // would be written into a column whose schema declares JSONB, corrupting reads. let stream = SchemaAdapter::new(stream.schema()).to_physical_stream(stream); - self.write_impl(stream, schema, id).await + let version = self + .write_params + .map(|params| params.storage_version_or_default()) + .unwrap_or_else(stable_file_version); + crate::dataset::versions::write_fragment( + version, + self, + stream, + schema, + id.unwrap_or_default(), + ) + .await } /// Write multi fragment which separated by max_rows_per_file. @@ -125,12 +139,16 @@ impl<'a> FragmentCreateBuilder<'a> { self.write_fragments_v2_impl(stream, schema).await } - async fn write_v2_impl( + pub(crate) async fn write_current_impl( &self, + create_writer: F, stream: SendableRecordBatchStream, schema: Schema, id: u64, - ) -> Result { + ) -> Result + where + F: FnOnce(Box, Schema, String) -> Result<(FileWriter, DataFile)>, + { let params = self.write_params.map(Cow::Borrowed).unwrap_or_default(); let progress = params.progress.as_ref(); @@ -147,18 +165,7 @@ impl<'a> FragmentCreateBuilder<'a> { let mut fragment = Fragment::new(id); let full_path = base_path.clone().join(DATA_DIR).join(filename.clone()); let obj_writer = object_store.create(&full_path).await?; - let mut writer = lance_file::writer::FileWriter::try_new( - obj_writer, - schema, - FileWriterOptions { - format_version: params.data_storage_version, - ..Default::default() - }, - )?; - - let (major, minor) = writer.version().to_numbers(); - - let data_file = DataFile::new_unstarted(filename, major, minor); + let (mut writer, data_file) = create_writer(obj_writer, schema, filename)?; fragment.files.push(data_file); progress.begin(&fragment).await?; @@ -210,7 +217,7 @@ impl<'a> FragmentCreateBuilder<'a> { Self::validate_schema(&schema, stream.schema().as_ref())?; - let version = params.data_storage_version.unwrap_or_default(); + let version = params.storage_version_or_default(); let needs_existing_dataset = params.target_base_names_or_paths.is_some() || params.target_bases.is_some() || params.target_all_bases.is_some() @@ -241,35 +248,27 @@ impl<'a> FragmentCreateBuilder<'a> { } else { None }; - do_write_fragments( + crate::dataset::versions::write_fragments_direct( + version, existing_dataset.as_ref(), object_store, &base_path, &schema, stream, params, - version, target_bases_info, Vec::new(), ) .await } - async fn write_impl( + pub(crate) async fn write_v1_impl( &self, stream: SendableRecordBatchStream, schema: Schema, - id: Option, + id: u64, ) -> Result { - let id = id.unwrap_or_default(); - let params = self.write_params.map(Cow::Borrowed).unwrap_or_default(); - - let storage_version = params.storage_version_or_default(); - - if storage_version != LanceFileVersion::Legacy { - return self.write_v2_impl(stream, schema, id).await; - } let progress = params.progress.as_ref(); Self::validate_schema(&schema, stream.schema().as_ref())?; @@ -283,7 +282,7 @@ impl<'a> FragmentCreateBuilder<'a> { let filename = format!("{}.lance", generate_random_filename()); let mut fragment = Fragment::with_file_legacy(id, &filename, &schema, None); let full_path = base_path.clone().join(DATA_DIR).join(filename.clone()); - let mut writer = PreviousFileWriter::::try_new( + let mut writer = V1FileWriter::::try_new( &object_store, &full_path, schema, @@ -693,7 +692,7 @@ mod tests { assert!(!fragment.files.is_empty()); fragment.files.iter().for_each(|f| { - let (major_version, minor_version) = file_version.to_numbers(); + let (major_version, minor_version) = file_version.resolve().to_data_file_numbers(); assert_eq!(f.file_major_version, major_version); assert_eq!(f.file_minor_version, minor_version); }) @@ -725,7 +724,7 @@ mod tests { assert!(!fragment.is_empty()); fragment[0].files.iter().for_each(|f| { - let (major_version, minor_version) = file_version.to_numbers(); + let (major_version, minor_version) = file_version.resolve().to_data_file_numbers(); assert_eq!(f.file_major_version, major_version); assert_eq!(f.file_minor_version, minor_version); }) diff --git a/rust/lance/src/dataset/hash_joiner.rs b/rust/lance/src/dataset/hash_joiner.rs index 92b67599134..d5410d9ae3a 100644 --- a/rust/lance/src/dataset/hash_joiner.rs +++ b/rust/lance/src/dataset/hash_joiner.rs @@ -194,21 +194,11 @@ impl HashJoiner { } pub fn check_lance_support_null(array: &ArrayRef, dataset: &Dataset) -> Result<()> { - if array.null_count() > 0 && !dataset.lance_supports_nulls(array.data_type()) { - return Err(Error::invalid_input(format!( - "Join produced null values for type: {:?}, but storing \ - nulls for this data type is not supported by the \ - dataset's current Lance file format version: {:?}. This \ - can be caused by an explicit null in the new data.", - array.data_type(), - dataset - .manifest() - .data_storage_format - .lance_file_version() - .unwrap() - ))); - } - Ok(()) + super::versions::validate_nulls( + dataset.manifest().data_storage_format.lance_file_format(), + array.data_type(), + array.null_count() > 0, + ) } /// Collecting the data using the index column from left table, @@ -307,6 +297,48 @@ mod tests { Dataset::open(&uri).await.unwrap() } + #[test] + fn test_null_validation_is_selected_by_exact_version() { + use lance_file::version::ConcreteFileVersion; + + assert!( + super::super::versions::validate_nulls( + ConcreteFileVersion::V1, + &DataType::Int32, + true, + ) + .is_err() + ); + assert!( + super::super::versions::validate_nulls(ConcreteFileVersion::V1, &DataType::Utf8, true,) + .is_ok() + ); + assert!( + super::super::versions::validate_nulls( + ConcreteFileVersion::V2_0, + &DataType::Struct(arrow_schema::Fields::empty()), + true, + ) + .is_err() + ); + assert!( + super::super::versions::validate_nulls( + ConcreteFileVersion::V2_1, + &DataType::Struct(arrow_schema::Fields::empty()), + true, + ) + .is_ok() + ); + assert!( + super::super::versions::validate_nulls( + ConcreteFileVersion::V1, + &DataType::Int32, + false, + ) + .is_ok() + ); + } + #[tokio::test] async fn test_joiner_collect() { let schema = Arc::new(Schema::new(vec![ diff --git a/rust/lance/src/dataset/index.rs b/rust/lance/src/dataset/index.rs index 29c82075fec..705c82183ac 100644 --- a/rust/lance/src/dataset/index.rs +++ b/rust/lance/src/dataset/index.rs @@ -16,7 +16,7 @@ use crate::index::scalar::infer_scalar_index_details; use arrow_schema::DataType; use async_trait::async_trait; use lance_core::{Error, Result}; -use lance_encoding::version::LanceFileVersion; +use lance_file::version::ConcreteFileVersion; use lance_index::is_system_index; use lance_index::pb::VectorIndexDetails; use lance_index::scalar::lance_format::LanceIndexStore; @@ -25,6 +25,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; use super::optimize::{IndexRemapper, IndexRemapperOptions}; +use super::versions; #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct DatasetIndexRemapperOptions {} @@ -157,18 +158,12 @@ pub trait LanceIndexStoreExt { Self: Sized; } -/// Extract the lance file version from a dataset, floored at V2_0. +/// Select the exact file version used for index files in this dataset version. /// /// Index files should never use the legacy format. If the dataset uses legacy -/// format or doesn't have a version set, V2_0 is used as the minimum. -pub(crate) fn dataset_format_version(dataset: &Dataset) -> LanceFileVersion { - dataset - .manifest - .data_storage_format - .lance_file_version() - .ok() - .map(|v| v.resolve().max(LanceFileVersion::V2_0)) - .unwrap_or(LanceFileVersion::V2_0) +/// format, V2_0 is selected explicitly by the dataset composition table. +pub(crate) fn dataset_format_version(dataset: &Dataset) -> ConcreteFileVersion { + versions::index_file_version(dataset.manifest.data_storage_format.lance_file_format()) } #[async_trait] diff --git a/rust/lance/src/dataset/mem_wal/memtable/flush.rs b/rust/lance/src/dataset/mem_wal/memtable/flush.rs index 6937894b2d0..7db8d7276f5 100644 --- a/rust/lance/src/dataset/mem_wal/memtable/flush.rs +++ b/rust/lance/src/dataset/mem_wal/memtable/flush.rs @@ -192,16 +192,14 @@ impl MemTableFlusher { /// (data fragments and index files) are written at this same version so the /// whole shard stays on one format (e.g. a 2.2 base => 2.2 flushed gens). /// - /// Falls back to [`LanceFileVersion::default`] when no base dataset exists at + /// Falls back to the default selector's exact version when no base dataset exists at /// `base_uri` (e.g. flusher unit tests that run without a committed base). /// In production MemWAL is always initialized on a real dataset, so the base /// version is inherited; other open errors are propagated. - async fn base_storage_version(&self) -> Result { + async fn base_storage_version(&self) -> Result { match self.open_base().await { - Ok(dataset) => dataset.manifest().data_storage_format.lance_file_version(), - Err(Error::DatasetNotFound { .. }) => { - Ok(lance_file::version::LanceFileVersion::default()) - } + Ok(dataset) => Ok(dataset.manifest().data_storage_format.lance_file_format()), + Err(Error::DatasetNotFound { .. }) => Ok(lance_file::version::stable_file_version()), Err(e) => Err(e), } } @@ -361,7 +359,7 @@ impl MemTableFlusher { // that the dense HNSW graph List columns overflow at scale). let write_params = WriteParams { max_rows_per_file: usize::MAX, - data_storage_version: Some(self.base_storage_version().await?), + data_storage_version: Some(self.base_storage_version().await?.to_selector()), // Write the generation through the base's store params + session so it // uses the same store the base was opened with. Adapted for the // generation URI: a path-bound store binding would send this write at @@ -906,7 +904,8 @@ impl MemTableFlusher { use arrow_schema::Schema as ArrowSchema; use lance_arrow::FixedSizeListArrayExt; use lance_core::ROW_ID; - use lance_file::writer::{FileWriter, FileWriterOptions}; + use lance_file::versions as file_versions; + use lance_file::writer::FileWriterOptions; use lance_index::pb; use lance_index::vector::DISTANCE_TYPE_KEY; use lance_index::vector::SQ_CODE_COLUMN; @@ -925,7 +924,8 @@ impl MemTableFlusher { // Write the index files at the base dataset's storage version (matches // the flushed data fragments; 2.2 avoids the v2.1 miniblock chunk cap). - let storage_version = self.base_storage_version().await?; + let storage_version = + crate::dataset::versions::index_file_version(self.base_storage_version().await?); let index_uuid = uuid::Uuid::new_v4(); let index_dir = gen_path @@ -997,13 +997,11 @@ impl MemTableFlusher { storage_ivf.add_partition(storage_batch.num_rows() as u32); let storage_path = index_dir.clone().join(INDEX_AUXILIARY_FILE_NAME); - let mut storage_writer = FileWriter::try_new( + let mut storage_writer = file_versions::create_writer( + storage_version, self.object_store.create(&storage_path).await?, (&storage_schema).try_into()?, - FileWriterOptions { - format_version: Some(storage_version), - ..Default::default() - }, + FileWriterOptions::default(), )?; storage_writer.write_batch(&storage_batch).await?; @@ -1072,13 +1070,11 @@ impl MemTableFlusher { ArrowSchema::new(fields) }; let index_path = index_dir.clone().join(INDEX_FILE_NAME); - let mut index_writer = FileWriter::try_new( + let mut index_writer = file_versions::create_writer( + storage_version, self.object_store.create(&index_path).await?, (&index_schema).try_into()?, - FileWriterOptions { - format_version: Some(storage_version), - ..Default::default() - }, + FileWriterOptions::default(), )?; index_writer.write_batch(&hnsw_batch).await?; diff --git a/rust/lance/src/dataset/optimize.rs b/rust/lance/src/dataset/optimize.rs index d8e82537536..e4ed49e184c 100644 --- a/rust/lance/src/dataset/optimize.rs +++ b/rust/lance/src/dataset/optimize.rs @@ -94,6 +94,7 @@ use super::transaction::{ Operation, RewriteGroup, RewrittenIndex, Transaction, TransactionBuilder, }; use super::utils::make_rowid_capture_stream; +use super::versions; use super::{WriteMode, WriteParams, cleanup_data_fragments, write_fragments_internal}; use crate::Dataset; use crate::Result; @@ -121,12 +122,11 @@ use roaring::{RoaringBitmap, RoaringTreemap}; use serde::{Deserialize, Serialize}; use tracing::{info, warn}; -mod binary_copy; +pub(super) mod binary_copy; pub mod remapping; use crate::index::frag_reuse::build_new_frag_reuse_index; use crate::io::deletion::read_dataset_deletion_file; -use binary_copy::rewrite_files_binary_copy; pub use remapping::{IgnoreRemap, IndexRemapper, IndexRemapperOptions, RemappedIndex}; /// Controls how data is rewritten during compaction. @@ -504,7 +504,8 @@ async fn can_use_binary_copy( options: &CompactionOptions, fragments: &[Fragment], ) -> bool { - can_use_binary_copy_impl(dataset, options, fragments) + let version = dataset.manifest.data_storage_format.lance_file_format(); + versions::can_use_binary_copy(version, dataset, options, fragments) .await .unwrap_or_else(|err| { log::warn!("Binary copy disabled due to error: {}", err); @@ -512,13 +513,12 @@ async fn can_use_binary_copy( }) } -async fn can_use_binary_copy_impl( +pub(super) async fn can_use_binary_copy_current( dataset: &Dataset, options: &CompactionOptions, fragments: &[Fragment], ) -> Result { use lance_file::reader::FileReader as LFReader; - use lance_file::version::LanceFileVersion; use lance_io::scheduler::{ScanScheduler, SchedulerConfig}; if matches!(options.compaction_mode(), CompactionMode::Reencode) { @@ -535,28 +535,11 @@ async fn can_use_binary_copy_impl( return Ok(false); } - let storage_ok = dataset - .manifest - .data_storage_format - .lance_file_version() - .map(|v| !matches!(v.resolve(), LanceFileVersion::Legacy)) - .unwrap_or(false); - if !storage_ok { - log::debug!("Binary copy disabled: dataset uses legacy storage format"); - return Ok(false); - } - if fragments.is_empty() { log::debug!("Binary copy disabled: no fragments to compact"); return Ok(false); } - let storage_file_version = dataset - .manifest - .data_storage_format - .lance_file_version()? - .resolve(); - if fragments[0].files.is_empty() { log::debug!( "Binary copy disabled: fragment {} has no data files", @@ -566,8 +549,6 @@ async fn can_use_binary_copy_impl( } let ref_fields = &fragments[0].files[0].fields; let ref_cols = &fragments[0].files[0].column_indices; - let mut is_same_version = true; - for fragment in fragments { if fragment.deletion_file.is_some() { log::debug!( @@ -578,16 +559,6 @@ async fn can_use_binary_copy_impl( } for data_file in &fragment.files { - let version_ok = LanceFileVersion::try_from_major_minor( - data_file.file_major_version, - data_file.file_minor_version, - ) - .map(|v| v.resolve()) - .is_ok_and(|v| v == storage_file_version); - - if !version_ok { - is_same_version = false; - } if data_file.fields != *ref_fields || data_file.column_indices != *ref_cols { return Ok(false); } @@ -624,11 +595,6 @@ async fn can_use_binary_copy_impl( } } - if !is_same_version { - log::debug!("Binary copy disabled: data files use different file versions"); - return Ok(false); - } - Ok(true) } @@ -1736,7 +1702,9 @@ async fn rewrite_files( } if can_binary_copy { - new_fragments = rewrite_files_binary_copy( + let version = dataset.manifest.data_storage_format.lance_file_format(); + new_fragments = versions::rewrite_files_binary_copy( + version, dataset.as_ref(), &fragments, ¶ms, @@ -8251,7 +8219,7 @@ mod tests { // overlay files into a fresh fragment with the overlays (and deletions) // materialized into the base data. use arrow_array::record_batch; - use lance_file::writer::{FileWriter, FileWriterOptions}; + use lance_file::writer::FileWriterOptions; use lance_io::utils::CachedFileSize; use lance_table::format::DataFile; use lance_table::format::overlay::{DataOverlayFile, OverlayCoverage}; @@ -8303,22 +8271,20 @@ mod tests { let filename = format!("{}.lance", Uuid::new_v4()); let path = dataset.base.clone().join(DATA_DIR).join(filename.as_str()); let obj_writer = dataset.object_store.create(&path).await.unwrap(); - let mut writer = FileWriter::try_new( + let file_version = LanceFileVersion::Stable.resolve(); + let mut writer = lance_file::versions::create_writer( + file_version, obj_writer, overlay_schema, - FileWriterOptions { - format_version: Some(LanceFileVersion::Stable), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); - let (major, minor) = writer.version().to_numbers(); for (column_index, array) in columns.into_iter().enumerate() { writer.write_column(column_index, array).await.unwrap(); } let summary = writer.finish().await.unwrap(); - let mut data_file = DataFile::new_unstarted(filename, major, minor); + let mut data_file = DataFile::new_unstarted(filename, file_version); data_file.fields = writer .field_id_to_column_indices() .iter() diff --git a/rust/lance/src/dataset/optimize/binary_copy.rs b/rust/lance/src/dataset/optimize/binary_copy.rs index f22737fff89..c76e0ea300f 100644 --- a/rust/lance/src/dataset/optimize/binary_copy.rs +++ b/rust/lance/src/dataset/optimize/binary_copy.rs @@ -7,90 +7,44 @@ use crate::dataset::DATA_DIR; use crate::dataset::WriteParams; use crate::dataset::fragment::write::generate_random_filename; use crate::datatypes::Schema; -use lance_arrow::DataTypeExt; use lance_core::Error; -use lance_encoding::decoder::{ColumnInfo, PageEncoding, PageInfo as DecPageInfo}; -use lance_encoding::version::LanceFileVersion; -use lance_file::format::pbfile; +use lance_encoding::decoder::{ColumnInfo, PageInfo as DecPageInfo}; use lance_file::reader::FileReader as LFReader; +use lance_file::version::ConcreteFileVersion; +use lance_file::versions as file_versions; use lance_file::writer::{FileWriter, FileWriterOptions}; use lance_io::scheduler::{ScanScheduler, SchedulerConfig}; -use lance_io::traits::Writer; use lance_table::format::{DataFile, Fragment}; use prost::Message; use prost_types::Any; use std::ops::Range; use std::sync::Arc; -use tokio::io::AsyncWriteExt; - -const ALIGN: usize = 64; - -/// Apply 64-byte alignment padding for V2.1+ files. -/// -/// For V2.1+, writes padding bytes to align the current position to a 64-byte boundary. -/// For V2.0 and earlier, no padding is applied as alignment is not required. -/// -/// Returns the new position after padding (if any). -async fn apply_alignment_padding( - writer: &mut dyn Writer, - current_pos: u64, - version: LanceFileVersion, -) -> Result { - if version >= LanceFileVersion::V2_1 { - static ZERO_BUFFER: std::sync::OnceLock> = std::sync::OnceLock::new(); - let zero_buf = ZERO_BUFFER.get_or_init(|| vec![0u8; ALIGN]); - - let pad = (ALIGN - (current_pos as usize % ALIGN)) % ALIGN; - if pad != 0 { - writer.write_all(&zero_buf[..pad]).await?; - return Ok(current_pos + pad as u64); - } - } - Ok(current_pos) -} async fn init_writer_if_necessary( dataset: &Dataset, - current_writer: &mut Option>, + version: ConcreteFileVersion, + current_writer: &mut Option, current_filename: &mut Option, ) -> Result { if current_writer.is_none() { let filename = format!("{}.lance", generate_random_filename()); let path = dataset.base.clone().join(DATA_DIR).join(filename.as_str()); - let writer = dataset.object_store.create(&path).await?; - *current_writer = Some(writer); + let object_writer = dataset.object_store.create(&path).await?; + *current_writer = Some(file_versions::create_lazy_writer( + version, + object_writer, + FileWriterOptions::default(), + )?); *current_filename = Some(filename); return Ok(true); } Ok(false) } -/// v2_0 vs v2_1+ field-to-column index mapping -/// - v2_1+ stores only leaf columns; non-leaf fields get `-1` in the mapping -/// - v2_0 includes structural headers as columns; non-leaf fields map to a concrete index -fn compute_field_column_indices( - schema: &Schema, - full_field_ids_len: usize, - version: LanceFileVersion, -) -> Vec { - let is_structural = version >= LanceFileVersion::V2_1; - let mut field_column_indices: Vec = Vec::with_capacity(full_field_ids_len); - let mut curr_col_idx: i32 = 0; - for field in schema.fields_pre_order() { - if field.is_packed_struct() || field.is_leaf() || !is_structural { - field_column_indices.push(curr_col_idx); - curr_col_idx += 1; - } else { - field_column_indices.push(-1); - } - } - field_column_indices -} - /// Finalize the current output file and return it as a single [Fragment]. /// - Ensures an output writer / filename is present (creates a new file if needed). /// - Converts the in-memory `col_pages` / `col_buffers` into `ColumnInfo` metadata, draining them. -/// - Applies v2_0 structural header rules (single page, normalized `num_rows` and `priority`). +/// - Lets the exact file version normalize copied column metadata. /// - Writes the Lance footer via [flush_footer] and registers the resulting [DataFile] in a [Fragment]. /// /// PAY ATTENTION current function will: @@ -99,28 +53,24 @@ fn compute_field_column_indices( #[allow(clippy::too_many_arguments)] async fn finalize_current_output_file( schema: &Schema, - full_field_ids: &[i32], - current_writer: &mut Option>, + version: ConcreteFileVersion, + current_writer: &mut Option, current_filename: &mut Option, current_page_table: &[ColumnInfo], col_pages: &mut [Vec], col_buffers: &mut [Vec<(u64, u64)>], - is_non_leaf_column: &[bool], total_rows_in_current: u64, - version: LanceFileVersion, ) -> Result { let mut final_cols: Vec> = Vec::with_capacity(current_page_table.len()); for (i, column_info) in current_page_table.iter().enumerate() { let mut pages_vec = std::mem::take(&mut col_pages[i]); - // For v2_0 struct headers, force a single page and set num_rows to total - if version == LanceFileVersion::V2_0 - && is_non_leaf_column.get(i).copied().unwrap_or(false) - && !pages_vec.is_empty() - { - pages_vec[0].num_rows = total_rows_in_current; - pages_vec[0].priority = 0; - pages_vec.truncate(1); - } + file_versions::finalize_external_metadata_column( + version, + schema, + i, + &mut pages_vec, + total_rows_in_current, + )?; let pages_arc = Arc::from(pages_vec.into_boxed_slice()); let buffers_vec = std::mem::take(&mut col_buffers[i]); final_cols.push(Arc::new(ColumnInfo::new( @@ -130,15 +80,19 @@ async fn finalize_current_output_file( column_info.encoding.clone(), ))); } - let writer = current_writer.take().unwrap(); - flush_footer(writer, schema, &final_cols, total_rows_in_current, version).await?; + let mut writer = current_writer + .take() + .ok_or_else(|| Error::internal("binary copy output writer was not initialized"))?; + flush_footer(&mut writer, schema, &final_cols, total_rows_in_current).await?; // Register the newly closed output file as a fragment data file - let (maj, min) = version.to_numbers(); let mut fragment = Fragment::new(0); - let field_column_indices = compute_field_column_indices(schema, full_field_ids.len(), version); - let mut data_file = DataFile::new_unstarted(current_filename.take().unwrap(), maj, min); - data_file.fields = full_field_ids.to_vec().into(); + let (field_ids, field_column_indices) = file_versions::data_file_columns(version, schema); + let filename = current_filename + .take() + .ok_or_else(|| Error::internal("binary copy output filename was not initialized"))?; + let mut data_file = DataFile::new_unstarted(filename, version); + data_file.fields = field_ids.into(); data_file.column_indices = field_column_indices.into(); fragment.files.push(data_file); fragment.physical_rows = Some(total_rows_in_current as usize); @@ -157,11 +111,9 @@ async fn finalize_current_output_file( /// └── final flush for remaining rows /// /// Behavior highlights: -/// - Assumes all input files share the same Lance file version; version drives column-count -/// calculation (v2.0 includes structural headers, v2.1+ only leaf columns). +/// - Assumes all input files share the same Lance file version. /// - Preserves stable row ids by concatenating row-id sequences when enabled. -/// - Enforces 64-byte alignment for page and buffer writes in V2.1+ files (V2.0 does not require alignment). -/// - For v2.0, preserves single-page structural headers and normalizes their row counts/priority. +/// - Delegates physical-column mapping and copied metadata normalization to the exact file version. /// - Flushes an output file once `max_rows_per_file` rows are accumulated, then repeats. /// /// Parameters: @@ -170,6 +122,7 @@ async fn finalize_current_output_file( /// - `params`: write parameters (uses `max_rows_per_file`). /// - `read_batch_bytes_opt`: optional I/O batch size when coalescing page reads. pub async fn rewrite_files_binary_copy( + version: ConcreteFileVersion, dataset: &Dataset, fragments: &[Fragment], params: &WriteParams, @@ -185,54 +138,19 @@ pub async fn rewrite_files_binary_copy( // - Reads page and buffer regions directly from source files in bounded batches // - Appends them to a new output file with alignment, updating offsets // - Recomputes page priorities by adding the cumulative row count to preserve order - // - For v2_0, enforces single-page structural header columns when closing a file // - Writes a new footer (schema descriptor, column metadata, offset tables, version) // - Optionally carries forward stable row ids and persists them inline in fragment metadata // Merge small Lance files into larger ones by page-level binary copy. let schema = dataset.schema().clone(); - let full_field_ids = schema.field_ids(); - - // The previous checks have ensured that the file versions of all files are consistent. - let version = LanceFileVersion::try_from_major_minor( - fragments[0].files[0].file_major_version, - fragments[0].files[0].file_minor_version, - ) - .unwrap() - .resolve(); - // v2.0 and v2.1+ handle structural headers differently during file writing: - // - v2_0 materializes ALL fields in pre-order traversal (leaf fields + non-leaf struct headers), - // which means the ColumnInfo set includes all fields in pre-order traversal. - // - v2_1+ materializes fields that are either leaf columns OR packed structs. Non-leaf structural - // headers (unpacked structs with children) are not stored as columns. - // As a result, the ColumnInfo set contains leaf fields and packed structs. - // To correctly align copy layout, we derive `column_count` by version: - // - v2_0: use total number of fields in pre-order (leaf + non-leaf headers) - // - v2_1+: use only the number of leaf fields plus packed structs - let column_count = if version == LanceFileVersion::V2_0 { - schema.fields_pre_order().count() - } else { - schema - .fields_pre_order() - .filter(|f| f.is_packed_struct() || f.is_leaf()) - .count() - }; - - // v2_0 compatibility: build a map to identify non-leaf structural header columns - // - In v2_0 these headers exist as columns and must have a single page - // - In v2_1+ these headers are not stored as columns and this map is unused - let mut is_non_leaf_column: Vec = vec![false; column_count]; - if version == LanceFileVersion::V2_0 { - for (col_idx, field) in schema.fields_pre_order().enumerate() { - // Only mark non-packed Struct fields (lists remain as leaf data carriers) - let is_non_leaf = field.data_type().is_struct() && !field.is_packed_struct(); - is_non_leaf_column[col_idx] = is_non_leaf; - } - } + let column_count = schema + .fields + .iter() + .map(|field| file_versions::physical_column_count(version, field)) + .sum(); let mut out: Vec = Vec::new(); - let mut current_writer: Option> = None; + let mut current_writer: Option = None; let mut current_filename: Option = None; - let mut current_pos: u64 = 0; let mut current_page_table: Vec = Vec::new(); // Baseline column encodings captured from the first source file; all subsequent // files must match per-column to safely concatenate column-level buffers. @@ -279,38 +197,34 @@ pub async fn rewrite_files_binary_copy( .collect(); baseline_col_encoding_bytes = src_column_infos .iter() - .map(|ci| Any::from_msg(&ci.encoding).unwrap().encode_to_vec()) - .collect(); + .map(|ci| Ok(Any::from_msg(&ci.encoding)?.encode_to_vec())) + .collect::>>()?; } // Iterate through each column of the current data file of the current fragment for (col_idx, src_column_info) in src_column_infos.iter().enumerate() { - // v2_0 compatibility: special handling for non-leaf structural header columns - // - v2_0 expects structural header columns to have a SINGLE page; they carry layout - // metadata only and are not true data carriers. - // - When merging multiple input files via binary copy, naively appending pages would - // yield multiple pages for the same structural header column, violating v2_0 rules. - // - To preserve v2_0 invariants, we skip pages beyond the first one for these columns. - // - During finalization we also normalize the single remaining page’s `num_rows` to the - // total number of rows in the output file and reset `priority` to 0. - // - For v2_1+ this logic does not apply because non-leaf headers are not stored as columns. - let is_non_leaf = col_idx < is_non_leaf_column.len() && is_non_leaf_column[col_idx]; - if is_non_leaf && !col_pages[col_idx].is_empty() { - continue; - } - - if init_writer_if_necessary(dataset, &mut current_writer, &mut current_filename) - .await? - { - current_pos = 0; - } + let has_existing_pages = !col_pages[col_idx].is_empty(); + file_versions::copy_external_metadata_column( + version, + &schema, + col_idx, + has_existing_pages, + || async { + init_writer_if_necessary( + dataset, + version, + &mut current_writer, + &mut current_filename, + ) + .await?; - let read_batch_bytes: u64 = read_batch_bytes_opt.unwrap_or(16 * 1024 * 1024) as u64; + let read_batch_bytes: u64 = + read_batch_bytes_opt.unwrap_or(16 * 1024 * 1024) as u64; - let mut page_index = 0; + let mut page_index = 0; - // Iterate through each page of the current column in the current data file of the current fragment - while page_index < src_column_info.page_infos.len() { + // Iterate through each page of the current column in the current data file of the current fragment + while page_index < src_column_info.page_infos.len() { let mut batch_ranges: Vec> = Vec::new(); let mut batch_counts: Vec = Vec::new(); let mut batch_bytes: u64 = 0; @@ -368,49 +282,42 @@ pub async fn rewrite_files_binary_copy( for (buffer_idx, (_, size)) in page.buffer_offsets_and_sizes.iter().enumerate() { - let writer = current_writer.as_mut().unwrap().as_mut(); - current_pos = - apply_alignment_padding(writer, current_pos, version).await?; - let start = current_pos; - if *size == 0 { - new_offsets.push((start, 0)); + let writer = current_writer.as_mut().ok_or_else(|| { + Error::internal("binary copy output writer was not initialized") + })?; + let bytes = if *size == 0 { + None } else { - let bytes = bytes_iter.next().ok_or_else(|| { + Some(bytes_iter.next().ok_or_else(|| { Error::execution(format!( "binary copy: missing page buffer bytes while rewriting data file \ (column {col_idx}, page {page_idx}, buffer {buffer_idx}, expected size {size})", )) - })?; - writer.write_all(&bytes).await?; - current_pos += bytes.len() as u64; - new_offsets.push((start, bytes.len() as u64)); - } + })?) + }; + let (start, written) = writer + .write_external_buffer(bytes.as_deref().unwrap_or_default()) + .await?; + new_offsets.push((start, written)); } - // manual clone encoding - let encoding = if page.encoding.is_structural() { - PageEncoding::Structural(page.encoding.as_structural().clone()) - } else { - PageEncoding::Legacy(page.encoding.as_legacy().clone()) - }; // `priority` acts as the global row offset for this page, ensuring // downstream iterators maintain the correct logical order across // merged inputs. let new_page_info = DecPageInfo { num_rows: page.num_rows, priority: page.priority + total_rows_in_current, - encoding, + encoding: page.encoding.clone(), buffer_offsets_and_sizes: Arc::from(new_offsets.into_boxed_slice()), }; col_pages[col_idx].push(new_page_info); } - } // finished scheduling & copying pages for this column in the current source file + } // finished scheduling & copying pages for this column in the current source file - if !src_column_info.buffer_offsets_and_sizes.is_empty() { + if !src_column_info.buffer_offsets_and_sizes.is_empty() { // Validate column-level encoding compatibility before copying buffers - let src_col_encoding_bytes = Any::from_msg(&src_column_info.encoding) - .unwrap() - .encode_to_vec(); + let src_col_encoding_bytes = + Any::from_msg(&src_column_info.encoding)?.encode_to_vec(); let baseline_bytes = &baseline_col_encoding_bytes[col_idx]; if src_col_encoding_bytes != *baseline_bytes { return Err(Error::execution(format!( @@ -434,24 +341,29 @@ pub async fn rewrite_files_binary_copy( for (buffer_idx, (_, size)) in src_column_info.buffer_offsets_and_sizes.iter().enumerate() { - let writer = current_writer.as_mut().unwrap().as_mut(); - current_pos = apply_alignment_padding(writer, current_pos, version).await?; - let start = current_pos; - if *size == 0 { - col_buffers[col_idx].push((start, 0)); + let writer = current_writer.as_mut().ok_or_else(|| { + Error::internal("binary copy output writer was not initialized") + })?; + let bytes = if *size == 0 { + None } else { - let bytes = bytes_iter.next().ok_or_else(|| { + Some(bytes_iter.next().ok_or_else(|| { Error::execution(format!( "binary copy: missing column buffer bytes while rewriting data file \ (column {col_idx}, buffer {buffer_idx}, expected size {size})", )) - })?; - writer.write_all(&bytes).await?; - current_pos += bytes.len() as u64; - col_buffers[col_idx].push((start, bytes.len() as u64)); - } + })?) + }; + let (start, written) = writer + .write_external_buffer(bytes.as_deref().unwrap_or_default()) + .await?; + col_buffers[col_idx].push((start, written)); } - } + } + Ok(()) + }, + ) + .await?; } // finished all columns in the current source file // Accumulate rows for the current output file and flush when reaching the threshold @@ -459,21 +371,18 @@ pub async fn rewrite_files_binary_copy( if total_rows_in_current >= max_rows_per_file { let fragment_out = finalize_current_output_file( &schema, - &full_field_ids, + version, &mut current_writer, &mut current_filename, ¤t_page_table, &mut col_pages, &mut col_buffers, - &is_non_leaf_column, total_rows_in_current, - version, ) .await?; // Reset state for next output file current_writer = None; - current_pos = 0; current_page_table.clear(); for v in col_pages.iter_mut() { v.clear(); @@ -489,18 +398,17 @@ pub async fn rewrite_files_binary_copy( if total_rows_in_current > 0 { // Flush remaining rows as a final output file - init_writer_if_necessary(dataset, &mut current_writer, &mut current_filename).await?; + init_writer_if_necessary(dataset, version, &mut current_writer, &mut current_filename) + .await?; let frag = finalize_current_output_file( &schema, - &full_field_ids, + version, &mut current_writer, &mut current_filename, ¤t_page_table, &mut col_pages, &mut col_buffers, - &is_non_leaf_column, total_rows_in_current, - version, ) .await?; out.push(frag); @@ -512,9 +420,7 @@ pub async fn rewrite_files_binary_copy( /// /// This function does not manually craft the footer. Instead it: /// - Pads the current `ObjectWriter` position to a 64‑byte boundary (required for v2_1+ readers). -/// - Converts the collected per‑column info (`final_cols`) into `ColumnMetadata`. -/// - Constructs a `lance_file::writer::FileWriter` with the active `schema`, column metadata, -/// and `total_rows_in_current`. +/// - Initializes the active `FileWriter` from the collected column metadata. /// - Calls `FileWriter::finish()` to emit column metadata, offset tables, global buffers /// (schema descriptor), version, and to close the writer. /// @@ -522,81 +428,14 @@ pub async fn rewrite_files_binary_copy( /// - All page data and column‑level buffers referenced by `final_cols` have already been written /// to `writer`; otherwise offsets in the footer will be invalid. /// -/// Version notes: -/// - v2_0 structural single‑page enforcement is handled when building `final_cols`; this function -/// only performs consistent finalization. async fn flush_footer( - mut writer: Box, + writer: &mut FileWriter, schema: &Schema, final_cols: &[Arc], total_rows_in_current: u64, - version: LanceFileVersion, ) -> Result<()> { - let pos = writer.tell().await? as u64; - let _new_pos = apply_alignment_padding(writer.as_mut(), pos, version).await?; - - let mut col_metadatas = Vec::with_capacity(final_cols.len()); - for col in final_cols { - let pages = col - .page_infos - .iter() - .map(|page_info| { - let encoded_encoding = match &page_info.encoding { - PageEncoding::Legacy(array_encoding) => { - Any::from_msg(array_encoding)?.encode_to_vec() - } - PageEncoding::Structural(page_layout) => { - Any::from_msg(page_layout)?.encode_to_vec() - } - }; - let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = page_info - .buffer_offsets_and_sizes - .as_ref() - .iter() - .cloned() - .unzip(); - Ok(pbfile::column_metadata::Page { - buffer_offsets, - buffer_sizes, - encoding: Some(pbfile::Encoding { - location: Some(pbfile::encoding::Location::Direct( - pbfile::DirectEncoding { - encoding: encoded_encoding, - }, - )), - }), - length: page_info.num_rows, - priority: page_info.priority, - }) - }) - .collect::>>()?; - let (buffer_offsets, buffer_sizes): (Vec<_>, Vec<_>) = - col.buffer_offsets_and_sizes.iter().cloned().unzip(); - let encoded_col_encoding = Any::from_msg(&col.encoding)?.encode_to_vec(); - let column = pbfile::ColumnMetadata { - pages, - buffer_offsets, - buffer_sizes, - encoding: Some(pbfile::Encoding { - location: Some(pbfile::encoding::Location::Direct(pbfile::DirectEncoding { - encoding: encoded_col_encoding, - })), - }), - }; - col_metadatas.push(column); - } - let mut file_writer = FileWriter::new_lazy( - writer, - FileWriterOptions { - format_version: Some(version), - ..Default::default() - }, - ); - file_writer.initialize_with_external_metadata( - schema.clone(), - col_metadatas, - total_rows_in_current, - ); - file_writer.finish().await?; + writer.write_external_buffer(&[]).await?; + writer.initialize_with_external_columns(schema.clone(), final_cols, total_rows_in_current)?; + writer.finish().await?; Ok(()) } diff --git a/rust/lance/src/dataset/optimize/tests/binary_copy.rs b/rust/lance/src/dataset/optimize/tests/binary_copy.rs index 08a4fe024ce..168a4792d32 100644 --- a/rust/lance/src/dataset/optimize/tests/binary_copy.rs +++ b/rust/lance/src/dataset/optimize/tests/binary_copy.rs @@ -3,9 +3,16 @@ use super::*; +const NON_LEGACY_VERSIONS: [LanceFileVersion; 4] = [ + LanceFileVersion::V2_0, + LanceFileVersion::V2_1, + LanceFileVersion::V2_2, + LanceFileVersion::V2_3, +]; + #[tokio::test] async fn test_binary_copy_merge_small_files() { - for version in LanceFileVersion::iter_non_legacy() { + for version in NON_LEGACY_VERSIONS { do_test_binary_copy_merge_small_files(version).await; } } @@ -45,9 +52,70 @@ async fn do_test_binary_copy_merge_small_files(version: LanceFileVersion) { assert_eq!(before, after); } +#[tokio::test] +async fn test_binary_copy_packed_struct_column_mapping() { + for version in NON_LEGACY_VERSIONS { + do_test_binary_copy_packed_struct_column_mapping(version).await; + } +} + +async fn do_test_binary_copy_packed_struct_column_mapping(version: LanceFileVersion) { + use arrow_array::StructArray; + use arrow_schema::Fields; + use std::collections::HashMap; + + let packed_fields = Fields::from(vec![Field::new("child", DataType::Int32, true)]); + let packed_field = Field::new("packed", DataType::Struct(packed_fields.clone()), true) + .with_metadata(HashMap::from([("packed".to_string(), "true".to_string())])); + let schema = Arc::new(Schema::new(vec![ + packed_field, + Field::new("tail", DataType::Int32, true), + ])); + let packed: ArrayRef = Arc::new(StructArray::new( + packed_fields, + vec![Arc::new(Int32Array::from(vec![1, 2, 3, 4]))], + None, + )); + let tail: ArrayRef = Arc::new(Int32Array::from(vec![10, 20, 30, 40])); + let batch = RecordBatch::try_new(schema.clone(), vec![packed, tail]).unwrap(); + + let test_dir = TempStrDir::default(); + let mut dataset = Dataset::write( + RecordBatchIterator::new(vec![Ok(batch)], schema), + &test_dir, + Some(WriteParams { + data_storage_version: Some(version), + max_rows_per_file: 2, + ..Default::default() + }), + ) + .await + .unwrap(); + assert_eq!(dataset.get_fragments().len(), 2); + + let before = dataset.scan().try_into_batch().await.unwrap(); + compact_files( + &mut dataset, + CompactionOptions { + target_rows_per_fragment: 100_000, + compaction_mode: Some(CompactionMode::ForceBinaryCopy), + ..Default::default() + }, + None, + ) + .await + .unwrap(); + let after = dataset.scan().try_into_batch().await.unwrap(); + assert_eq!(before, after); + + let data_file = &dataset.manifest.fragments[0].files[0]; + assert_eq!(data_file.fields.len(), 2); + assert_eq!(data_file.column_indices.as_ref(), &[0, 1]); +} + #[tokio::test] async fn test_binary_copy_empty_string_scalar_index() { - for version in LanceFileVersion::iter_non_legacy() { + for version in NON_LEGACY_VERSIONS { do_test_binary_copy_empty_string_scalar_index(version).await; } } @@ -115,7 +183,7 @@ async fn do_test_binary_copy_empty_string_scalar_index(version: LanceFileVersion #[tokio::test] async fn test_binary_copy_with_defer_remap() { - for version in LanceFileVersion::iter_non_legacy() { + for version in NON_LEGACY_VERSIONS { do_test_binary_copy_with_defer_remap(version).await; } } @@ -187,7 +255,7 @@ async fn do_test_binary_copy_with_defer_remap(version: LanceFileVersion) { #[tokio::test] async fn test_binary_copy_preserves_stable_row_ids() { - for version in LanceFileVersion::iter_non_legacy() { + for version in NON_LEGACY_VERSIONS { do_binary_copy_preserves_stable_row_ids(version).await; } } @@ -320,7 +388,7 @@ async fn do_binary_copy_preserves_stable_row_ids(version: LanceFileVersion) { #[tokio::test] async fn test_binary_copy_remaps_unstable_row_ids() { - for version in LanceFileVersion::iter_non_legacy() { + for version in NON_LEGACY_VERSIONS { do_binary_copy_remaps_unstable_row_ids(version).await; } } @@ -685,7 +753,8 @@ async fn test_can_use_binary_copy_version_mismatch() { ); // Simulate mixed file versions by marking the second fragment as v2.1. - let (v21_major, v21_minor) = LanceFileVersion::V2_1.to_numbers(); + let (v21_major, v21_minor) = + lance_file::version::ConcreteFileVersion::V2_1.to_data_file_numbers(); for file in &mut frags[1].files { file.file_major_version = v21_major; file.file_minor_version = v21_minor; @@ -723,7 +792,7 @@ async fn test_can_use_binary_copy_reject_deletions() { #[tokio::test] async fn test_binary_copy_compaction_with_complex_schema() { - for version in LanceFileVersion::iter_non_legacy() { + for version in NON_LEGACY_VERSIONS { do_test_binary_copy_compaction_with_complex_schema(version).await; } } diff --git a/rust/lance/src/dataset/scanner.rs b/rust/lance/src/dataset/scanner.rs index 804129ad690..847b30f559a 100644 --- a/rust/lance/src/dataset/scanner.rs +++ b/rust/lance/src/dataset/scanner.rs @@ -83,6 +83,7 @@ use tracing::{Span, info_span, instrument}; use uuid::Uuid; use super::Dataset; +use super::versions; use crate::dataset::overlay::{ collect_overlay_stale_frags, collect_overlay_stale_rows_for_segment, overlaid_fragments, }; @@ -294,10 +295,10 @@ impl MaterializationStyle { } #[derive(Debug)] -struct PlannedFilteredScan { - plan: Arc, - limit_pushed_down: bool, - filter_pushed_down: bool, +pub(super) struct PlannedFilteredScan { + pub(super) plan: Arc, + pub(super) limit_pushed_down: bool, + pub(super) filter_pushed_down: bool, } pub struct FilterPlan { @@ -2800,7 +2801,7 @@ impl Scanner { // Do not call this directly, use filtered_read instead // // First return value is the plan, second is whether the limit was pushed down - async fn legacy_filtered_read( + pub(super) async fn legacy_filtered_read( &self, filter_plan: &ExprFilterPlan, projection: Projection, @@ -2904,7 +2905,7 @@ impl Scanner { // Helper function for filtered_read // // Do not call this directly, use filtered_read instead - async fn new_filtered_read( + pub(super) async fn new_filtered_read( &self, filter_plan: &ExprFilterPlan, projection: Projection, @@ -3027,34 +3028,20 @@ impl Scanner { scan_range: Option>, is_prefilter: bool, ) -> Result { - // Use legacy path if dataset uses legacy storage format - if self.dataset.is_legacy_storage() { - self.legacy_filtered_read( - filter_plan, - projection, - make_deletions_null, - fragments, - scan_range, - is_prefilter, - ) - .await - } else { - let limit_pushed_down = scan_range.is_some(); - let plan = self - .new_filtered_read( - filter_plan, - projection, - make_deletions_null, - fragments, - scan_range, - ) - .await?; - Ok(PlannedFilteredScan { - filter_pushed_down: true, - limit_pushed_down, - plan, - }) - } + versions::filtered_read( + self.dataset + .manifest() + .data_storage_format + .lance_file_format(), + self, + filter_plan, + projection, + make_deletions_null, + fragments, + scan_range, + is_prefilter, + ) + .await } fn u64s_as_take_input(&self, u64s: Vec) -> Result> { @@ -5324,11 +5311,26 @@ impl Scanner { return Ok(input); } + versions::take( + self.dataset + .manifest() + .data_storage_format + .lance_file_format(), + self, + input, + output_projection, + ) + } + + pub(super) fn take_current( + &self, + input: Arc, + output_projection: Projection, + ) -> Result> { let input_schema = input.schema(); let has_row_id = input_schema.column_with_name(ROW_ID).is_some(); let has_row_addr = input_schema.column_with_name(ROW_ADDR).is_some(); - // The v1 reader cannot serve a FilteredReadExec - if !self.dataset.is_legacy_storage() && (has_row_id || has_row_addr) { + if has_row_id || has_row_addr { // Pass the full (un-subtracted) target so a rebuild against a // different child re-derives what to fetch, and preserve carried // identity columns (downstream nodes may key off them; the final @@ -5355,6 +5357,14 @@ impl Scanner { )?)); } + self.take_legacy(input, output_projection) + } + + pub(super) fn take_legacy( + &self, + input: Arc, + output_projection: Projection, + ) -> Result> { let coalesced = Arc::new(CoalesceBatchesExec::new( input.clone(), self.get_batch_size(), @@ -9118,6 +9128,14 @@ mod test { } } + fn uses_legacy_scan(&self) -> bool { + self.dataset + .manifest() + .data_storage_format + .lance_file_format() + == lance_file::version::ConcreteFileVersion::V1 + } + async fn check_vector_scalar_indexed_and_refine(&self, params: &ScalarTestParams) { let (query_plan, batch) = self .run_query( @@ -9127,7 +9145,7 @@ mod test { ) .await; // Materialization is always required if there is a refine - if self.dataset.is_legacy_storage() { + if self.uses_legacy_scan() { assert!(query_plan.contains("MaterializeIndex")); } // The result should not include the sample query @@ -9159,7 +9177,7 @@ mod test { let (query_plan, batch) = self .run_query("indexed != 50", Some(self.sample_query()), params) .await; - if self.dataset.is_legacy_storage() { + if self.uses_legacy_scan() { if params.use_index { // An ANN search whose prefilter is fully satisfied by the index should be // able to use a ScalarIndexQuery @@ -9208,7 +9226,7 @@ mod test { async fn check_simple_indexed_only(&self, params: &ScalarTestParams) { let (query_plan, batch) = self.run_query("indexed != 50", None, params).await; // Materialization is always required for non-vector search - if self.dataset.is_legacy_storage() { + if self.uses_legacy_scan() { assert!(query_plan.contains("MaterializeIndex")); } else { assert!(query_plan.contains("LanceRead")); @@ -9249,7 +9267,7 @@ mod test { params ).await; // Materialization is always required for non-vector search - if self.dataset.is_legacy_storage() { + if self.uses_legacy_scan() { assert!(query_plan.contains("MaterializeIndex")); } else { assert!(query_plan.contains("LanceRead")); diff --git a/rust/lance/src/dataset/schema_evolution.rs b/rust/lance/src/dataset/schema_evolution.rs index 40eac95e919..d984fdd96b8 100644 --- a/rust/lance/src/dataset/schema_evolution.rs +++ b/rust/lance/src/dataset/schema_evolution.rs @@ -25,14 +25,13 @@ use lance_arrow::SchemaExt; use lance_core::datatypes::{Field, Schema}; use lance_datafusion::utils::StreamingWriteSource; use lance_encoding::constants::{PACKED_STRUCT_LEGACY_META_KEY, PACKED_STRUCT_META_KEY}; -use lance_encoding::version::LanceFileVersion; +#[cfg(test)] +use lance_file::version::ConcreteFileVersion; use lance_table::format::Fragment; -mod optimize; +pub mod optimize; -use optimize::{ - ChainedNewColumnTransformOptimizer, NewColumnTransformOptimizer, SqlToAllNullsOptimizer, -}; +use optimize::{ChainedNewColumnTransformOptimizer, NewColumnTransformOptimizer}; async fn validate_no_nulls_before_making_non_nullable(dataset: &Dataset, path: &str) -> Result<()> { let field = dataset.schema().field(path).ok_or_else(|| { @@ -148,44 +147,19 @@ impl ColumnAlteration { } } -/// Limit casts to same type. This is mostly to filter out weird casts like -/// casting a string to a boolean or float to string. -fn is_upcast_downcast(from_type: &DataType, to_type: &DataType, version: LanceFileVersion) -> bool { - use DataType::*; - match (from_type, to_type) { - // Legacy storage cannot materialize a fresh Dictionary column via - // alter because the writer expects `field.dictionary` metadata to be - // pre-populated, which the alter pipeline does not compute. - (_, Dictionary(_, _)) if matches!(version, LanceFileVersion::Legacy) => false, - // These need to be in front - (Dictionary(_, from_value_type), _) => { - is_upcast_downcast(from_value_type, to_type, version) - } - (_, Dictionary(_, to_value_type)) => is_upcast_downcast(from_type, to_value_type, version), - (from, to) if from.is_integer() => to.is_integer(), - (from, to) if from.is_floating() => to.is_floating(), - (from, to) if from.is_temporal() => to.is_temporal(), - (Boolean, to) => matches!(to, Boolean), - (Utf8 | LargeUtf8, to) => matches!(to, Utf8 | LargeUtf8), - (Binary | LargeBinary, to) => matches!(to, Binary | LargeBinary), - (Decimal128(_, _) | Decimal256(_, _), to) => { - matches!(to, Decimal128(_, _) | Decimal256(_, _)) - } - (List(from_field) | LargeList(from_field) | FixedSizeList(from_field, _), to) => match to { - List(to_field) | LargeList(to_field) | FixedSizeList(to_field, _) => { - is_upcast_downcast(from_field.data_type(), to_field.data_type(), version) - } - _ => false, - }, - - _ => false, - } -} - trait ArrowFieldExt { fn is_packed(&self) -> bool; } +#[cfg(test)] +fn is_upcast_downcast( + from_type: &DataType, + to_type: &DataType, + version: ConcreteFileVersion, +) -> bool { + super::versions::is_upcast_downcast(version, from_type, to_type) +} + impl ArrowFieldExt for ArrowField { fn is_packed(&self) -> bool { let metadata = self.metadata(); @@ -196,10 +170,10 @@ impl ArrowFieldExt for ArrowField { } } -fn check_field_conflict( +pub fn check_field_conflict_with( left: &ArrowField, right: &ArrowField, - version: &LanceFileVersion, + validate_nested_column_add: fn(&ArrowField) -> Result<()>, ) -> Result<()> { if left.name() != right.name() { return Ok(()); @@ -207,13 +181,7 @@ fn check_field_conflict( match (left.data_type(), right.data_type()) { (DataType::Struct(fl), DataType::Struct(fr)) => { - if !version.support_add_sub_column() { - return Err(Error::invalid_input(format!( - "Column {} is a struct col, add sub column is not supported in Lance file version {}", - left.name(), - version - ))); - } + validate_nested_column_add(left)?; if left.is_packed() || right.is_packed() { return Err(Error::invalid_input(format!( @@ -224,15 +192,19 @@ fn check_field_conflict( for l_field in fl.iter() { if let Some((_, r_field)) = fr.find(l_field.name()) { - check_field_conflict(l_field, r_field, version)?; + check_field_conflict_with(l_field, r_field, validate_nested_column_add)?; } } Ok(()) } - (DataType::List(fl), DataType::List(fr)) => check_field_conflict(fl, fr, version), - (DataType::LargeList(fl), DataType::LargeList(fr)) => check_field_conflict(fl, fr, version), + (DataType::List(fl), DataType::List(fr)) => { + check_field_conflict_with(fl, fr, validate_nested_column_add) + } + (DataType::LargeList(fl), DataType::LargeList(fr)) => { + check_field_conflict_with(fl, fr, validate_nested_column_add) + } (DataType::FixedSizeList(fl, _), DataType::FixedSizeList(fr, _)) => { - check_field_conflict(fl, fr, version) + check_field_conflict_with(fl, fr, validate_nested_column_add) } (l_type, r_type) if l_type == r_type => Err(Error::invalid_input(format!( "Column {} already exists in the dataset", @@ -248,6 +220,15 @@ fn check_field_conflict( } } +#[cfg(test)] +fn check_field_conflict( + left: &ArrowField, + right: &ArrowField, + version: &ConcreteFileVersion, +) -> Result<()> { + super::versions::check_field_conflict(*version, left, right) +} + pub(super) async fn add_columns_to_fragments( dataset: &Dataset, transforms: NewColumnTransform, @@ -257,12 +238,12 @@ pub(super) async fn add_columns_to_fragments( ) -> Result<(Vec, Schema, Vec)> { // Check names early (before calling add_columns_impl) to avoid extra work if // the names are wrong. - let version = dataset.manifest.data_storage_format.lance_file_version()?; + let version = dataset.manifest.data_storage_format.lance_file_format(); let check_names = |output_schema: &ArrowSchema| { for field in &dataset.schema().fields { if let Ok(out_field) = output_schema.field_with_name(&field.name) { let ds_field = ArrowField::from(field); - check_field_conflict(&ds_field, out_field, &version)?; + super::versions::check_field_conflict(version, &ds_field, out_field)?; } } Ok::<(), Error>(()) @@ -270,10 +251,7 @@ pub(super) async fn add_columns_to_fragments( // Optimize the transforms let mut optimizer = ChainedNewColumnTransformOptimizer::new(vec![]); - // ALlNull transform can not performed on legacy files - if !dataset.is_legacy_storage() { - optimizer.add_optimizer(Box::new(SqlToAllNullsOptimizer::new())); - } + super::versions::configure_new_column_optimizers(version, &mut optimizer); let transforms = optimizer.optimize(dataset, transforms)?; let (output_schema, new_fragments, fragments_to_cleanup) = match transforms { @@ -392,15 +370,7 @@ pub(super) async fn add_columns_to_fragments( .map(|f| f.metadata.clone()) .collect::>(); - // Check if any of the fragment's files are using the legacy dataset version if so, we - // can't add all-null columns as a metadata-only operation. The reason is because we - // use the NullReader for fragments that have missing columns and we can't mix legacy - // and non-legacy readers when reading the fragment. - if dataset.is_legacy_storage() { - return Err(Error::not_supported_source( - "Cannot add all-null columns to legacy dataset version.".into(), - )); - } + super::versions::validate_metadata_only_null_columns(version)?; Ok((output_schema, fragments, Vec::new())) } @@ -713,7 +683,7 @@ pub(super) async fn alter_columns( let mut cast_fields: Vec<(Field, Field)> = Vec::new(); let mut next_field_id = dataset.manifest.max_field_id() + 1; - let version = dataset.manifest.data_storage_format.lance_file_version()?; + let version = dataset.manifest.data_storage_format.lance_file_format(); for alteration in alterations { let field_src = dataset.schema().field(&alteration.path).ok_or_else(|| { @@ -740,7 +710,7 @@ pub(super) async fn alter_columns( if let Some(data_type) = &alteration.data_type { if !(can_cast_types(&field_src.data_type(), data_type) - && is_upcast_downcast(&field_src.data_type(), data_type, version)) + && super::versions::is_upcast_downcast(version, &field_src.data_type(), data_type)) { return Err(Error::invalid_input(format!( "Cannot cast column \"{}\" from {:?} to {:?}", @@ -909,9 +879,10 @@ pub(super) async fn drop_columns(dataset: &mut Dataset, columns: &[&str]) -> Res } } - let version = dataset.manifest.data_storage_format.lance_file_version()?; + let version = dataset.manifest.data_storage_format.lance_file_format(); let columns_to_remove = dataset.manifest.schema.project(columns)?; - let new_schema = exclude(&dataset.manifest.schema, &columns_to_remove, &version)?; + let new_schema = + super::versions::exclude_schema(version, &dataset.manifest.schema, &columns_to_remove)?; if new_schema.fields.is_empty() { return Err(Error::invalid_input( @@ -932,17 +903,19 @@ pub(super) async fn drop_columns(dataset: &mut Dataset, columns: &[&str]) -> Res Ok(()) } -/// Exclude the fields from `other` Schema, and returns a new Schema. -pub fn exclude(source: &Schema, other: &Schema, version: &LanceFileVersion) -> Result { +/// Exclude the fields from `other` Schema using the selected nested-field rule. +pub fn exclude_with( + source: &Schema, + other: &Schema, + exclude_nested_field: fn(&Field, &Field) -> Option, +) -> Result { let other: Schema = other.try_into().map_err(|_| { Error::schema("The other schema is not compatible with this schema".to_string()) })?; let mut fields = vec![]; for field in source.fields.iter() { if let Some(other_field) = other.field(&field.name) { - if version.support_remove_sub_column(field) - && let Some(f) = field.exclude(other_field) - { + if let Some(f) = exclude_nested_field(field, other_field) { fields.push(f) } } else { @@ -955,6 +928,11 @@ pub fn exclude(source: &Schema, other: &Schema, version: &LanceFileVersion) -> R }) } +#[cfg(test)] +fn exclude(source: &Schema, other: &Schema, version: &ConcreteFileVersion) -> Result { + super::versions::exclude_schema(*version, source, other) +} + #[cfg(test)] mod test { use std::{collections::HashMap, fs, num::NonZero, path::Path as StdPath, sync::Mutex}; @@ -967,7 +945,7 @@ mod test { use super::*; use arrow_schema::Fields as ArrowFields; use lance_core::utils::tempfile::TempStrDir; - use lance_file::version::LanceFileVersion; + use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; use lance_table::format::{BasePath, DataFile}; use rstest::rstest; @@ -1295,8 +1273,7 @@ mod test { "checkpointed.lance", vec![dataset.manifest.max_field_id() + 1], vec![0], - 2, - 2, + ConcreteFileVersion::V2_2, NonZero::new(17), None, )); @@ -2459,7 +2436,7 @@ mod test { let schema = Schema::try_from(&arrow_schema).unwrap(); let projection = schema.project(&["a", "b.f2", "b.f3"]).unwrap(); - let excluded = exclude(&schema, &projection, &LanceFileVersion::V2_2).unwrap(); + let excluded = exclude(&schema, &projection, &ConcreteFileVersion::V2_2).unwrap(); let expected_arrow_schema = ArrowSchema::new(vec![ ArrowField::new( @@ -3127,8 +3104,8 @@ mod test { let dict_i16_utf8 = Dictionary(Box::new(Int16), Box::new(Utf8)); let dict_i32_large_utf8 = Dictionary(Box::new(Int32), Box::new(LargeUtf8)); let dict_i32_int64 = Dictionary(Box::new(Int32), Box::new(Int64)); - let stable = LanceFileVersion::Stable; - let legacy = LanceFileVersion::Legacy; + let stable = LanceFileVersion::Stable.resolve(); + let legacy = LanceFileVersion::Legacy.resolve(); // Dict(_, Utf8) -> Utf8 / LargeUtf8 (decode direction): both versions. assert!(is_upcast_downcast(&dict_i32_utf8, &Utf8, stable)); @@ -3624,7 +3601,7 @@ mod test { DataType::Struct(vec![ArrowField::new("a", DataType::Int32, false)].into()), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // different struct let field1 = ArrowField::new( @@ -3637,7 +3614,7 @@ mod test { DataType::Struct(vec![ArrowField::new("b", DataType::Int32, false)].into()), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_ok()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_ok()); // same nested struct let inner_struct1 = ArrowField::new( @@ -3652,22 +3629,22 @@ mod test { ); let field1 = ArrowField::new("test", DataType::Struct(vec![inner_struct1].into()), false); let field2 = ArrowField::new("test", DataType::Struct(vec![inner_struct2].into()), false); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // basic type with different name let field1 = ArrowField::new("test1", DataType::Int32, false); let field2 = ArrowField::new("test2", DataType::Int32, false); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_ok()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_ok()); // basic type with same name let field1 = ArrowField::new("test", DataType::Int32, false); let field2 = ArrowField::new("test", DataType::Int32, false); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // different basic type let field1 = ArrowField::new("test", DataType::Int32, false); let field2 = ArrowField::new("test", DataType::Float64, false); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // partial conflict let field1 = ArrowField::new( @@ -3692,7 +3669,7 @@ mod test { ), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // same list let field1 = ArrowField::new( @@ -3705,7 +3682,7 @@ mod test { DataType::List(Arc::new(ArrowField::new("item", DataType::Int32, true))), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // list with struct let field1 = ArrowField::new( @@ -3726,7 +3703,7 @@ mod test { ))), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // list with different struct let field1 = ArrowField::new( @@ -3747,7 +3724,7 @@ mod test { ))), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_ok()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_ok()); // list of struct and basic let field1 = ArrowField::new( @@ -3764,7 +3741,7 @@ mod test { DataType::List(Arc::new(ArrowField::new("item", DataType::Int32, true))), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // FixedSizeList with struct let field1 = ArrowField::new( @@ -3791,7 +3768,7 @@ mod test { ), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // FixedSizeList with different struct let field1 = ArrowField::new( @@ -3818,7 +3795,7 @@ mod test { ), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_ok()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_ok()); // LargeList with struct let field1 = ArrowField::new( @@ -3839,7 +3816,7 @@ mod test { ))), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_err()); // LargeList with different struct let field1 = ArrowField::new( @@ -3860,7 +3837,7 @@ mod test { ))), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_ok()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_ok()); // packed struct let mut packed_meta = HashMap::new(); @@ -3879,7 +3856,7 @@ mod test { DataType::Struct(vec![ArrowField::new("b", DataType::Int32, false)].into()), false, ); - assert!(check_field_conflict(&field1, &field2, &LanceFileVersion::V2_2).is_ok()); + assert!(check_field_conflict(&field1, &field2, &ConcreteFileVersion::V2_2).is_ok()); let new_packed_field = ArrowField::new( "new_packed", @@ -3892,7 +3869,7 @@ mod test { DataType::Struct(vec![new_packed_field].into()), false, ); - assert!(check_field_conflict(&field1, &field3, &LanceFileVersion::V2_2).is_ok()); + assert!(check_field_conflict(&field1, &field3, &ConcreteFileVersion::V2_2).is_ok()); let conflict_field = ArrowField::new( "packed", @@ -3901,6 +3878,6 @@ mod test { ) .with_metadata(packed_meta); let field4 = ArrowField::new("test", DataType::Struct(vec![conflict_field].into()), false); - assert!(check_field_conflict(&field1, &field4, &LanceFileVersion::V2_2).is_err()); + assert!(check_field_conflict(&field1, &field4, &ConcreteFileVersion::V2_2).is_err()); } } diff --git a/rust/lance/src/dataset/schema_evolution/optimize.rs b/rust/lance/src/dataset/schema_evolution/optimize.rs index 19bb6a6d46c..5be44626821 100644 --- a/rust/lance/src/dataset/schema_evolution/optimize.rs +++ b/rust/lance/src/dataset/schema_evolution/optimize.rs @@ -14,7 +14,7 @@ use crate::Result; use super::NewColumnTransform; /// Optimizes a `NewColumnTransform` into -pub(super) trait NewColumnTransformOptimizer: Send + Sync { +pub trait NewColumnTransformOptimizer: Send + Sync { /// Optimize the passed `NewColumnTransform` to a more efficient form. fn optimize( &self, @@ -24,16 +24,16 @@ pub(super) trait NewColumnTransformOptimizer: Send + Sync { } /// A `NewColumnTransformOptimizer` that chains multiple `NewColumnTransformOptimizer`s together. -pub(super) struct ChainedNewColumnTransformOptimizer { +pub struct ChainedNewColumnTransformOptimizer { optimizers: Vec>, } impl ChainedNewColumnTransformOptimizer { - pub(super) fn new(optimizers: Vec>) -> Self { + pub fn new(optimizers: Vec>) -> Self { Self { optimizers } } - pub(super) fn add_optimizer(&mut self, optimizer: Box) { + pub fn add_optimizer(&mut self, optimizer: Box) { self.optimizers.push(optimizer); } } @@ -59,10 +59,10 @@ impl NewColumnTransformOptimizer for ChainedNewColumnTransformOptimizer { /// would be optimized to /// `NewColumnTransform::AllNulls(Schema::new(vec![Field::new("new_col", DataType::Int)]))`. /// -pub(super) struct SqlToAllNullsOptimizer; +pub struct SqlToAllNullsOptimizer; impl SqlToAllNullsOptimizer { - pub(super) fn new() -> Self { + pub fn new() -> Self { Self } diff --git a/rust/lance/src/dataset/statistics.rs b/rust/lance/src/dataset/statistics.rs index 627ccfc1081..c734ba9b321 100644 --- a/rust/lance/src/dataset/statistics.rs +++ b/rust/lance/src/dataset/statistics.rs @@ -13,7 +13,7 @@ use lance_index::scalar::zonemap::ZoneMapIndex; use lance_io::scheduler::{ScanScheduler, SchedulerConfig}; use roaring::RoaringBitmap; -use super::{Dataset, fragment::FileFragment}; +use super::{Dataset, fragment::FileFragment, versions}; use crate::index::{DatasetIndexExt, DatasetIndexInternalExt}; /// Statistics about a single field in the dataset @@ -52,32 +52,12 @@ impl DatasetStatisticsExt for Dataset { }, ) })); - if !self.is_legacy_storage() { - let scan_scheduler = ScanScheduler::new( - self.object_store.clone(), - SchedulerConfig::max_bandwidth(self.object_store.as_ref()), - ); - let schema = self.schema().clone(); - let dataset = self.clone(); - let fragments = self.fragments().as_ref().clone(); - futures::stream::iter(fragments) - .map(|fragment| { - let file_fragment = FileFragment::new(dataset.clone(), fragment); - let schema = schema.clone(); - let scan_scheduler = scan_scheduler.clone(); - async move { file_fragment.storage_stats(&schema, scan_scheduler).await } - }) - .buffer_unordered(self.object_store.io_parallelism()) - .try_for_each(|fragment_stats| { - for (field_id, bytes) in fragment_stats { - if let Some(stats) = field_stats.get_mut(&field_id) { - stats.bytes_on_disk += bytes; - } - } - futures::future::ready(Ok(())) - }) - .await?; - } + versions::collect_data_stats( + self.manifest().data_storage_format.lance_file_format(), + self, + &mut field_stats, + ) + .await?; let field_stats = field_ids .into_iter() .map(|id| field_stats.remove(&(id as u32)).unwrap()) @@ -88,6 +68,35 @@ impl DatasetStatisticsExt for Dataset { } } +pub(super) async fn collect_current_data_stats( + dataset: &Arc, + field_stats: &mut HashMap, +) -> Result<()> { + let scan_scheduler = ScanScheduler::new( + dataset.object_store.clone(), + SchedulerConfig::max_bandwidth(dataset.object_store.as_ref()), + ); + let schema = dataset.schema().clone(); + let fragments = dataset.fragments().as_ref().clone(); + futures::stream::iter(fragments) + .map(|fragment| { + let file_fragment = FileFragment::new(dataset.clone(), fragment); + let schema = schema.clone(); + let scan_scheduler = scan_scheduler.clone(); + async move { file_fragment.storage_stats(&schema, scan_scheduler).await } + }) + .buffer_unordered(dataset.object_store.io_parallelism()) + .try_for_each(|fragment_stats| { + for (field_id, bytes) in fragment_stats { + if let Some(stats) = field_stats.get_mut(&field_id) { + stats.bytes_on_disk += bytes; + } + } + futures::future::ready(Ok(())) + }) + .await +} + /// A read-only handle for cheap, index-derived statistics about a [`Dataset`]. /// /// Obtained via [`Dataset::statistics`]. Groups statistics accessors behind one diff --git a/rust/lance/src/dataset/take.rs b/rust/lance/src/dataset/take.rs index 5d42e80473b..c92a87926aa 100644 --- a/rust/lance/src/dataset/take.rs +++ b/rust/lance/src/dataset/take.rs @@ -228,7 +228,7 @@ async fn do_take_rows( .with_row_created_at_version(with_row_created_at_version_in_projection) .with_row_last_updated_at_version(with_row_last_updated_at_version_in_projection); let reader = fragment.open(&physical_schema, read_config).await?; - reader.legacy_read_range_as_batch(range).await + reader.read_range_as_batch(range).await } else if row_addr_stats.sorted { // Don't need to re-arrange data, just concatenate let mut batches: Vec<_> = Vec::new(); diff --git a/rust/lance/src/dataset/tests/dataset_index.rs b/rust/lance/src/dataset/tests/dataset_index.rs index b12a7198e5a..b63a79566b3 100644 --- a/rust/lance/src/dataset/tests/dataset_index.rs +++ b/rust/lance/src/dataset/tests/dataset_index.rs @@ -33,7 +33,7 @@ use lance_core::cache::LanceCache; use lance_core::utils::tempfile::TempStrDir; use lance_datagen::{BatchCount, Dimension, RowCount, array, gen_batch}; use lance_file::reader::{FileReader, FileReaderOptions}; -use lance_file::version::LanceFileVersion; +use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; use lance_index::optimize::OptimizeOptions; use lance_index::scalar::FullTextSearchQuery; use lance_index::scalar::inverted::{ @@ -3936,7 +3936,7 @@ async fn test_index_inherits_dataset_file_version() { // Verify that the index file uses the same version as the dataset assert_eq!( index_reader.metadata().version(), - dataset_version, + dataset_version.resolve(), "Index file should use the same format version as the dataset" ); @@ -3965,7 +3965,7 @@ async fn test_index_inherits_dataset_file_version() { assert_eq!( aux_reader.metadata().version(), - dataset_version, + dataset_version.resolve(), "Auxiliary index file should use the same format version as the dataset" ); } @@ -4044,7 +4044,7 @@ async fn test_legacy_dataset_uses_v2_0_for_indexes() { // Verify that the index file uses V2_0 (not legacy) assert_eq!( index_reader.metadata().version(), - LanceFileVersion::V2_0, + ConcreteFileVersion::V2_0, "Index files should never use legacy format, even for legacy datasets" ); } diff --git a/rust/lance/src/dataset/tests/dataset_io.rs b/rust/lance/src/dataset/tests/dataset_io.rs index 062f0452629..f9c07cf788a 100644 --- a/rust/lance/src/dataset/tests/dataset_io.rs +++ b/rust/lance/src/dataset/tests/dataset_io.rs @@ -35,8 +35,8 @@ use lance_arrow::{ARROW_EXT_META_KEY, ARROW_EXT_NAME_KEY}; use lance_core::utils::tempfile::{TempStdDir, TempStrDir}; use lance_datagen::{BatchCount, RowCount, array, gen_batch}; use lance_file::{ - version::LanceFileVersion, - writer::{FileWriter, FileWriterOptions}, + version::{ConcreteFileVersion, LanceFileVersion}, + writer::FileWriterOptions, }; use lance_io::assert_io_eq; use lance_table::feature_flags; @@ -449,13 +449,10 @@ async fn test_create_data_file_rejects_nested_schema_mismatch() { .create(&dataset.data_dir().join(file_name)) .await .unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_2::create_writer( object_writer, crate::datatypes::Schema::try_from(schema.as_ref()).unwrap(), - FileWriterOptions { - format_version: Some(LanceFileVersion::V2_2), - ..Default::default() - }, + FileWriterOptions::default(), ) .unwrap(); writer.write_batch(&batch).await.unwrap(); @@ -985,15 +982,18 @@ async fn test_write_params( assert_eq!(dataset.count_fragments(), 10); for fragment in &fragments { assert_eq!(fragment.count_rows(None).await.unwrap(), 100); - let reader = fragment - .open(dataset.schema(), FragReadConfig::default()) - .await - .unwrap(); // No group / batch concept in v2 if data_storage_version == LanceFileVersion::Legacy { - assert_eq!(reader.legacy_num_batches(), 10); - for i in 0..reader.legacy_num_batches() as u32 { - assert_eq!(reader.legacy_num_rows_in_batch(i).unwrap(), 10); + let reader = crate::dataset::versions::open_v1_fragment_reader( + fragment, + dataset.schema(), + &FragReadConfig::default(), + ) + .await + .unwrap(); + assert_eq!(reader.num_batches(), 10); + for i in 0..reader.num_batches() as u32 { + assert_eq!(reader.num_rows_in_batch(i).unwrap(), 10); } } } @@ -1002,7 +1002,11 @@ async fn test_write_params( #[rstest] #[tokio::test] async fn test_write_manifest( - #[values(LanceFileVersion::Legacy, LanceFileVersion::Stable)] + #[values( + LanceFileVersion::Legacy, + LanceFileVersion::Stable, + LanceFileVersion::Next + )] data_storage_version: LanceFileVersion, ) { use lance_table::feature_flags::FLAG_UNKNOWN; @@ -1051,8 +1055,12 @@ async fn test_write_manifest( assert_eq!( manifest.data_storage_format, - DataStorageFormat::new(data_storage_version) + DataStorageFormat::new(data_storage_version.resolve()) ); + assert!(!matches!( + manifest.data_storage_format.version.to_manifest_string(), + "stable" | "next" + )); assert_eq!(manifest.reader_feature_flags, 0); // Create one with deletions @@ -1171,8 +1179,8 @@ async fn test_rle_v2_v23_write_and_append() { .await .unwrap(); assert_eq!( - manifest.data_storage_format.lance_file_version().unwrap(), - LanceFileVersion::V2_3 + manifest.data_storage_format.lance_file_format(), + ConcreteFileVersion::V2_3 ); let append_batch = RecordBatch::try_new( @@ -1194,12 +1202,8 @@ async fn test_rle_v2_v23_write_and_append() { .unwrap(); assert_eq!( - dataset - .manifest - .data_storage_format - .lance_file_version() - .unwrap(), - LanceFileVersion::V2_3 + dataset.manifest.data_storage_format.lance_file_format(), + ConcreteFileVersion::V2_3 ); let actual = dataset.scan().try_into_batch().await.unwrap(); @@ -1241,12 +1245,8 @@ async fn test_rle_v2_uncommitted_create_commits_v23_storage() { .await .unwrap(); assert_eq!( - dataset - .manifest - .data_storage_format - .lance_file_version() - .unwrap(), - LanceFileVersion::V2_3 + dataset.manifest.data_storage_format.lance_file_format(), + ConcreteFileVersion::V2_3 ); } @@ -1281,12 +1281,8 @@ async fn test_rle_v2_shallow_clone_preserves_v23_storage() { .await .unwrap(); assert_eq!( - clone - .manifest - .data_storage_format - .lance_file_version() - .unwrap(), - LanceFileVersion::V2_3 + clone.manifest.data_storage_format.lance_file_format(), + ConcreteFileVersion::V2_3 ); } @@ -2415,12 +2411,8 @@ async fn test_overwrite_mixed_version() { .unwrap(); assert_eq!( - dataset - .manifest - .data_storage_format - .lance_file_version() - .unwrap(), - LanceFileVersion::Legacy + dataset.manifest.data_storage_format.lance_file_format(), + ConcreteFileVersion::V1 ); let reader = RecordBatchIterator::new(vec![data].into_iter().map(Ok), schema); @@ -2436,12 +2428,8 @@ async fn test_overwrite_mixed_version() { .unwrap(); assert_eq!( - dataset - .manifest - .data_storage_format - .lance_file_version() - .unwrap(), - LanceFileVersion::Legacy + dataset.manifest.data_storage_format.lance_file_format(), + ConcreteFileVersion::V1 ); } diff --git a/rust/lance/src/dataset/tests/dataset_merge_update.rs b/rust/lance/src/dataset/tests/dataset_merge_update.rs index 4b81f62dc6c..23a43198965 100644 --- a/rust/lance/src/dataset/tests/dataset_merge_update.rs +++ b/rust/lance/src/dataset/tests/dataset_merge_update.rs @@ -36,7 +36,6 @@ use lance_core::utils::tempfile::{TempDir, TempStrDir}; use lance_datafusion::utils::reader_to_stream; use lance_datagen::{BatchCount, RowCount, array, gen_batch}; use lance_file::version::LanceFileVersion; -use lance_file::writer::FileWriter; use lance_io::utils::CachedFileSize; use lance_table::format::{BasePath, DataFile, Fragment}; @@ -745,7 +744,7 @@ async fn test_datafile_replacement() { .create(&Path::from("data/test.lance")) .await .unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_1::create_writer( object_writer, schema.as_ref().try_into().unwrap(), Default::default(), @@ -852,7 +851,7 @@ async fn test_datafile_partial_replacement() { .create(&Path::from("data/test.lance")) .await .unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_1::create_writer( object_writer, partial_schema.as_ref().try_into().unwrap(), Default::default(), @@ -864,7 +863,7 @@ async fn test_datafile_partial_replacement() { writer.write_batch(&batch).await.unwrap(); writer.finish().await.unwrap(); - let (major, minor) = lance_file::version::LanceFileVersion::Stable.to_numbers(); + let (major, minor) = LanceFileVersion::Stable.resolve().to_data_file_numbers(); // find the datafile we want to replace let new_data_file = DataFile { @@ -2199,7 +2198,7 @@ async fn test_data_replacement_invalidates_index_bitmap() { .create(&Path::from("data/replacement.lance")) .await .unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_1::create_writer( object_writer, single_col_schema.as_ref().try_into().unwrap(), Default::default(), @@ -2312,7 +2311,7 @@ fn build_overlay_frag(prev: &Fragment, field_id: i32, new_file: &str) -> Fragmen new_file, vec![field_id], vec![0], - &LanceFileVersion::default(), + LanceFileVersion::default().resolve(), None, ); overlay @@ -2377,7 +2376,7 @@ async fn test_merge_rewriting_indexed_column_keeps_index_consistent() { .unwrap(); let new_a_path = dataset.data_dir().join("merge_new_a.lance"); let object_writer = dataset.object_store.create(&new_a_path).await.unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_1::create_writer( object_writer, a_only.as_ref().try_into().unwrap(), Default::default(), @@ -2425,7 +2424,6 @@ async fn test_merge_rewriting_indexed_column_keeps_index_consistent() { /// stale index entries are blocked at query time. #[tokio::test] async fn test_data_replacement_populates_invalidated_bitmap() { - use lance_file::writer::FileWriter; use object_store::path::Path; let schema = Arc::new(ArrowSchema::new(vec![ @@ -2481,7 +2479,7 @@ async fn test_data_replacement_populates_invalidated_bitmap() { .create(&Path::from("data/replacement_inv.lance")) .await .unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_1::create_writer( object_writer, value_schema.as_ref().try_into().unwrap(), Default::default(), @@ -2606,7 +2604,7 @@ async fn test_fts_stale_entries_after_data_replacement() { .create(&replacement_path) .await .unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_1::create_writer( object_writer, schema.as_ref().try_into().unwrap(), Default::default(), @@ -2783,7 +2781,7 @@ async fn test_vector_index_after_data_replacement() { .create(&replacement_path) .await .unwrap(); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::v2_1::create_writer( object_writer, schema.as_ref().try_into().unwrap(), Default::default(), diff --git a/rust/lance/src/dataset/tests/dataset_overlay_index_masking.rs b/rust/lance/src/dataset/tests/dataset_overlay_index_masking.rs index cfabdc13266..fb04b14afb3 100644 --- a/rust/lance/src/dataset/tests/dataset_overlay_index_masking.rs +++ b/rust/lance/src/dataset/tests/dataset_overlay_index_masking.rs @@ -24,7 +24,7 @@ use lance_table::format::overlay::{DataOverlayFile, OverlayCoverage}; use roaring::RoaringBitmap; use rstest::rstest; -use lance_file::writer::{FileWriter, FileWriterOptions}; +use lance_file::writer::FileWriterOptions; use crate::Dataset; use crate::dataset::transaction::{DataOverlayGroup, Operation}; @@ -98,15 +98,20 @@ async fn commit_overlay( // For memory:// stores base is empty so the result is the same as before. let path = dataset.base.clone().join("data").join(filename.as_str()); let obj_writer = dataset.object_store.create(&path).await.unwrap(); - let mut writer = - FileWriter::try_new(obj_writer, overlay_schema, FileWriterOptions::default()).unwrap(); - let (major, minor) = writer.version().to_numbers(); + let file_version = dataset.manifest.data_storage_format.lance_file_format(); + let mut writer = lance_file::versions::create_writer( + file_version, + obj_writer, + overlay_schema, + FileWriterOptions::default(), + ) + .unwrap(); for (i, array) in columns.into_iter().enumerate() { writer.write_column(i, array).await.unwrap(); } let summary = writer.finish().await.unwrap(); - let mut data_file = DataFile::new_unstarted(filename, major, minor); + let mut data_file = DataFile::new_unstarted(filename, file_version); data_file.fields = writer .field_id_to_column_indices() .iter() diff --git a/rust/lance/src/dataset/tests/dataset_schema_evolution.rs b/rust/lance/src/dataset/tests/dataset_schema_evolution.rs index d311d62f555..c00f3c4cdaa 100644 --- a/rust/lance/src/dataset/tests/dataset_schema_evolution.rs +++ b/rust/lance/src/dataset/tests/dataset_schema_evolution.rs @@ -10,7 +10,7 @@ use arrow_array::{ use arrow_schema::{ DataType, Field as ArrowField, Field, Fields as ArrowFields, Fields, Schema as ArrowSchema, }; -use lance_encoding::version::LanceFileVersion; +use lance_file::version::LanceFileVersion; use rstest::rstest; use std::collections::HashMap; use std::sync::Arc; diff --git a/rust/lance/src/dataset/transaction.rs b/rust/lance/src/dataset/transaction.rs index 49a5c154d32..0efb7254def 100644 --- a/rust/lance/src/dataset/transaction.rs +++ b/rust/lance/src/dataset/transaction.rs @@ -23,7 +23,7 @@ use lance_core::datatypes::{ }; use lance_core::deepsize::DeepSizeOf; use lance_core::{Error, Result, datatypes::Schema}; -use lance_file::{datatypes::Fields, version::LanceFileVersion}; +use lance_file::{datatypes::Fields, version::ConcreteFileVersion}; use lance_index::mem_wal::MergedGeneration; use lance_index::{frag_reuse::FRAG_REUSE_INDEX_NAME, is_system_index}; use lance_io::object_store::ObjectStore; @@ -1735,7 +1735,7 @@ impl Transaction { fn data_storage_format_from_files( fragments: &[Fragment], - user_requested: Option, + user_requested: Option, ) -> Result { if let Some(file_version) = Fragment::try_infer_version(fragments)? { // Ensure user-requested matches data files @@ -2297,11 +2297,7 @@ impl Transaction { // just add it to the final fragments. Push the DataFile as // given so every field (including base_id) is preserved. if columns_covered.is_disjoint(&new_file.fields.iter().collect()) { - LanceFileVersion::try_from_major_minor( - new_file.file_major_version, - new_file.file_minor_version, - ) - .expect("Expected valid file version"); + new_file.file_version()?; new_frag.files.push(new_file.clone()); } @@ -2428,9 +2424,9 @@ impl Transaction { } let user_requested_version = match (&config.storage_format, config.use_legacy_format) { - (Some(storage_format), _) => Some(storage_format.lance_file_version()?), - (None, Some(true)) => Some(LanceFileVersion::Legacy), - (None, Some(false)) => Some(LanceFileVersion::V2_0), + (Some(storage_format), _) => Some(storage_format.lance_file_format()), + (None, Some(true)) => Some(ConcreteFileVersion::V1), + (None, Some(false)) => Some(ConcreteFileVersion::V2_0), (None, None) => None, }; @@ -3953,11 +3949,20 @@ fn schema_fragments_valid( schema: &Schema, fragments: &[Fragment], ) -> Result<()> { - if let Some(manifest) = manifest - && manifest.data_storage_format.lance_file_version()? == LanceFileVersion::Legacy - { - return schema_fragments_legacy_valid(schema, fragments); + if let Some(manifest) = manifest { + return super::versions::validate_fragment_schema( + manifest.data_storage_format.lance_file_format(), + schema, + fragments, + ); } + schema_fragments_modern_valid(schema, fragments) +} + +pub(crate) fn schema_fragments_modern_valid( + _schema: &Schema, + fragments: &[Fragment], +) -> Result<()> { // validate that each data file at least contains one field. for fragment in fragments { for data_file in &fragment.files { @@ -3975,7 +3980,7 @@ fn schema_fragments_valid( /// Check that each fragment contains all fields in the schema. /// It is not required that the schema contains all fields in the fragment. /// There may be masked fields. -fn schema_fragments_legacy_valid(schema: &Schema, fragments: &[Fragment]) -> Result<()> { +pub(crate) fn schema_fragments_legacy_valid(schema: &Schema, fragments: &[Fragment]) -> Result<()> { // TODO: add additional validation. Consider consolidating with various // validate() methods in the codebase. for fragment in fragments { @@ -4090,7 +4095,7 @@ mod tests { use lance_core::utils::address::RowAddress; use lance_core::utils::tempfile::TempStrDir; use lance_core::{ROW_ADDR, ROW_CREATED_AT_VERSION, ROW_LAST_UPDATED_AT_VERSION}; - use lance_file::version::LanceFileVersion; + use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; use lance_io::utils::CachedFileSize; use lance_table::format::overlay::OverlayCoverage; use lance_table::format::{ @@ -4111,7 +4116,7 @@ mod tests { Manifest::new( LanceSchema::try_from(&schema).unwrap(), Arc::new(vec![Fragment::new(0)]), - DataStorageFormat::new(LanceFileVersion::V2_0), + DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ) } @@ -4197,7 +4202,7 @@ mod tests { let manifest = Manifest::new( LanceSchema::try_from(&schema).unwrap(), Arc::new(original_fragments), - DataStorageFormat::new(LanceFileVersion::V2_0), + DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); @@ -5092,8 +5097,14 @@ mod tests { let row_ids = RowIdSequence::from([10u64, 11, 12, 13, 14].as_slice()); let row_id_meta = Some(RowIdMeta::Inline(write_row_ids(&row_ids))); - let (major, minor) = lance_file::version::LanceFileVersion::Stable.to_numbers(); - let data_file = DataFile::new("data.lance", vec![0], vec![0], major, minor, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0], + vec![0], + LanceFileVersion::Stable.resolve(), + None, + None, + ); let fragment = Fragment { id: 1, @@ -5350,7 +5361,7 @@ mod tests { let legacy_manifest = Manifest::new( schema.clone(), Arc::new(vec![Fragment::new(0)]), - DataStorageFormat::new(LanceFileVersion::Legacy), + DataStorageFormat::new(ConcreteFileVersion::V1), HashMap::new(), ); @@ -5362,8 +5373,7 @@ mod tests { "data.lance", vec![0, 1, 3, 4], // no field 2 (struct parent) vec![0, 1, 2, 3], - lance_file::format::MAJOR_VERSION as u32, - lance_file::format::MINOR_VERSION as u32, + ConcreteFileVersion::V1, None, None, )], @@ -5394,7 +5404,7 @@ mod tests { let mut manifest = Manifest::new( LanceSchema::try_from(&schema).unwrap(), Arc::new(fragments), - DataStorageFormat::new(LanceFileVersion::V2_0), + DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); manifest.reader_feature_flags = FLAG_STABLE_ROW_IDS; @@ -5448,8 +5458,16 @@ mod tests { use lance_file::version::LanceFileVersion; use lance_table::feature_flags::FLAG_STABLE_ROW_IDS; - let (major, minor) = LanceFileVersion::Stable.to_numbers(); - let mk_file = |path: &str| DataFile::new(path, vec![0], vec![0], major, minor, None, None); + let mk_file = |path: &str| { + DataFile::new( + path, + vec![0], + vec![0], + LanceFileVersion::Stable.resolve(), + None, + None, + ) + }; let arrow_schema = ArrowSchema::new(vec![ArrowField::new("id", DataType::Int32, false)]); let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); @@ -5471,7 +5489,7 @@ mod tests { let mut manifest = Manifest::new( lance_schema.clone(), Arc::new(vec![prev_fragment.clone()]), - DataStorageFormat::new(LanceFileVersion::V2_0), + DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); manifest.reader_feature_flags |= FLAG_STABLE_ROW_IDS; @@ -5518,8 +5536,14 @@ mod tests { use lance_table::feature_flags::FLAG_STABLE_ROW_IDS; use lance_table::rowids::version::{RowDatasetVersionMeta, RowDatasetVersionSequence}; - let (major, minor) = LanceFileVersion::Stable.to_numbers(); - let data_file = DataFile::new("same.lance", vec![0], vec![0], major, minor, None, None); + let data_file = DataFile::new( + "same.lance", + vec![0], + vec![0], + LanceFileVersion::Stable.resolve(), + None, + None, + ); let arrow_schema = ArrowSchema::new(vec![ArrowField::new("id", DataType::Int32, false)]); let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); @@ -5544,7 +5568,7 @@ mod tests { let mut manifest = Manifest::new( lance_schema.clone(), Arc::new(vec![prev_fragment]), - DataStorageFormat::new(LanceFileVersion::V2_0), + DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); manifest.reader_feature_flags |= FLAG_STABLE_ROW_IDS; @@ -5594,8 +5618,16 @@ mod tests { use lance_file::version::LanceFileVersion; use lance_table::feature_flags::FLAG_STABLE_ROW_IDS; - let (major, minor) = LanceFileVersion::Stable.to_numbers(); - let mk_file = |path: &str| DataFile::new(path, vec![0], vec![0], major, minor, None, None); + let mk_file = |path: &str| { + DataFile::new( + path, + vec![0], + vec![0], + LanceFileVersion::Stable.resolve(), + None, + None, + ) + }; let arrow_schema = ArrowSchema::new(vec![ArrowField::new("id", DataType::Int32, false)]); let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); @@ -5614,7 +5646,7 @@ mod tests { let manifest = Manifest::new( lance_schema.clone(), Arc::new(vec![prev_fragment.clone()]), - DataStorageFormat::new(LanceFileVersion::V2_0), + DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); assert_eq!( @@ -5657,8 +5689,16 @@ mod tests { use lance_file::version::LanceFileVersion; use lance_table::feature_flags::FLAG_STABLE_ROW_IDS; - let (major, minor) = LanceFileVersion::Stable.to_numbers(); - let mk_file = |path: &str| DataFile::new(path, vec![0], vec![0], major, minor, None, None); + let mk_file = |path: &str| { + DataFile::new( + path, + vec![0], + vec![0], + LanceFileVersion::Stable.resolve(), + None, + None, + ) + }; let arrow_schema = ArrowSchema::new(vec![ArrowField::new("id", DataType::Int32, false)]); let lance_schema = LanceSchema::try_from(&arrow_schema).unwrap(); @@ -5679,7 +5719,7 @@ mod tests { let mut manifest = Manifest::new( lance_schema.clone(), Arc::new(vec![existing_fragment.clone()]), - DataStorageFormat::new(LanceFileVersion::V2_0), + DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); manifest.reader_feature_flags |= FLAG_STABLE_ROW_IDS; @@ -6531,7 +6571,7 @@ mod tests { let mut manifest = Manifest::new( LanceSchema::try_from(&schema).unwrap(), Arc::new(vec![frag0, frag1, frag2]), - lance_table::format::DataStorageFormat::new(LanceFileVersion::V2_0), + lance_table::format::DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); // The pre-existing overlays were committed at v3, so the current @@ -6617,7 +6657,7 @@ mod tests { let manifest = Manifest::new( LanceSchema::try_from(&schema).unwrap(), Arc::new(vec![fragment]), - lance_table::format::DataStorageFormat::new(LanceFileVersion::V2_0), + lance_table::format::DataStorageFormat::new(ConcreteFileVersion::V2_0), HashMap::new(), ); diff --git a/rust/lance/src/dataset/updater.rs b/rust/lance/src/dataset/updater.rs index 752a479e5b0..8b40975edec 100644 --- a/rust/lance/src/dataset/updater.rs +++ b/rust/lance/src/dataset/updater.rs @@ -12,7 +12,8 @@ use lance_table::utils::stream::ReadBatchFutStream; use super::Dataset; use super::fragment::FragmentReader; use super::scanner::get_default_batch_size; -use super::write::{GenericWriter, cleanup_data_fragments, open_update_writer}; +use super::versions; +use super::write::{GenericWriter, cleanup_data_fragments}; use crate::dataset::FileFragment; use crate::dataset::utils::SchemaAdapter; @@ -72,7 +73,13 @@ impl Updater { (None, None) }; - let legacy_batch_size = reader.legacy_num_rows_in_batch(0); + let storage_version = fragment + .dataset() + .manifest() + .data_storage_format + .lance_file_format(); + let legacy_batch_size = + versions::row_group_size_for_rewrite(storage_version, &fragment).await?; let batch_size = match (&legacy_batch_size, batch_size) { // If this is a v1 dataset we must use the row group size of the file @@ -144,9 +151,9 @@ impl Updater { .dataset() .manifest() .data_storage_format - .lance_file_version()?; + .lance_file_format(); - open_update_writer(self.dataset(), &schema, data_storage_version).await + versions::open_update_writer(data_storage_version, self.dataset(), &schema).await } /// Update one batch. @@ -229,12 +236,22 @@ impl Updater { } let mut fragment = Fragment::new(self.fragment.id() as u64); + let storage_version = self + .dataset() + .manifest() + .data_storage_format + .lance_file_format(); // cleanup_data_fragments only needs path/base_id to remove the unfinished // data file and any blob sidecars. Build a minimal synthetic fragment so // we can reuse the shared cleanup path without fabricating full metadata. - fragment - .files - .push(DataFile::new(path, vec![], vec![], 0, 0, None, base_id)); + fragment.files.push(DataFile::new( + path, + vec![], + vec![], + storage_version, + None, + base_id, + )); cleanup_data_fragments( &self.dataset().object_store, &self.dataset().base, diff --git a/rust/lance/src/dataset/versions/mod.rs b/rust/lance/src/dataset/versions/mod.rs new file mode 100644 index 00000000000..5e2d6c2257b --- /dev/null +++ b/rust/lance/src/dataset/versions/mod.rs @@ -0,0 +1,875 @@ +//! Dataset policies that differ across exact Lance file versions. +//! +//! File grammar belongs to `lance_file::versions`. This module contains only +//! operation-level dataset choices whose behavior actually differs by version. + +use std::{collections::HashMap, ops::Range, sync::Arc}; + +use arrow_schema::{DataType, Field as ArrowField}; +use datafusion::execution::SendableRecordBatchStream; +use datafusion::physical_plan::ExecutionPlan; +use datafusion::physical_plan::metrics::ExecutionPlanMetricsSet; +use futures::{StreamExt, TryStreamExt}; +use lance_arrow::DataTypeExt; +use lance_core::{ + Error, Result, + datatypes::{Field, Projection, Schema, SchemaCompareOptions}, +}; +use lance_datafusion::chunker::{break_stream, chunk_stream}; +use lance_file::{ + version::ConcreteFileVersion, + versions as file_versions, + writer::{FileWriter, FileWriterOptions}, +}; +use lance_index::scalar::seed::IndexSeedWriter; +use lance_io::object_store::ObjectStore; +use lance_io::traits::Writer as ObjectWriter; +use lance_table::format::{DataFile, DataStorageFormat, Fragment, Manifest}; +use object_store::path::Path; + +use super::Dataset; +use super::fragment::{ + FileFragment, FragReadConfig, GenericFileReader, MetadataMode, V1FragmentReader, + write::FragmentCreateBuilder, +}; +use super::optimize::CompactionOptions; +use super::scanner::{PlannedFilteredScan, Scanner}; +use super::schema_evolution::optimize::{ + ChainedNewColumnTransformOptimizer, SqlToAllNullsOptimizer, +}; +use super::statistics::FieldStatistics; +use super::utils::SchemaAdapter; +use super::write::{self, GenericWriter, TargetBaseInfo, WriteParams, WriterOptions}; +use crate::io::exec::filtered_read::{FilteredReadExec, FilteredReadOptions}; +use crate::io::exec::{ + AddRowAddrExec, FilterPlan as ExprFilterPlan, LanceScanConfig, LanceStream, TakeExec, +}; + +#[allow(clippy::too_many_arguments)] +pub fn create_scan_stream( + version: ConcreteFileVersion, + dataset: Arc, + fragments: Arc>, + offsets: Option>, + projection: Arc, + config: LanceScanConfig, + metrics: &ExecutionPlanMetricsSet, + partition: usize, +) -> datafusion::error::Result { + match version { + ConcreteFileVersion::V1 => LanceStream::try_new_v1( + dataset, fragments, offsets, projection, config, metrics, partition, + ), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => LanceStream::try_new_v2( + dataset, fragments, offsets, projection, config, metrics, partition, + ), + } +} + +pub fn schema_compare_options(version: ConcreteFileVersion) -> SchemaCompareOptions { + match version { + ConcreteFileVersion::V1 => SchemaCompareOptions { + compare_dictionary: true, + ..Default::default() + }, + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => SchemaCompareOptions::default(), + } +} + +async fn create_seed_writers( + version: ConcreteFileVersion, + dataset: Option<&Dataset>, + params: &WriteParams, +) -> Result>> { + match version { + ConcreteFileVersion::V1 => Ok(Vec::new()), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => write::create_seed_writers_current(dataset, params).await, + } +} + +fn create_current_file_writer( + version: ConcreteFileVersion, + object_writer: Box, + schema: Schema, + filename: String, + base_id: Option, +) -> Result<(FileWriter, DataFile)> { + let writer = + file_versions::create_writer(version, object_writer, schema, FileWriterOptions::default())?; + let mut data_file = DataFile::new_unstarted(filename, version); + data_file.base_id = base_id; + Ok((writer, data_file)) +} + +#[allow(clippy::too_many_arguments)] +pub async fn write_fragments( + version: ConcreteFileVersion, + dataset: Option<&Dataset>, + object_store: Arc, + base_dir: &Path, + normalized_schema: Schema, + data: SendableRecordBatchStream, + params: WriteParams, + target_bases_info: Option>, +) -> Result<(Vec, Schema)> { + let version_name = format!("{version:?}"); + let schema = write::prepare_write_schema( + dataset, + normalized_schema, + ¶ms, + schema_compare_options(version), + )?; + match version { + ConcreteFileVersion::V1 | ConcreteFileVersion::V2_0 | ConcreteFileVersion::V2_1 => { + write::validate_legacy_blob_write_schema(&schema, &version_name)?; + } + ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => { + write::validate_blob_v2_write_schema(&schema)?; + } + } + let seed_writers = create_seed_writers(version, dataset, ¶ms).await?; + let fragments = write_fragments_direct( + version, + dataset, + object_store, + base_dir, + &schema, + data, + params, + target_bases_info, + seed_writers, + ) + .await?; + Ok((fragments, schema)) +} + +#[allow(clippy::too_many_arguments)] +pub async fn write_fragments_direct( + version: ConcreteFileVersion, + dataset: Option<&Dataset>, + object_store: Arc, + base_dir: &Path, + schema: &Schema, + data: SendableRecordBatchStream, + params: WriteParams, + target_bases_info: Option>, + seed_writers: Vec>, +) -> Result> { + let adapter = SchemaAdapter::new(data.schema()); + let data = adapter.to_physical_stream(data); + let buffered_reader = match version { + ConcreteFileVersion::V1 => chunk_stream(data, params.max_rows_per_group), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => break_stream(data, params.max_rows_per_file) + .map_ok(|batch| vec![batch]) + .boxed(), + }; + let external_base_resolver = match version { + ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => { + write::blob_v2_external_base_resolver(dataset, ¶ms, schema).await? + } + ConcreteFileVersion::V1 | ConcreteFileVersion::V2_0 | ConcreteFileVersion::V2_1 => None, + }; + write::do_write_fragments_impl( + dataset, + object_store, + base_dir, + schema, + buffered_reader, + params, + move |object_store, schema, base_dir, options| async move { + open_writer(version, &object_store, &schema, &base_dir, options).await + }, + external_base_resolver, + target_bases_info, + seed_writers, + ) + .await +} + +fn binary_copy_files_match(fragments: &[Fragment], expected: ConcreteFileVersion) -> Result { + for fragment in fragments { + for data_file in &fragment.files { + if data_file.file_version()? != expected { + return Ok(false); + } + } + } + Ok(true) +} + +pub async fn can_use_binary_copy( + version: ConcreteFileVersion, + dataset: &Dataset, + options: &CompactionOptions, + fragments: &[Fragment], +) -> Result { + match version { + ConcreteFileVersion::V1 => Ok(false), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + if !binary_copy_files_match(fragments, version)? { + return Ok(false); + } + super::optimize::can_use_binary_copy_current(dataset, options, fragments).await + } + } +} + +pub async fn rewrite_files_binary_copy( + version: ConcreteFileVersion, + dataset: &Dataset, + fragments: &[Fragment], + params: &WriteParams, + read_batch_bytes: Option, +) -> Result> { + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported( + "binary-copy compaction is not supported for Lance file version 1".to_string(), + )), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + super::optimize::binary_copy::rewrite_files_binary_copy( + version, + dataset, + fragments, + params, + read_batch_bytes, + ) + .await + } + } +} + +pub fn check_manifest_storage_version(manifest: &mut Manifest) -> Result<()> { + let version = manifest.data_storage_format.lance_file_format(); + match version { + ConcreteFileVersion::V1 => repair_legacy_manifest_storage(manifest), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => validate_exact_manifest_storage(manifest, version), + } +} + +pub fn validate_column_indices(manifest: &Manifest) -> Result<()> { + match manifest.data_storage_format.lance_file_format() { + ConcreteFileVersion::V1 | ConcreteFileVersion::V2_0 => Ok(()), + ConcreteFileVersion::V2_1 | ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => { + validate_leaf_column_indices(manifest) + } + } +} + +fn validate_leaf_column_indices(manifest: &Manifest) -> Result<()> { + for fragment in manifest.fragments.iter() { + for data_file in &fragment.files { + if data_file.column_indices.is_empty() { + continue; + } + if data_file.fields.len() != data_file.column_indices.len() { + return Err(Error::invalid_input(format!( + "Data file '{}' (fragment {}) has {} field ids but {} column indices. These must be the same length.", + data_file.path, + fragment.id, + data_file.fields.len(), + data_file.column_indices.len() + ))); + } + for (field_id, column_index) in + data_file.fields.iter().zip(data_file.column_indices.iter()) + { + let Some(field) = manifest.schema.field_by_id(*field_id) else { + continue; + }; + let needs_column = field.is_leaf() || field.is_packed_struct() || field.is_blob(); + if needs_column && *column_index == -1 { + return Err(Error::invalid_input(format!( + "Field '{}' (id={}) in data file '{}' (fragment {}) has column_index=-1, but leaf fields, packed structs, and blob fields must have a valid column index in file format 2.1+.", + field.name, field_id, data_file.path, fragment.id + ))); + } + if !needs_column && *column_index != -1 { + return Err(Error::invalid_input(format!( + "Non-leaf field '{}' (id={}) in data file '{}' (fragment {}) has column_index={}, but non-leaf fields should have column_index=-1 in file format 2.1+.", + field.name, field_id, data_file.path, fragment.id, column_index + ))); + } + } + } + } + Ok(()) +} + +pub fn validate_fragment_schema( + version: ConcreteFileVersion, + schema: &Schema, + fragments: &[Fragment], +) -> Result<()> { + match version { + ConcreteFileVersion::V1 => { + super::transaction::schema_fragments_legacy_valid(schema, fragments) + } + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + super::transaction::schema_fragments_modern_valid(schema, fragments) + } + } +} + +pub async fn write_fragment( + version: ConcreteFileVersion, + builder: &FragmentCreateBuilder<'_>, + stream: SendableRecordBatchStream, + schema: Schema, + id: u64, +) -> Result { + match version { + ConcreteFileVersion::V1 => builder.write_v1_impl(stream, schema, id).await, + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + builder + .write_current_impl( + move |object_writer, schema, filename| { + create_current_file_writer(version, object_writer, schema, filename, None) + }, + stream, + schema, + id, + ) + .await + } + } +} + +pub async fn open_writer( + version: ConcreteFileVersion, + object_store: &ObjectStore, + schema: &Schema, + base_dir: &Path, + options: WriterOptions, +) -> Result> { + match version { + ConcreteFileVersion::V1 => { + write::open_v1_writer(object_store, schema, base_dir, options).await + } + ConcreteFileVersion::V2_0 | ConcreteFileVersion::V2_1 => { + write::open_current_writer( + move |object_writer, schema, filename, base_id| { + create_current_file_writer(version, object_writer, schema, filename, base_id) + }, + object_store, + schema, + base_dir, + options, + ) + .await + } + ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => { + write::open_current_blob_v2_writer( + move |object_writer, schema, filename, base_id| { + create_current_file_writer(version, object_writer, schema, filename, base_id) + }, + object_store, + schema, + base_dir, + options, + ) + .await + } + } +} + +pub async fn open_data_writer( + version: ConcreteFileVersion, + object_store: &ObjectStore, + schema: &Schema, + base_dir: &Path, +) -> Result> { + open_writer( + version, + object_store, + schema, + base_dir, + WriterOptions::data_file(), + ) + .await +} + +pub async fn open_update_writer( + version: ConcreteFileVersion, + dataset: &Dataset, + schema: &Schema, +) -> Result> { + let external_base_resolver = match version { + ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => { + write::blob_v2_external_base_resolver(Some(dataset), &WriteParams::default(), schema) + .await? + } + ConcreteFileVersion::V1 | ConcreteFileVersion::V2_0 | ConcreteFileVersion::V2_1 => None, + }; + open_writer( + version, + &dataset.object_store, + schema, + &dataset.base, + WriterOptions::update(dataset.session.store_registry(), external_base_resolver), + ) + .await +} + +pub async fn create_fragment_from_file( + file_version: ConcreteFileVersion, + dataset_version: ConcreteFileVersion, + filename: &str, + dataset: &Dataset, + fragment_id: usize, + physical_rows: Option, +) -> Result { + if file_version != dataset_version { + return Err(Error::invalid_input(format!( + "File version mismatch. Dataset version: {:?} Fragment version: {:?}", + dataset_version, file_version + ))); + } + match file_version { + ConcreteFileVersion::V1 => { + FileFragment::create_from_v1_file(filename, dataset, fragment_id, physical_rows).await + } + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + FileFragment::create_from_current_file(filename, dataset, fragment_id).await + } + } +} + +pub fn index_file_version(version: ConcreteFileVersion) -> ConcreteFileVersion { + match version { + ConcreteFileVersion::V1 | ConcreteFileVersion::V2_0 => ConcreteFileVersion::V2_0, + ConcreteFileVersion::V2_1 => ConcreteFileVersion::V2_1, + ConcreteFileVersion::V2_2 => ConcreteFileVersion::V2_2, + ConcreteFileVersion::V2_3 => ConcreteFileVersion::V2_3, + } +} + +pub async fn open_file_reader( + version: ConcreteFileVersion, + fragment: &FileFragment, + data_file: &DataFile, + projection: Option<&Schema>, + read_config: &FragReadConfig, + metadata_mode: MetadataMode, +) -> Result>> { + match version { + ConcreteFileVersion::V1 => fragment.open_v1_file_reader(data_file, projection).await, + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + fragment + .open_current_file_reader(data_file, projection, read_config, metadata_mode) + .await + } + } +} + +pub async fn open_v1_fragment_reader( + fragment: &FileFragment, + projection: &Schema, + read_config: &FragReadConfig, +) -> Result { + for data_file in &fragment.metadata().files { + let actual = data_file.file_version()?; + if actual != ConcreteFileVersion::V1 { + return Err(Error::invalid_input(format!( + "Cannot open file {} with the v1 reader because it has version {}", + data_file.path, actual + ))); + } + } + fragment + .open_v1_fragment_reader(projection, read_config) + .await +} + +pub async fn row_group_size_for_rewrite( + version: ConcreteFileVersion, + fragment: &FileFragment, +) -> Result> { + match version { + ConcreteFileVersion::V1 => { + let reader = open_v1_fragment_reader( + fragment, + fragment.dataset().schema(), + &FragReadConfig::default(), + ) + .await?; + Ok(reader.num_rows_in_batch(0)) + } + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => Ok(None), + } +} + +pub fn is_upcast_downcast( + version: ConcreteFileVersion, + from_type: &DataType, + to_type: &DataType, +) -> bool { + is_upcast_downcast_impl( + from_type, + to_type, + !matches!(version, ConcreteFileVersion::V1), + ) +} + +fn is_upcast_downcast_impl( + from_type: &DataType, + to_type: &DataType, + dictionary_materialization: bool, +) -> bool { + use DataType::*; + match (from_type, to_type) { + (_, Dictionary(_, _)) if !dictionary_materialization => false, + (Dictionary(_, from_value_type), _) => { + is_upcast_downcast_impl(from_value_type, to_type, dictionary_materialization) + } + (_, Dictionary(_, to_value_type)) => { + is_upcast_downcast_impl(from_type, to_value_type, dictionary_materialization) + } + (from, to) if from.is_integer() => to.is_integer(), + (from, to) if from.is_floating() => to.is_floating(), + (from, to) if from.is_temporal() => to.is_temporal(), + (Boolean, to) => matches!(to, Boolean), + (Utf8 | LargeUtf8, to) => matches!(to, Utf8 | LargeUtf8), + (Binary | LargeBinary, to) => matches!(to, Binary | LargeBinary), + (Decimal128(_, _) | Decimal256(_, _), to) => { + matches!(to, Decimal128(_, _) | Decimal256(_, _)) + } + (List(from_field) | LargeList(from_field) | FixedSizeList(from_field, _), to_type) => { + match to_type { + List(to_field) | LargeList(to_field) | FixedSizeList(to_field, _) => { + is_upcast_downcast_impl( + from_field.data_type(), + to_field.data_type(), + dictionary_materialization, + ) + } + _ => false, + } + } + _ => false, + } +} + +pub fn validate_nulls( + version: ConcreteFileVersion, + datatype: &DataType, + has_nulls: bool, +) -> Result<()> { + let supported = match version { + ConcreteFileVersion::V1 => matches!( + datatype, + DataType::Utf8 + | DataType::LargeUtf8 + | DataType::Binary + | DataType::List(_) + | DataType::FixedSizeBinary(_) + | DataType::FixedSizeList(_, _) + ), + ConcreteFileVersion::V2_0 => !matches!(datatype, DataType::Struct(..)), + ConcreteFileVersion::V2_1 | ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => true, + }; + if has_nulls && !supported { + return Err(Error::invalid_input(format!( + "Join produced null values for type: {:?}, but storing nulls for this data type is not supported by the dataset's current Lance file format version: {:?}. This can be caused by an explicit null in the new data.", + datatype, version + ))); + } + Ok(()) +} + +fn reject_nested_column_add(field: &ArrowField, version: ConcreteFileVersion) -> Result<()> { + Err(Error::invalid_input(format!( + "Column {} is a struct col, add sub column is not supported in Lance file version {}", + field.name(), + version + ))) +} + +fn reject_nested_v1(field: &ArrowField) -> Result<()> { + reject_nested_column_add(field, ConcreteFileVersion::V1) +} + +fn reject_nested_v2_0(field: &ArrowField) -> Result<()> { + reject_nested_column_add(field, ConcreteFileVersion::V2_0) +} + +fn reject_nested_v2_1(field: &ArrowField) -> Result<()> { + reject_nested_column_add(field, ConcreteFileVersion::V2_1) +} + +fn allow_nested(_field: &ArrowField) -> Result<()> { + Ok(()) +} + +pub fn check_field_conflict( + version: ConcreteFileVersion, + left: &ArrowField, + right: &ArrowField, +) -> Result<()> { + let validate = match version { + ConcreteFileVersion::V1 => reject_nested_v1, + ConcreteFileVersion::V2_0 => reject_nested_v2_0, + ConcreteFileVersion::V2_1 => reject_nested_v2_1, + ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => allow_nested, + }; + super::schema_evolution::check_field_conflict_with(left, right, validate) +} + +fn exclude_struct_field(field: &Field, other: &Field) -> Option { + field + .data_type() + .is_struct() + .then(|| field.exclude(other)) + .flatten() +} + +fn exclude_nested_field(field: &Field, other: &Field) -> Option { + field + .data_type() + .is_nested() + .then(|| field.exclude(other)) + .flatten() +} + +pub fn exclude_schema( + version: ConcreteFileVersion, + source: &Schema, + other: &Schema, +) -> Result { + let exclude = match version { + ConcreteFileVersion::V1 | ConcreteFileVersion::V2_0 | ConcreteFileVersion::V2_1 => { + exclude_struct_field + } + ConcreteFileVersion::V2_2 | ConcreteFileVersion::V2_3 => exclude_nested_field, + }; + super::schema_evolution::exclude_with(source, other, exclude) +} + +pub fn configure_new_column_optimizers( + version: ConcreteFileVersion, + optimizer: &mut ChainedNewColumnTransformOptimizer, +) { + match version { + ConcreteFileVersion::V1 => {} + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + optimizer.add_optimizer(Box::new(SqlToAllNullsOptimizer::new())); + } + } +} + +pub fn validate_metadata_only_null_columns(version: ConcreteFileVersion) -> Result<()> { + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported_source( + "Cannot add all-null columns to legacy dataset version.".into(), + )), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => Ok(()), + } +} + +#[allow(clippy::too_many_arguments)] +pub(in crate::dataset) async fn filtered_read( + version: ConcreteFileVersion, + scanner: &Scanner, + filter_plan: &ExprFilterPlan, + projection: Projection, + make_deletions_null: bool, + fragments: Option>>, + scan_range: Option>, + is_prefilter: bool, +) -> Result { + match version { + ConcreteFileVersion::V1 => { + scanner + .legacy_filtered_read( + filter_plan, + projection, + make_deletions_null, + fragments, + scan_range, + is_prefilter, + ) + .await + } + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + let limit_pushed_down = scan_range.is_some(); + let plan = scanner + .new_filtered_read( + filter_plan, + projection, + make_deletions_null, + fragments, + scan_range, + ) + .await?; + Ok(PlannedFilteredScan { + filter_pushed_down: true, + limit_pushed_down, + plan, + }) + } + } +} + +pub fn take( + version: ConcreteFileVersion, + scanner: &Scanner, + input: Arc, + output_projection: Projection, +) -> Result> { + match version { + ConcreteFileVersion::V1 => scanner.take_legacy(input, output_projection), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => scanner.take_current(input, output_projection), + } +} + +pub async fn collect_data_stats( + version: ConcreteFileVersion, + dataset: &Arc, + field_stats: &mut HashMap, +) -> Result<()> { + match version { + ConcreteFileVersion::V1 => Ok(()), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + super::statistics::collect_current_data_stats(dataset, field_stats).await + } + } +} + +pub fn merge_insert_indexed_take( + version: ConcreteFileVersion, + dataset: Arc, + mut index_mapper: Arc, + projection: Projection, + add_row_addr: bool, +) -> Result> { + match version { + ConcreteFileVersion::V1 => { + if add_row_addr { + let position = index_mapper.schema().fields().len(); + index_mapper = Arc::new(AddRowAddrExec::try_new( + index_mapper, + dataset.clone(), + position, + )?); + } + Ok(Arc::new( + TakeExec::try_new(dataset, index_mapper, projection)?.ok_or_else(|| { + Error::internal("merge-insert legacy take unexpectedly needed no columns") + })?, + )) + } + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => { + let mut projection = projection.with_row_id(); + if add_row_addr { + projection = projection.with_row_addr(); + } + Ok(Arc::new(FilteredReadExec::try_new( + dataset, + FilteredReadOptions::new(projection), + Some(index_mapper), + )?)) + } + } +} + +pub fn validate_row_stream_read(version: ConcreteFileVersion) -> Result<()> { + match version { + ConcreteFileVersion::V1 => Err(Error::not_supported_source( + "taking rows through FilteredReadExec requires the v2 storage format" + .to_string() + .into(), + )), + ConcreteFileVersion::V2_0 + | ConcreteFileVersion::V2_1 + | ConcreteFileVersion::V2_2 + | ConcreteFileVersion::V2_3 => Ok(()), + } +} + +fn repair_legacy_manifest_storage(manifest: &mut Manifest) -> Result<()> { + let declared = manifest.data_storage_format.lance_file_format(); + if let Some(actual) = Fragment::try_infer_version(&manifest.fragments) + .map_err(|error| { + Error::internal(format!( + "The dataset contains a mixture of file versions. You will need to rollback to an earlier version: {error}" + )) + })? + && actual != ConcreteFileVersion::V1 + { + log::warn!( + "Data storage version {} is less than the actual file version {}. This has been automatically updated.", + declared, + actual + ); + manifest.data_storage_format = DataStorageFormat::new(actual); + } + Ok(()) +} + +fn validate_exact_manifest_storage( + manifest: &Manifest, + expected: ConcreteFileVersion, +) -> Result<()> { + if let Some(actual) = Fragment::try_infer_version(&manifest.fragments)? + && actual != expected + { + return Err(Error::internal(format!( + "The operation added files with version {}. However, the data storage version is {}.", + actual, expected + ))); + } + Ok(()) +} diff --git a/rust/lance/src/dataset/write.rs b/rust/lance/src/dataset/write.rs index b9226146e41..5cbfb80f252 100644 --- a/rust/lance/src/dataset/write.rs +++ b/rust/lance/src/dataset/write.rs @@ -5,33 +5,32 @@ use arrow_array::RecordBatch; use bytes::Bytes; use chrono::TimeDelta; use datafusion::physical_plan::SendableRecordBatchStream; -use futures::{StreamExt, TryStreamExt}; +use futures::StreamExt; use lance_arrow::{ ARROW_EXT_NAME_KEY, BLOB_DEDICATED_SIZE_THRESHOLD_META_KEY, BLOB_INLINE_SIZE_THRESHOLD_META_KEY, BLOB_META_KEY, BLOB_PACK_FILE_SIZE_THRESHOLD_META_KEY, BLOB_V2_EXT_NAME, }; -use lance_core::datatypes::{ - NullabilityComparison, OnMissing, OnTypeMismatch, SchemaCompareOptions, -}; +use lance_core::datatypes::{NullabilityComparison, OnMissing, OnTypeMismatch}; use lance_core::utils::tracing::{AUDIT_MODE_CREATE, AUDIT_TYPE_DATA, TRACE_FILE_AUDIT}; use lance_core::{Error, Result, datatypes::Schema}; -use lance_datafusion::chunker::{break_stream, chunk_stream}; use lance_datafusion::utils::StreamingWriteSource; -use lance_file::previous::writer::{ - FileWriter as PreviousFileWriter, ManifestProvider as PreviousManifestProvider, +use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; +use lance_file::versions::v1::writer::{ + FileWriter as V1FileWriter, ManifestProvider as V1ManifestProvider, }; -use lance_file::version::LanceFileVersion; -use lance_file::writer::{self as current_writer, FileWriterOptions}; +use lance_file::writer::{self as current_writer}; use lance_io::object_store::{ ObjectStore, ObjectStoreParams, ObjectStoreRegistry, parse_base_scoped_key, }; +use lance_io::traits::Writer; use lance_table::format::{BasePath, DataFile, Fragment, IndexMetadata}; use lance_table::io::commit::{CommitHandler, commit_handler_from_url}; use lance_table::io::manifest::ManifestDescribing; use object_store::path::Path; use std::borrow::Cow; use std::collections::{BTreeSet, HashMap, HashSet}; +use std::future::Future; use std::num::NonZero; use std::sync::Arc; use std::sync::atomic::AtomicUsize; @@ -53,6 +52,7 @@ use super::fragment::write::generate_random_filename; use super::progress::{NoopFragmentWriteProgress, WriteFragmentProgress}; use super::transaction::Transaction; use super::utils::SchemaAdapter; +use super::versions; mod commit; pub mod delete; @@ -453,8 +453,8 @@ impl WriteParams { } } - pub fn storage_version_or_default(&self) -> LanceFileVersion { - self.data_storage_version.unwrap_or_default() + pub fn storage_version_or_default(&self) -> ConcreteFileVersion { + self.data_storage_version.unwrap_or_default().resolve() } pub fn store_registry(&self) -> Arc { @@ -594,40 +594,22 @@ pub async fn write_fragments( } #[allow(clippy::too_many_arguments)] -pub async fn do_write_fragments( +pub(super) async fn do_write_fragments_impl( dataset: Option<&Dataset>, object_store: Arc, base_dir: &Path, schema: &Schema, - data: SendableRecordBatchStream, + mut buffered_reader: futures::stream::BoxStream<'static, Result>>, params: WriteParams, - storage_version: LanceFileVersion, + open_writer: OpenWriter, + external_base_resolver: Option>, target_bases_info: Option>, mut seed_writers: Vec>, -) -> Result> { - let adapter = SchemaAdapter::new(data.schema()); - let data = adapter.to_physical_stream(data); - - let mut buffered_reader = if storage_version == LanceFileVersion::Legacy { - // In v1 we split the stream into row group sized batches - chunk_stream(data, params.max_rows_per_group) - } else { - // In v2 we don't care about group size but we do want to break - // the stream on file boundaries - break_stream(data, params.max_rows_per_file) - .map_ok(|batch| vec![batch]) - .boxed() - }; - - let external_base_resolver = if storage_version >= LanceFileVersion::V2_2 - && schema.fields_pre_order().any(|field| field.is_blob_v2()) - { - Some(Arc::new( - build_external_base_resolver(dataset, ¶ms).await?, - )) - } else { - None - }; +) -> Result> +where + OpenWriter: Fn(Arc, Schema, Path, WriterOptions) -> OpenWriterFuture + Send + Sync, + OpenWriterFuture: Future>> + Send, +{ let source_store_registry = dataset .map(|ds| ds.session.store_registry()) .unwrap_or_else(|| params.store_registry()); @@ -639,7 +621,7 @@ pub async fn do_write_fragments( object_store.clone(), base_dir, schema, - storage_version, + open_writer, target_bases_info, external_base_resolver, params.allow_external_blob_outside_bases, @@ -782,6 +764,44 @@ pub async fn do_write_fragments( Ok(fragments) } +#[cfg(test)] +#[allow(clippy::too_many_arguments)] +async fn do_write_fragments( + dataset: Option<&Dataset>, + object_store: Arc, + base_dir: &Path, + schema: &Schema, + data: SendableRecordBatchStream, + params: WriteParams, + storage_version: ConcreteFileVersion, + target_bases_info: Option>, + seed_writers: Vec>, +) -> Result> { + versions::write_fragments_direct( + storage_version, + dataset, + object_store, + base_dir, + schema, + data, + params, + target_bases_info, + seed_writers, + ) + .await +} + +#[cfg(test)] +async fn open_writer_with_options( + object_store: &ObjectStore, + schema: &Schema, + base_dir: &Path, + storage_version: ConcreteFileVersion, + options: WriterOptions, +) -> Result> { + versions::open_writer(storage_version, object_store, schema, base_dir, options).await +} + /// Flush all seed writers into the given file writer, embedding seed buffers /// and schema metadata before `finish()` is called. async fn flush_seed_writers( @@ -1258,6 +1278,20 @@ async fn build_external_base_resolver( Ok(ExternalBaseResolver::new(candidates, store_registry)) } +pub(super) async fn blob_v2_external_base_resolver( + dataset: Option<&Dataset>, + params: &WriteParams, + schema: &Schema, +) -> Result>> { + if schema.fields_pre_order().any(|field| field.is_blob_v2()) { + Ok(Some(Arc::new( + build_external_base_resolver(dataset, params).await?, + ))) + } else { + Ok(None) + } +} + /// Writes the given data to the dataset and returns fragments. /// /// NOTE: the fragments have not yet been assigned an ID. That must be done @@ -1296,103 +1330,98 @@ pub async fn write_fragments_internal( validate_external_blob_write_params(¶ms)?; let normalized_converted_schema = normalize_prepared_blob_schema(&converted_schema)?; - let (schema, storage_version) = if let Some(dataset) = dataset { + let storage_version = resolve_storage_version(dataset, ¶ms); + versions::write_fragments( + storage_version, + dataset, + object_store, + base_dir, + normalized_converted_schema, + data, + params, + target_bases_info, + ) + .await +} + +fn resolve_storage_version(dataset: Option<&Dataset>, params: &WriteParams) -> ConcreteFileVersion { + if let Some(dataset) = dataset { match params.mode { WriteMode::Append | WriteMode::Create => { - // Append mode, so we need to check compatibility - normalized_converted_schema.check_compatible( - dataset.schema(), - &SchemaCompareOptions { - // We don't care if the user claims their data is nullable / non-nullable. We will - // verify against the actual data. - compare_nullability: NullabilityComparison::Ignore, - allow_missing_if_nullable: true, - ignore_field_order: true, - compare_dictionary: dataset.is_legacy_storage(), - ..Default::default() - }, - )?; - validate_blob_threshold_metadata_for_append( - &normalized_converted_schema, - dataset.schema(), - )?; - let write_schema = dataset.schema().project_by_schema( - &normalized_converted_schema, - OnMissing::Error, - OnTypeMismatch::Error, - )?; // Use the storage version from the dataset, ignoring any version from the user. - let data_storage_version = dataset - .manifest() - .data_storage_format - .lance_file_version()?; - (write_schema, data_storage_version) + dataset.manifest().data_storage_format.lance_file_format() } WriteMode::Overwrite => { // Overwrite, use the schema from the data. If the user specified // a storage version use that. Otherwise use the version from the // dataset. - let data_storage_version = params.data_storage_version.unwrap_or( - dataset - .manifest() - .data_storage_format - .lance_file_version()?, - ); - (normalized_converted_schema, data_storage_version) + params + .data_storage_version + .map(LanceFileVersion::resolve) + .unwrap_or_else(|| dataset.manifest().data_storage_format.lance_file_format()) } } } else { // Brand new dataset, use the schema from the data and the storage version // from the user or the default. - ( - normalized_converted_schema, - params.storage_version_or_default(), - ) - }; + params.storage_version_or_default() + } +} - if storage_version < LanceFileVersion::V2_2 && schema.fields_pre_order().any(|f| f.is_blob_v2()) +pub(super) fn prepare_write_schema( + dataset: Option<&Dataset>, + normalized_converted_schema: Schema, + params: &WriteParams, + mut schema_compare_options: lance_core::datatypes::SchemaCompareOptions, +) -> Result { + let schema = if let Some(dataset) = dataset + && matches!(params.mode, WriteMode::Append | WriteMode::Create) { + schema_compare_options.compare_nullability = NullabilityComparison::Ignore; + schema_compare_options.allow_missing_if_nullable = true; + schema_compare_options.ignore_field_order = true; + normalized_converted_schema.check_compatible(dataset.schema(), &schema_compare_options)?; + validate_blob_threshold_metadata_for_append( + &normalized_converted_schema, + dataset.schema(), + )?; + dataset.schema().project_by_schema( + &normalized_converted_schema, + OnMissing::Error, + OnTypeMismatch::Error, + )? + } else { + normalized_converted_schema + }; + Ok(schema) +} + +pub(super) fn validate_legacy_blob_write_schema( + schema: &Schema, + version_debug: &str, +) -> Result<()> { + if schema.fields_pre_order().any(|field| field.is_blob_v2()) { return Err(Error::invalid_input(format!( - "Blob v2 requires file version >= 2.2 (got {:?})", - storage_version + "Blob v2 requires file version >= 2.2 (got {version_debug})" ))); } + Ok(()) +} - if storage_version >= LanceFileVersion::V2_2 - && let Some(blob_field_path) = legacy_blob_field_path(&schema) - { +pub(super) fn validate_blob_v2_write_schema(schema: &Schema) -> Result<()> { + if let Some(blob_field_path) = legacy_blob_field_path(schema) { return Err(Error::invalid_input(format!( "Legacy blob columns (field metadata key {BLOB_META_KEY:?}) are not supported for file version >= 2.2. Found legacy blob field: {blob_field_path}. Use the blob v2 extension type (ARROW:extension:name = \"lance.blob.v2\") and the new blob APIs (e.g. lance::blob::blob_field / lance::blob::BlobArrayBuilder)." ))); } - - let seed_writers = create_seed_writers(dataset, ¶ms, storage_version).await?; - - let fragments = do_write_fragments( - dataset, - object_store, - base_dir, - &schema, - data, - params, - storage_version, - target_bases_info, - seed_writers, - ) - .await?; - - Ok((fragments, schema)) + Ok(()) } -async fn create_seed_writers( +pub(crate) async fn create_seed_writers_current( dataset: Option<&Dataset>, params: &WriteParams, - storage_version: LanceFileVersion, ) -> Result>> { - // Seeds only make sense when appending to an existing dataset with V2 files. - if storage_version == LanceFileVersion::Legacy { - return Ok(Vec::new()); - } + // Seeds only make sense when appending to an existing dataset. if !matches!(params.mode, WriteMode::Append) { return Ok(Vec::new()); } @@ -1471,9 +1500,9 @@ pub trait GenericWriter: Send { struct V1WriterAdapter where - M: PreviousManifestProvider + Send + Sync, + M: V1ManifestProvider + Send + Sync, { - writer: PreviousFileWriter, + writer: V1FileWriter, path: String, base_id: Option, } @@ -1481,7 +1510,7 @@ where #[async_trait::async_trait] impl GenericWriter for V1WriterAdapter where - M: PreviousManifestProvider + Send + Sync, + M: V1ManifestProvider + Send + Sync, { async fn write(&mut self, batches: &[RecordBatch]) -> Result<()> { self.writer.write(batches).await @@ -1508,8 +1537,7 @@ where struct V2WriterAdapter { writer: current_writer::FileWriter, - path: String, - base_id: Option, + data_file: Option, preprocessor: Option, } @@ -1529,7 +1557,10 @@ impl GenericWriter for V2WriterAdapter { Ok(()) } fn data_file_path(&self) -> (&str, Option) { - (&self.path, self.base_id) + self.data_file + .as_ref() + .map(|data_file| (data_file.path.as_str(), data_file.base_id)) + .unwrap_or(("", None)) } async fn tell(&mut self) -> Result { Ok(self.writer.tell().await?) @@ -1550,17 +1581,14 @@ impl GenericWriter for V2WriterAdapter { .iter() .map(|(_, column_index)| *column_index as i32) .collect::>(); - let (major, minor) = self.writer.version().to_numbers(); let write_summary = self.writer.finish().await?; - let data_file = DataFile::new( - std::mem::take(&mut self.path), - field_ids, - column_indices, - major, - minor, - NonZero::new(write_summary.size_bytes), - self.base_id, - ); + let mut data_file = self + .data_file + .take() + .ok_or_else(|| Error::internal("current writer was already finished"))?; + data_file.fields = field_ids.into(); + data_file.column_indices = column_indices.into(); + data_file.file_size_bytes = NonZero::new(write_summary.size_bytes).into(); Ok((write_summary.num_rows as u32, data_file)) } @@ -1573,60 +1601,8 @@ impl GenericWriter for V2WriterAdapter { } } -pub async fn open_writer( - object_store: &ObjectStore, - schema: &Schema, - base_dir: &Path, - storage_version: LanceFileVersion, -) -> Result> { - open_writer_with_options( - object_store, - schema, - base_dir, - storage_version, - WriterOptions { - add_data_dir: true, - ..Default::default() - }, - ) - .await -} - -pub(super) async fn open_update_writer( - dataset: &Dataset, - schema: &Schema, - storage_version: LanceFileVersion, -) -> Result> { - // add_columns / alter_columns reuse the normal writer stack, but they do not - // flow through WriteParams. Rebuild the external base resolver here so blob - // v2 reference columns can resolve dataset-registered external URIs. - let external_base_resolver = if storage_version >= LanceFileVersion::V2_2 - && schema.fields_pre_order().any(|f| f.is_blob_v2()) - { - Some(Arc::new( - build_external_base_resolver(Some(dataset), &WriteParams::default()).await?, - )) - } else { - None - }; - - open_writer_with_options( - &dataset.object_store, - schema, - &dataset.base, - storage_version, - WriterOptions { - add_data_dir: true, - external_base_resolver, - source_store_registry: dataset.session.store_registry(), - ..Default::default() - }, - ) - .await -} - #[derive(Default)] -struct WriterOptions { +pub(crate) struct WriterOptions { add_data_dir: bool, base_id: Option, external_base_resolver: Option>, @@ -1637,13 +1613,99 @@ struct WriterOptions { blob_pack_file_size_threshold: Option, } -async fn open_writer_with_options( +impl WriterOptions { + pub(super) fn data_file() -> Self { + Self { + add_data_dir: true, + ..Default::default() + } + } + + pub(super) fn update( + source_store_registry: Arc, + external_base_resolver: Option>, + ) -> Self { + Self { + add_data_dir: true, + external_base_resolver, + source_store_registry, + ..Default::default() + } + } +} + +pub(crate) async fn open_v1_writer( object_store: &ObjectStore, schema: &Schema, base_dir: &Path, - storage_version: LanceFileVersion, options: WriterOptions, ) -> Result> { + let WriterOptions { + add_data_dir, + base_id, + .. + } = options; + let (_data_file_key, filename, _data_dir, full_path) = + prepare_data_file_path(base_dir, add_data_dir); + Ok(Box::new(V1WriterAdapter { + writer: V1FileWriter::::try_new( + object_store, + &full_path, + schema.clone(), + &Default::default(), + ) + .await?, + path: filename, + base_id, + })) +} + +pub(in crate::dataset) async fn open_current_writer( + create_file_writer: F, + object_store: &ObjectStore, + schema: &Schema, + base_dir: &Path, + options: WriterOptions, +) -> Result> +where + F: FnOnce( + Box, + Schema, + String, + Option, + ) -> Result<(current_writer::FileWriter, DataFile)>, +{ + let WriterOptions { + add_data_dir, + base_id, + .. + } = options; + let (_data_file_key, filename, _data_dir, full_path) = + prepare_data_file_path(base_dir, add_data_dir); + let writer = object_store.create(&full_path).await?; + let (file_writer, data_file) = create_file_writer(writer, schema.clone(), filename, base_id)?; + Ok(Box::new(V2WriterAdapter { + writer: file_writer, + data_file: Some(data_file), + preprocessor: None, + })) +} + +pub(in crate::dataset) async fn open_current_blob_v2_writer( + create_file_writer: F, + object_store: &ObjectStore, + schema: &Schema, + base_dir: &Path, + options: WriterOptions, +) -> Result> +where + F: FnOnce( + Box, + Schema, + String, + Option, + ) -> Result<(current_writer::FileWriter, DataFile)>, +{ let WriterOptions { add_data_dir, base_id, @@ -1654,66 +1716,39 @@ async fn open_writer_with_options( source_store_params, blob_pack_file_size_threshold, } = options; + let (data_file_key, filename, data_dir, full_path) = + prepare_data_file_path(base_dir, add_data_dir); + let writer = object_store.create(&full_path).await?; + let (file_writer, data_file) = create_file_writer(writer, schema.clone(), filename, base_id)?; + let preprocessor = BlobPreprocessor::new( + object_store.clone(), + data_dir, + data_file_key, + schema, + external_base_resolver, + allow_external_blob_outside_bases, + external_blob_mode, + source_store_registry, + source_store_params, + blob_pack_file_size_threshold, + )?; + Ok(Box::new(V2WriterAdapter { + writer: file_writer, + data_file: Some(data_file), + preprocessor: Some(preprocessor), + })) +} +fn prepare_data_file_path(base_dir: &Path, add_data_dir: bool) -> (String, String, Path, Path) { let data_file_key = generate_random_filename(); let filename = format!("{}.lance", data_file_key); - let data_dir = if add_data_dir { base_dir.clone().join(DATA_DIR) } else { base_dir.clone() }; - let full_path = data_dir.clone().join(filename.as_str()); - - let writer = if storage_version == LanceFileVersion::Legacy { - Box::new(V1WriterAdapter { - writer: PreviousFileWriter::::try_new( - object_store, - &full_path, - schema.clone(), - &Default::default(), - ) - .await?, - path: filename, - base_id, - }) - } else { - let writer = object_store.create(&full_path).await?; - let enable_blob_v2 = storage_version >= LanceFileVersion::V2_2; - let file_writer = current_writer::FileWriter::try_new( - writer, - schema.clone(), - FileWriterOptions { - format_version: Some(storage_version), - ..Default::default() - }, - )?; - let preprocessor = if enable_blob_v2 { - Some(BlobPreprocessor::new( - object_store.clone(), - data_dir.clone(), - data_file_key.clone(), - schema, - external_base_resolver, - allow_external_blob_outside_bases, - external_blob_mode, - source_store_registry, - source_store_params, - blob_pack_file_size_threshold, - )?) - } else { - None - }; - let writer_adapter = V2WriterAdapter { - writer: file_writer, - path: filename, - base_id, - preprocessor, - }; - Box::new(writer_adapter) as Box - }; - Ok(writer) + (data_file_key, filename, data_dir, full_path) } /// Reserved base id that refers to the dataset's primary storage in @@ -1738,13 +1773,13 @@ pub struct TargetBaseInfo { pub is_dataset_root: bool, } -struct WriterGenerator { +struct WriterGenerator { /// Default object store (used when no target bases specified) object_store: Arc, /// Default base directory (used when no target bases specified) base_dir: Path, schema: Schema, - storage_version: LanceFileVersion, + open_writer: OpenWriter, /// Target base information (if writing to specific bases) target_bases_info: Option>, external_base_resolver: Option>, @@ -1757,13 +1792,17 @@ struct WriterGenerator { next_base_index: AtomicUsize, } -impl WriterGenerator { +impl WriterGenerator +where + OpenWriter: Fn(Arc, Schema, Path, WriterOptions) -> OpenWriterFuture + Send + Sync, + OpenWriterFuture: Future>> + Send, +{ #[allow(clippy::too_many_arguments)] pub fn new( object_store: Arc, base_dir: &Path, schema: &Schema, - storage_version: LanceFileVersion, + open_writer: OpenWriter, target_bases_info: Option>, external_base_resolver: Option>, allow_external_blob_outside_bases: bool, @@ -1776,7 +1815,7 @@ impl WriterGenerator { object_store, base_dir: base_dir.clone(), schema: schema.clone(), - storage_version, + open_writer, target_bases_info, external_base_resolver, allow_external_blob_outside_bases, @@ -1804,11 +1843,10 @@ impl WriterGenerator { let fragment = Fragment::new(0); let writer = if let Some(base_info) = self.select_target_base() { - open_writer_with_options( - &base_info.object_store, - &self.schema, - &base_info.base_dir, - self.storage_version, + (self.open_writer)( + base_info.object_store.clone(), + self.schema.clone(), + base_info.base_dir.clone(), WriterOptions { add_data_dir: base_info.is_dataset_root, // Primary-storage slots stamp no base id, like a write @@ -1824,11 +1862,10 @@ impl WriterGenerator { ) .await? } else { - open_writer_with_options( - &self.object_store, - &self.schema, - &self.base_dir, - self.storage_version, + (self.open_writer)( + self.object_store.clone(), + self.schema.clone(), + self.base_dir.clone(), WriterOptions { add_data_dir: true, base_id: None, @@ -1889,12 +1926,40 @@ mod tests { use datafusion::{error::DataFusionError, physical_plan::stream::RecordBatchStreamAdapter}; use datafusion_physical_plan::RecordBatchStream; use futures::TryStreamExt; + use lance_datafusion::chunker::chunk_stream; use lance_datagen::{BatchCount, RowCount, array, gen_batch}; - use lance_file::previous::reader::FileReader as PreviousFileReader; + use lance_file::version::ConcreteFileVersion; + use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_io::object_store::StorageOptionsAccessor; use lance_io::traits::Reader; use lance_table::format::BasePath; + async fn open_v2_1_test_writer( + object_store: Arc, + schema: Schema, + base_dir: Path, + options: WriterOptions, + ) -> Result> { + open_current_writer( + |object_writer, schema, filename, base_id| { + let writer = lance_file::versions::v2_1::create_writer( + object_writer, + schema, + lance_file::writer::FileWriterOptions::default(), + )? + .into(); + let mut data_file = DataFile::new_unstarted(filename, ConcreteFileVersion::V2_1); + data_file.base_id = base_id; + Ok((writer, data_file)) + }, + &object_store, + &schema, + &base_dir, + options, + ) + .await + } + #[test] fn test_auto_cleanup_disabled_by_default() { // Auto-cleanup must be off by default: the cleanup hook is expensive on @@ -2220,7 +2285,7 @@ mod tests { LanceFileVersion::Next, ]; for version in versions { - let (major, minor) = version.to_numbers(); + let (major, minor) = version.resolve().to_data_file_numbers(); let write_params = WriteParams { data_storage_version: Some(version), // This parameter should be ignored @@ -2333,7 +2398,7 @@ mod tests { .join(DATA_DIR) .join(fragment.files[0].path.as_str()); let file_reader: Arc = object_store.open(&path).await.unwrap().into(); - let reader = PreviousFileReader::try_new_from_reader( + let reader = V1FileReader::try_new_from_reader( &path, file_reader, None, @@ -2521,7 +2586,7 @@ mod tests { object_store.clone(), &base_dir, &schema, - LanceFileVersion::Stable, + open_v2_1_test_writer, Some(target_bases), None, false, @@ -2571,7 +2636,7 @@ mod tests { &object_store, &schema, &base_dir, - LanceFileVersion::Stable, + LanceFileVersion::Stable.resolve(), WriterOptions { add_data_dir: false, // Don't add /data ..Default::default() @@ -2639,7 +2704,7 @@ mod tests { Arc::new(ObjectStore::memory()), &Path::from("default"), &schema, - LanceFileVersion::Stable, + open_v2_1_test_writer, Some(target_bases), None, false, @@ -3885,7 +3950,7 @@ mod tests { &schema, stream, WriteParams::default(), - LanceFileVersion::V2_1, + ConcreteFileVersion::V2_1, None, Vec::new(), ) @@ -3946,7 +4011,7 @@ mod tests { max_rows_per_file: 3, ..Default::default() }, - LanceFileVersion::V2_1, + ConcreteFileVersion::V2_1, None, Vec::new(), ) @@ -3983,9 +4048,10 @@ mod tests { // Sanity check: file is on disk. assert_eq!(count_data_files(test_uri), 1); - let mut external_file = DataFile::new_unstarted("external.lance", 2, 1); + let mut external_file = + DataFile::new_unstarted("external.lance", ConcreteFileVersion::V2_1); external_file.base_id = Some(42); - let local_file = DataFile::new_unstarted(local_filename, 2, 1); + let local_file = DataFile::new_unstarted(local_filename, ConcreteFileVersion::V2_1); let fragments = vec![Fragment { id: 0, files: vec![external_file, local_file], @@ -4061,11 +4127,11 @@ mod tests { assert_eq!(count_data_files(base1_dir.as_str()), 1); assert_eq!(count_plain_files(base2_dir.as_str()), 1); - let mut base1_file = DataFile::new_unstarted("one.lance", 2, 1); + let mut base1_file = DataFile::new_unstarted("one.lance", ConcreteFileVersion::V2_1); base1_file.base_id = Some(1); - let mut base2_file = DataFile::new_unstarted("two.lance", 2, 1); + let mut base2_file = DataFile::new_unstarted("two.lance", ConcreteFileVersion::V2_1); base2_file.base_id = Some(2); - let mut unknown_file = DataFile::new_unstarted("unknown.lance", 2, 1); + let mut unknown_file = DataFile::new_unstarted("unknown.lance", ConcreteFileVersion::V2_1); unknown_file.base_id = Some(42); let fragments = vec![Fragment { id: 0, @@ -4173,7 +4239,7 @@ mod tests { max_rows_per_file: 3, ..Default::default() }, - LanceFileVersion::V2_1, + ConcreteFileVersion::V2_1, Some(target_bases), vec![], ) diff --git a/rust/lance/src/dataset/write/commit.rs b/rust/lance/src/dataset/write/commit.rs index fdea57d88c6..f43b9ae3da1 100644 --- a/rust/lance/src/dataset/write/commit.rs +++ b/rust/lance/src/dataset/write/commit.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; -use lance_file::version::LanceFileVersion; +use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; use lance_io::object_store::{ObjectStore, ObjectStoreParams}; use lance_select::RowAddrTreeMap; use lance_table::{ @@ -39,7 +39,7 @@ pub struct CommitBuilder<'a> { dest: WriteDestination<'a>, use_stable_row_ids: Option, enable_v2_manifest_paths: bool, - storage_format: Option, + storage_format: Option, commit_handler: Option>, store_params: Option, object_store: Option>, @@ -95,6 +95,11 @@ impl<'a> CommitBuilder<'a> { /// All data files must use the same storage format as the existing dataset. /// If a different format is passed, an error will be returned. pub fn with_storage_format(mut self, storage_format: LanceFileVersion) -> Self { + self.storage_format = Some(storage_format.resolve()); + self + } + + pub(crate) fn with_exact_storage_format(mut self, storage_format: ConcreteFileVersion) -> Self { self.storage_format = Some(storage_format); self } @@ -555,7 +560,8 @@ mod tests { use super::*; fn sample_fragment() -> Fragment { - let (major_version, minor_version) = LanceFileVersion::Stable.to_numbers(); + let (major_version, minor_version) = + LanceFileVersion::Stable.resolve().to_data_file_numbers(); Fragment { id: 0, files: vec![DataFile { diff --git a/rust/lance/src/dataset/write/insert.rs b/rust/lance/src/dataset/write/insert.rs index 144cc2bac6e..114cef022ea 100644 --- a/rust/lance/src/dataset/write/insert.rs +++ b/rust/lance/src/dataset/write/insert.rs @@ -7,11 +7,11 @@ use std::sync::Arc; use arrow_array::{RecordBatch, RecordBatchIterator}; use datafusion::execution::SendableRecordBatchStream; use humantime::format_duration; -use lance_core::datatypes::{NullabilityComparison, Schema, SchemaCompareOptions}; +use lance_core::datatypes::{NullabilityComparison, Schema}; use lance_core::is_system_column; use lance_core::utils::tracing::{DATASET_WRITING_EVENT, TRACE_DATASET_EVENTS}; use lance_datafusion::utils::StreamingWriteSource; -use lance_file::version::LanceFileVersion; +use lance_file::version::{ConcreteFileVersion, LanceFileVersion}; use lance_io::object_store::ObjectStore; use lance_table::feature_flags::can_write_dataset; use lance_table::format::Fragment; @@ -136,7 +136,7 @@ impl<'a> InsertBuilder<'a> { async fn do_commit(context: &WriteContext<'_>, transaction: Transaction) -> Result { let mut commit_builder = CommitBuilder::new(context.dest.clone()) .use_stable_row_ids(context.params.enable_stable_row_ids) - .with_storage_format(context.storage_version) + .with_exact_storage_format(context.storage_version) .enable_v2_manifest_paths(context.params.enable_v2_manifest_paths) .with_commit_handler(context.commit_handler.clone()) .with_object_store(context.object_store.clone()) @@ -316,13 +316,11 @@ impl<'a> InsertBuilder<'a> { context.params.enable_stable_row_ids = dataset.manifest.uses_stable_row_ids(); } - let schema_cmp_opts = SchemaCompareOptions { - compare_dictionary: dataset.manifest.should_use_legacy_format(), - compare_nullability: NullabilityComparison::Ignore, - allow_missing_if_nullable: true, - ignore_field_order: true, - ..Default::default() - }; + let version = dataset.manifest.data_storage_format.lance_file_format(); + let mut schema_cmp_opts = crate::dataset::versions::schema_compare_options(version); + schema_cmp_opts.compare_nullability = NullabilityComparison::Ignore; + schema_cmp_opts.allow_missing_if_nullable = true; + schema_cmp_opts.ignore_field_order = true; let normalized_data_schema = normalize_prepared_blob_schema(data_schema)?; normalized_data_schema.check_compatible(dataset.schema(), &schema_cmp_opts)?; @@ -416,15 +414,15 @@ impl<'a> InsertBuilder<'a> { (WriteMode::Overwrite, WriteDestination::Dataset(dataset)) => { // If overwriting an existing dataset, allow the user to specify but use // the existing version if they don't - params.data_storage_version.map(Ok).unwrap_or_else(|| { - let m = dataset.manifest.as_ref(); - m.data_storage_format.lance_file_version() - })? + params + .data_storage_version + .map(LanceFileVersion::resolve) + .unwrap_or_else(|| dataset.manifest.data_storage_format.lance_file_format()) } (_, WriteDestination::Dataset(dataset)) => { // If appending to an existing dataset, always use the dataset version let m = dataset.manifest.as_ref(); - m.data_storage_format.lance_file_version()? + m.data_storage_format.lance_file_format() } // Otherwise (no existing dataset) fallback to the default if the user didn't specify (_, WriteDestination::Uri(_)) => params.storage_version_or_default(), @@ -448,7 +446,7 @@ struct WriteContext<'a> { object_store: Arc, base_path: Path, commit_handler: Arc, - storage_version: LanceFileVersion, + storage_version: ConcreteFileVersion, } #[cfg(test)] diff --git a/rust/lance/src/dataset/write/merge_insert.rs b/rust/lance/src/dataset/write/merge_insert.rs index 2f0d8ee4b37..ff832a02849 100644 --- a/rust/lance/src/dataset/write/merge_insert.rs +++ b/rust/lance/src/dataset/write/merge_insert.rs @@ -55,15 +55,14 @@ use crate::{ Dataset, datafusion::dataframe::SessionContextExt, dataset::{ - fragment::{FileFragment, FragReadConfig}, + fragment::FileFragment, transaction::{Operation, Transaction}, - write::{merge_insert::logical_plan::MergeInsertPlanner, open_writer}, + versions, + write::merge_insert::logical_plan::MergeInsertPlanner, }, index::DatasetIndexInternalExt, io::exec::{ - AddRowAddrExec, Planner, TakeExec, - filtered_read::{FilteredReadExec, FilteredReadOptions}, - project, + Planner, project, scalar_index::{IndexLookup, MapIndexExec}, utils::ReplayExec, }, @@ -124,6 +123,7 @@ use lance_datafusion::{ spill::spilling_table_provider, utils::{StreamingWriteSource, reader_to_stream}, }; +#[cfg(test)] use lance_file::version::LanceFileVersion; use lance_index::IndexCriteria; use lance_index::mem_wal::MergedGeneration; @@ -754,11 +754,13 @@ impl MergeInsertJob { let lance_schema: lance_core::datatypes::Schema = schema.try_into()?; let target_schema = self.dataset.schema(); - let mut options = SchemaCompareOptions { - compare_dictionary: self.dataset.is_legacy_storage(), - compare_nullability: NullabilityComparison::Ignore, - ..Default::default() - }; + let version = self + .dataset + .manifest() + .data_storage_format + .lance_file_format(); + let mut options = versions::schema_compare_options(version); + options.compare_nullability = NullabilityComparison::Ignore; // Try full schema match first. if lance_schema @@ -868,7 +870,7 @@ impl MergeInsertJob { .iter() .map(|(col, idx)| IndexLookup::new(col.clone(), idx.name.clone())) .collect::>(); - let mut index_mapper: Arc = Arc::new(MapIndexExec::new_multi( + let index_mapper: Arc = Arc::new(MapIndexExec::new_multi( self.dataset.clone(), lookups, index_mapper_input, @@ -880,29 +882,16 @@ impl MergeInsertJob { .dataset .empty_projection() .union_arrow_schema(schema.as_ref(), OnMissing::Error)?; - let mut target: Arc = if self.dataset.is_legacy_storage() { - if add_row_addr { - let pos = index_mapper.schema().fields().len(); // Add to end - index_mapper = Arc::new(AddRowAddrExec::try_new( - index_mapper, - self.dataset.clone(), - pos, - )?); - } - Arc::new(TakeExec::try_new(self.dataset.clone(), index_mapper, projection)?.unwrap()) - } else { - // Keep the mapped row ids; the read synthesizes the row addresses - // if requested (no AddRowAddrExec needed) - let mut projection = projection.with_row_id(); - if add_row_addr { - projection = projection.with_row_addr(); - } - Arc::new(FilteredReadExec::try_new( - self.dataset.clone(), - FilteredReadOptions::new(projection), - Some(index_mapper), - )?) - }; + let mut target = versions::merge_insert_indexed_take( + self.dataset + .manifest() + .data_storage_format + .lance_file_format(), + self.dataset.clone(), + index_mapper, + projection, + add_row_addr, + )?; // 5 - Take puts the row id and row addr at the beginning. A full scan (used when there is // no scalar index) puts the row id and addr at the end. We need to match these up so @@ -1254,15 +1243,13 @@ impl MergeInsertJob { // Also, because we already sorted by row address, the rows // will be in the correct order. - let data_storage_version = dataset - .manifest() - .data_storage_format - .lance_file_version()?; - let mut writer = open_writer( + let data_storage_version = + dataset.manifest().data_storage_format.lance_file_format(); + let mut writer = versions::open_data_writer( + data_storage_version, &dataset.object_store, &write_schema, &dataset.base, - data_storage_version, ) .await?; @@ -1291,16 +1278,12 @@ impl MergeInsertJob { } } - if data_storage_version == LanceFileVersion::Legacy { + if let Some(batch_size) = + versions::row_group_size_for_rewrite(data_storage_version, &fragment) + .await? + { // Need to match the existing batch size exactly, otherwise // we'll get errors. - let reader = fragment - .open( - dataset.schema(), - FragReadConfig::default().with_row_address(true), - ) - .await?; - let batch_size = reader.legacy_num_rows_in_batch(0).unwrap(); let stream = stream::iter(batches.into_iter().map(Ok)); let stream = Box::pin(RecordBatchStreamAdapter::new( Arc::new((&write_schema).into()), diff --git a/rust/lance/src/dataset/write/update.rs b/rust/lance/src/dataset/write/update.rs index a87b3a4260b..0cd09e156c8 100644 --- a/rust/lance/src/dataset/write/update.rs +++ b/rust/lance/src/dataset/write/update.rs @@ -320,18 +320,13 @@ impl UpdateJob { }); let stream = RecordBatchStreamAdapter::new(schema, stream); - let version = self - .dataset - .manifest() - .data_storage_format - .lance_file_version()?; let (mut new_fragments, _) = write_fragments_internal( Some(&self.dataset), self.dataset.object_store.clone(), &self.dataset.base, self.dataset.schema().clone(), Box::pin(stream), - WriteParams::with_storage_version(version), + WriteParams::default(), None, // TODO: support multiple bases for update ) .await?; diff --git a/rust/lance/src/index.rs b/rust/lance/src/index.rs index 65a2692414e..a666b8bb212 100644 --- a/rust/lance/src/index.rs +++ b/rust/lance/src/index.rs @@ -21,8 +21,8 @@ use lance_core::utils::parse::parse_env_as_bool; use lance_core::utils::tracing::{ IO_TYPE_OPEN_FRAG_REUSE, IO_TYPE_OPEN_MEM_WAL, IO_TYPE_OPEN_VECTOR, TRACE_IO_EVENTS, }; -use lance_file::previous::reader::FileReader as PreviousFileReader; use lance_file::reader::FileReaderOptions; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_index::INDEX_METADATA_SCHEMA_KEY; pub use lance_index::IndexParams; use lance_index::frag_reuse::{FRAG_REUSE_INDEX_NAME, FragReuseIndex, FragReuseIndexHandle}; @@ -2249,7 +2249,7 @@ impl DatasetIndexInternalExt for Dataset { (0, 2) => { info!(target: TRACE_IO_EVENTS, index_uuid=%uuid, r#type=IO_TYPE_OPEN_VECTOR, version="0.2", index_type="IVF_PQ"); - let reader = PreviousFileReader::try_new_self_described_from_reader( + let reader = V1FileReader::try_new_self_described_from_reader( reader.clone(), Some(&self.metadata_cache.file_metadata_cache(&index_file)), ) diff --git a/rust/lance/src/index/vector.rs b/rust/lance/src/index/vector.rs index bfc4a4f3474..7bfd718bd04 100644 --- a/rust/lance/src/index/vector.rs +++ b/rust/lance/src/index/vector.rs @@ -26,7 +26,7 @@ use datafusion::physical_plan::SendableRecordBatchStream; use datafusion::physical_plan::stream::RecordBatchStreamAdapter; use futures::stream; use lance_core::utils::tempfile::TempStdDir; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_index::frag_reuse::FragReuseIndex; use lance_index::metrics::NoOpMetricsCollector; use lance_index::optimize::OptimizeOptions; @@ -1711,7 +1711,7 @@ pub(crate) async fn open_vector_index_v2( dataset: Arc, column: &str, uuid: &Uuid, - reader: PreviousFileReader, + reader: V1FileReader, frag_reuse_index: Option>, ) -> Result> { let index_metadata = reader @@ -2825,7 +2825,8 @@ mod tests { .await .unwrap(); let arrow_schema = ArrowSchema::new(vec![Field::new("dummy", ArrowDataType::Int32, true)]); - let mut v2w = lance_file::writer::FileWriter::try_new( + let mut v2w = lance_file::versions::create_writer( + dataset_format_version(&dataset), writer, lance_core::datatypes::Schema::try_from(&arrow_schema).unwrap(), FileWriterOptions::default(), diff --git a/rust/lance/src/index/vector/builder.rs b/rust/lance/src/index/vector/builder.rs index f881d28e745..9b003563938 100644 --- a/rust/lance/src/index/vector/builder.rs +++ b/rust/lance/src/index/vector/builder.rs @@ -27,8 +27,9 @@ use lance_core::datatypes::Schema; use lance_core::utils::tempfile::TempStdDir; use lance_core::utils::tokio::{get_num_compute_intensive_cpus, spawn_cpu}; use lance_core::{Error, ROW_ID_FIELD, Result}; -use lance_encoding::version::LanceFileVersion; -use lance_file::writer::{FileWriter, FileWriterOptions}; +use lance_file::version::ConcreteFileVersion; +use lance_file::versions as file_versions; +use lance_file::writer::FileWriterOptions; use lance_index::frag_reuse::FragReuseIndex; use lance_index::metrics::NoOpMetricsCollector; use lance_index::optimize::OptimizeOptions; @@ -163,7 +164,7 @@ pub struct IvfIndexBuilder { transpose_codes: bool, // lance file version for writing index files - format_version: LanceFileVersion, + format_version: ConcreteFileVersion, progress: Arc, } @@ -1134,23 +1135,22 @@ impl IvfIndexBuilder let storage_path = self.index_dir.clone().join(INDEX_AUXILIARY_FILE_NAME); let index_path = self.index_dir.clone().join(INDEX_FILE_NAME); - let writer_options = FileWriterOptions { - format_version: Some(self.format_version), - ..Default::default() - }; + let writer_options = FileWriterOptions::default(); let mut storage_writer = if is_flat { None } else { let mut fields = vec![ROW_ID_FIELD.clone(), quantizer.field()]; fields.extend(quantizer.extra_fields()); let storage_schema: Schema = (&arrow_schema::Schema::new(fields)).try_into()?; - Some(FileWriter::try_new( + Some(file_versions::create_writer( + self.format_version, self.store.create(&storage_path).await?, storage_schema, writer_options.clone(), )?) }; - let mut index_writer = FileWriter::try_new( + let mut index_writer = file_versions::create_writer( + self.format_version, self.store.create(&index_path).await?, S::schema().as_ref().try_into()?, writer_options.clone(), @@ -1218,7 +1218,8 @@ impl IvfIndexBuilder if storage_writer.is_none() { let storage_schema: Schema = batch.schema_ref().as_ref().try_into()?; - storage_writer = Some(FileWriter::try_new( + storage_writer = Some(file_versions::create_writer( + self.format_version, self.store.create(&storage_path).await?, storage_schema, writer_options.clone(), @@ -1286,7 +1287,8 @@ impl IvfIndexBuilder ), ]); let storage_schema: Schema = (&flat_schema).try_into()?; - storage_writer = Some(FileWriter::try_new( + storage_writer = Some(file_versions::create_writer( + self.format_version, self.store.create(&storage_path).await?, storage_schema, writer_options.clone(), diff --git a/rust/lance/src/index/vector/ivf.rs b/rust/lance/src/index/vector/ivf.rs index 1d56300fc4b..0e72dd33d4f 100644 --- a/rust/lance/src/index/vector/ivf.rs +++ b/rust/lance/src/index/vector/ivf.rs @@ -19,7 +19,11 @@ use crate::index::vector::open_index_file; use crate::index::vector::utils::{get_vector_dim, get_vector_type}; use crate::{ dataset::Dataset, - index::{INDEX_FILE_NAME, pb, prefilter::PreFilter, vector::ivf::io::write_pq_partitions}, + index::{ + INDEX_FILE_NAME, pb, + prefilter::PreFilter, + vector::ivf::io::{write_pq_partition_payload, write_pq_partitions}, + }, }; use crate::{dataset::builder::DatasetBuilder, index::vector::IndexFileVersion}; use arrow::array::ArrayData; @@ -55,10 +59,9 @@ use lance_core::{ use lance_encoding::decoder::FilterExpression; use lance_file::{ format::MAGIC, - previous::writer::{ - FileWriter as PreviousFileWriter, FileWriterOptions as PreviousFileWriterOptions, - }, reader::{FileReader as V2Reader, FileReaderOptions as V2ReaderOptions}, + versions as file_versions, + versions::v1::writer::{FileWriter as V1FileWriter, FileWriterOptions as V1FileWriterOptions}, writer::{FileWriter as V2Writer, FileWriterOptions as V2WriterOptions}, }; use lance_index::metrics::MetricsCollector; @@ -102,7 +105,6 @@ use lance_io::scheduler::{ScanScheduler, SchedulerConfig}; use lance_io::utils::CachedFileSize; use lance_io::{ ReadBatchParams, - encodings::plain::PlainEncoder, local::to_local_path, object_store::ObjectStore, stream::RecordBatchStream, @@ -971,11 +973,8 @@ async fn optimize_ivf_hnsw_indices( // Prepare the HNSW writer let schema = lance_core::datatypes::Schema::try_from(HNSW::schema().as_ref())?; - let mut writer = PreviousFileWriter::with_object_writer( - writer, - schema, - &PreviousFileWriterOptions::default(), - )?; + let mut writer = + V1FileWriter::with_object_writer(writer, schema, &V1FileWriterOptions::default())?; writer.add_metadata( INDEX_METADATA_SCHEMA_KEY, json!(IndexMetadata { @@ -999,11 +998,8 @@ async fn optimize_ivf_hnsw_indices( ), ]); let schema = lance_core::datatypes::Schema::try_from(&schema)?; - let mut aux_writer = PreviousFileWriter::with_object_writer( - aux_writer, - schema, - &PreviousFileWriterOptions::default(), - )?; + let mut aux_writer = + V1FileWriter::with_object_writer(aux_writer, schema, &V1FileWriterOptions::default())?; aux_writer.add_metadata( INDEX_METADATA_SCHEMA_KEY, json!(IndexMetadata { @@ -1845,8 +1841,12 @@ impl RemapPageTask { page.pq.code_dim(), page.row_ids.as_ref().unwrap().len(), ); - PlainEncoder::write(writer, &[&original_pq]).await?; - PlainEncoder::write(writer, &[page.row_ids.as_ref().unwrap().as_ref()]).await?; + write_pq_partition_payload( + writer, + &[&original_pq], + &[page.row_ids.as_ref().unwrap().as_ref()], + ) + .await?; Ok(()) } } @@ -2165,11 +2165,8 @@ async fn write_ivf_hnsw_file( let writer = object_store.create(&path).await?; let schema = lance_core::datatypes::Schema::try_from(HNSW::schema().as_ref())?; - let mut writer = PreviousFileWriter::with_object_writer( - writer, - schema, - &PreviousFileWriterOptions::default(), - )?; + let mut writer = + V1FileWriter::with_object_writer(writer, schema, &V1FileWriterOptions::default())?; writer.add_metadata( INDEX_METADATA_SCHEMA_KEY, json!(IndexMetadata { @@ -2197,11 +2194,8 @@ async fn write_ivf_hnsw_file( ), ]); let schema = lance_core::datatypes::Schema::try_from(&schema)?; - let mut aux_writer = PreviousFileWriter::with_object_writer( - aux_writer, - schema, - &PreviousFileWriterOptions::default(), - )?; + let mut aux_writer = + V1FileWriter::with_object_writer(aux_writer, schema, &V1FileWriterOptions::default())?; aux_writer.add_metadata( INDEX_METADATA_SCHEMA_KEY, json!(IndexMetadata { @@ -2542,13 +2536,11 @@ async fn write_root_vector_index_from_auxiliary( // Schema for HNSW sub-index: include neighbors/dist fields; empty batch is fine. let arrow_schema = HNSW::schema(); let schema = lance_core::datatypes::Schema::try_from(arrow_schema.as_ref())?; - let mut v2_writer = V2Writer::try_new( + let mut v2_writer = file_versions::create_writer( + format_version, obj_writer, schema, - V2WriterOptions { - format_version: Some(format_version), - ..Default::default() - }, + V2WriterOptions::default(), )?; // For HNSW variants, attach per-partition metadata list; for FLAT-based diff --git a/rust/lance/src/index/vector/ivf/builder.rs b/rust/lance/src/index/vector/ivf/builder.rs index 9bd1ba95803..d1961071315 100644 --- a/rust/lance/src/index/vector/ivf/builder.rs +++ b/rust/lance/src/index/vector/ivf/builder.rs @@ -13,7 +13,8 @@ use futures::{StreamExt, TryStreamExt}; use lance_arrow::{RecordBatchExt, SchemaExt}; use lance_core::utils::address::RowAddress; use lance_core::utils::tokio::{get_num_compute_intensive_cpus, spawn_cpu}; -use lance_file::previous::writer::FileWriter as PreviousFileWriter; +use lance_file::versions as file_versions; +use lance_file::versions::v1::writer::FileWriter as V1FileWriter; use lance_file::writer::FileWriterOptions; use lance_index::vector::PART_ID_COLUMN; use lance_index::vector::pq::ProductQuantizer; @@ -35,6 +36,7 @@ use lance_linalg::distance::{DistanceType, MetricType}; use crate::Dataset; use crate::dataset::builder::DatasetBuilder; +use crate::dataset::index::dataset_format_version; use crate::index::vector::ivf::io::write_pq_partitions; use super::io::write_hnsw_quantization_index_partitions; @@ -217,7 +219,11 @@ pub async fn write_vector_storage( data.boxed() }; - let mut writer = lance_file::writer::FileWriter::new_lazy(writer, FileWriterOptions::default()); + let mut writer = file_versions::create_lazy_writer( + dataset_format_version(dataset), + writer, + FileWriterOptions::default(), + )?; let mut transformed_stream = data .map_ok(move |batch| { let ivf_transformer = ivf_transformer.clone(); @@ -242,8 +248,8 @@ pub async fn write_vector_storage( #[instrument(level = "debug", skip(writer, auxiliary_writer, data, ivf, quantizer))] pub(super) async fn build_hnsw_partitions( dataset: Arc, - writer: &mut PreviousFileWriter, - auxiliary_writer: Option<&mut PreviousFileWriter>, + writer: &mut V1FileWriter, + auxiliary_writer: Option<&mut V1FileWriter>, data: impl RecordBatchStream + Unpin + 'static, column: &str, ivf: &mut IvfModel, diff --git a/rust/lance/src/index/vector/ivf/io.rs b/rust/lance/src/index/vector/ivf/io.rs index a49398a0750..3a0bef953a6 100644 --- a/rust/lance/src/index/vector/ivf/io.rs +++ b/rust/lance/src/index/vector/ivf/io.rs @@ -12,7 +12,9 @@ use crate::index::vector::pq::{PQIndex, build_pq_storage}; use arrow::compute::concat; use arrow_array::UInt64Array; use arrow_array::{ - Array, FixedSizeListArray, RecordBatch, UInt32Array, cast::AsArray, types::UInt64Type, + Array, FixedSizeListArray, PrimitiveArray, RecordBatch, UInt32Array, + cast::AsArray, + types::{ArrowPrimitiveType, UInt8Type, UInt64Type}, }; use futures::stream::Peekable; use futures::{Stream, StreamExt, TryStreamExt}; @@ -22,8 +24,8 @@ use lance_core::datatypes::Schema; use lance_core::traits::DatasetTakeRows; use lance_core::utils::tempfile::TempStdDir; use lance_core::utils::tokio::{get_num_compute_intensive_cpus, spawn_cpu}; -use lance_file::previous::reader::FileReader as PreviousFileReader; -use lance_file::previous::writer::FileWriter as PreviousFileWriter; +use lance_file::versions::v1::reader::FileReader as V1FileReader; +use lance_file::versions::v1::writer::FileWriter as V1FileWriter; use lance_index::metrics::NoOpMetricsCollector; use lance_index::vector::hnsw::HNSW; use lance_index::vector::hnsw::{HnswMetadata, builder::HnswBuildParams}; @@ -34,7 +36,6 @@ use lance_index::vector::quantizer::{Quantization, Quantizer}; use lance_index::vector::v3::subindex::IvfSubIndex; use lance_index::vector::{PART_ID_COLUMN, PQ_CODE_COLUMN}; use lance_io::ReadBatchParams; -use lance_io::encodings::plain::PlainEncoder; use lance_io::object_store::ObjectStore; use lance_io::traits::Writer; use lance_linalg::distance::{DistanceType, MetricType}; @@ -42,6 +43,7 @@ use lance_linalg::kernels::normalize_fsl; use lance_table::format::SelfDescribingFileReader; use lance_table::io::manifest::ManifestDescribing; use object_store::path::Path; +use tokio::io::AsyncWriteExt; use tokio::sync::Semaphore; use tokio::task::JoinHandle; @@ -51,6 +53,73 @@ use crate::Result; static HNSW_PARTITIONS_BUILD_PARALLEL: LazyLock = LazyLock::new(get_num_compute_intensive_cpus); +async fn write_primitive_values( + writer: &mut dyn Writer, + array: &PrimitiveArray, +) -> Result<()> { + let data = array.to_data(); + let byte_width = std::mem::size_of::(); + let start = array.offset() * byte_width; + let end = start + array.len() * byte_width; + writer + .write_all(&data.buffers()[0].as_slice()[start..end]) + .await?; + Ok(()) +} + +async fn write_pq_codes(writer: &mut dyn Writer, arrays: &[&dyn Array]) -> Result<()> { + for array in arrays { + let codes = array + .as_any() + .downcast_ref::() + .ok_or_else(|| { + Error::index(format!( + "legacy IVF PQ codes must be FixedSizeList, found {}", + array.data_type() + )) + })?; + let value_offset = codes.value_offset(0) as usize; + let value_len = codes.len() * codes.value_length() as usize; + let values = codes.values().slice(value_offset, value_len); + let values = values + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + Error::index(format!( + "legacy IVF PQ code values must be UInt8, found {}", + values.data_type() + )) + })?; + write_primitive_values(writer, values).await?; + } + Ok(()) +} + +async fn write_row_ids(writer: &mut dyn Writer, arrays: &[&dyn Array]) -> Result<()> { + for array in arrays { + let row_ids = array + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + Error::index(format!( + "legacy IVF row ids must be UInt64, found {}", + array.data_type() + )) + })?; + write_primitive_values(writer, row_ids).await?; + } + Ok(()) +} + +pub(super) async fn write_pq_partition_payload( + writer: &mut dyn Writer, + pq_codes: &[&dyn Array], + row_ids: &[&dyn Array], +) -> Result<()> { + write_pq_codes(writer, pq_codes).await?; + write_row_ids(writer, row_ids).await +} + /// Merge streams with the same partition id and collect PQ codes and row IDs. async fn merge_streams( streams_heap: &mut BinaryHeap<(Reverse, usize)>, @@ -222,10 +291,8 @@ pub(super) async fn write_pq_partitions( ivf.add_partition_with_offset(writer.tell().await?, total_records as u32); if total_records > 0 { let pq_refs = pq_array.iter().map(|a| a.as_ref()).collect::>(); - PlainEncoder::write(writer, &pq_refs).await?; - let row_ids_refs = row_id_array.iter().map(|a| a.as_ref()).collect::>(); - PlainEncoder::write(writer, row_ids_refs.as_slice()).await?; + write_pq_partition_payload(writer, &pq_refs, &row_ids_refs).await?; } log::info!( "Wrote partition {} in {} ms", @@ -242,8 +309,8 @@ pub(super) async fn write_hnsw_quantization_index_partitions( column: &str, distance_type: DistanceType, hnsw_params: &HnswBuildParams, - writer: &mut PreviousFileWriter, - mut auxiliary_writer: Option<&mut PreviousFileWriter>, + writer: &mut V1FileWriter, + mut auxiliary_writer: Option<&mut V1FileWriter>, ivf: &mut IvfModel, quantizer: Quantizer, streams: Option>>>, @@ -338,7 +405,7 @@ pub(super) async fn write_hnsw_quantization_index_partitions( } let (part_file, aux_part_file) = (&part_files[part_id], &aux_part_files[part_id]); - let part_writer = PreviousFileWriter::::try_new( + let part_writer = V1FileWriter::::try_new( &object_store, part_file, Schema::try_from(writer.schema())?, @@ -348,7 +415,7 @@ pub(super) async fn write_hnsw_quantization_index_partitions( let aux_part_writer = match auxiliary_writer.as_ref() { Some(writer) => Some( - PreviousFileWriter::::try_new( + V1FileWriter::::try_new( &object_store, aux_part_file, Schema::try_from(writer.schema())?, @@ -410,7 +477,7 @@ pub(super) async fn write_hnsw_quantization_index_partitions( let (part_file, aux_part_file) = (&part_files[part_id], &aux_part_files[part_id]); let part_reader = - PreviousFileReader::try_new_self_described(&object_store, part_file, None).await?; + V1FileReader::try_new_self_described(&object_store, part_file, None).await?; let batches = futures::stream::iter(0..part_reader.num_batches()) .map(|batch_id| { @@ -434,7 +501,7 @@ pub(super) async fn write_hnsw_quantization_index_partitions( if let Some(aux_writer) = auxiliary_writer.as_mut() { let aux_part_reader = - PreviousFileReader::try_new_self_described(&object_store, aux_part_file, None) + V1FileReader::try_new_self_described(&object_store, aux_part_file, None) .await?; let batches = futures::stream::iter(0..aux_part_reader.num_batches()) @@ -514,8 +581,8 @@ async fn build_hnsw_quantization_partition( column: &str, metric_type: MetricType, hnsw_params: Arc, - writer: PreviousFileWriter, - aux_writer: Option>, + writer: V1FileWriter, + aux_writer: Option>, quantizer: Quantizer, row_ids_array: Vec>, code_array: Vec>, @@ -579,7 +646,7 @@ async fn build_and_write_hnsw( vectors: Arc, params: HnswBuildParams, distance_type: DistanceType, - mut writer: PreviousFileWriter, + mut writer: V1FileWriter, ) -> Result { let batch = params.build(vectors, distance_type).await?.to_batch()?; let metadata = batch.schema_ref().metadata().clone(); @@ -592,7 +659,7 @@ async fn build_and_write_pq_storage( row_ids: Arc, code_array: Vec>, pq: ProductQuantizer, - mut writer: PreviousFileWriter, + mut writer: V1FileWriter, ) -> Result<()> { let storage = spawn_cpu(move || build_pq_storage(metric_type, row_ids, code_array, pq)).await?; @@ -611,15 +678,42 @@ mod tests { use crate::Dataset; use crate::index::vector::ivf::v2; use crate::index::{DatasetIndexExt, DatasetIndexInternalExt, vector::VectorIndexParams}; - use arrow_array::RecordBatchIterator; + use arrow_array::{RecordBatchIterator, UInt8Array}; use arrow_schema::{Field, Schema}; - use lance_core::utils::tempfile::TempStrDir; + use lance_core::utils::tempfile::{TempObjFile, TempStrDir}; use lance_index::IndexType; use lance_index::metrics::NoOpMetricsCollector; use lance_index::vector::ivf::IvfBuildParams; use lance_index::vector::pq::PQBuildParams; use lance_testing::datagen::generate_random_array; + #[tokio::test] + async fn pq_partition_payload_preserves_sliced_values() { + let codes = + FixedSizeListArray::try_new_from_values(UInt8Array::from_iter_values(0..6), 2).unwrap(); + let codes = codes.slice(1, 2); + let row_ids = UInt64Array::from_iter_values([10, 11, 12]).slice(1, 2); + + let path = TempObjFile::default(); + let object_store = ObjectStore::local(); + let mut writer = object_store.create(&path).await.unwrap(); + write_pq_partition_payload( + writer.as_mut(), + &[&codes as &dyn Array], + &[&row_ids as &dyn Array], + ) + .await + .unwrap(); + Writer::shutdown(&mut writer).await.unwrap(); + + let reader = object_store.open(&path).await.unwrap(); + let bytes = reader.get_range(0..20).await.unwrap(); + let mut expected = vec![2, 3, 4, 5]; + expected.extend_from_slice(&11_u64.to_le_bytes()); + expected.extend_from_slice(&12_u64.to_le_bytes()); + assert_eq!(bytes.as_ref(), expected); + } + #[tokio::test] async fn test_merge_multiple_indices() { const DIM: usize = 32; diff --git a/rust/lance/src/index/vector/ivf/v2.rs b/rust/lance/src/index/vector/ivf/v2.rs index c8d8a15de55..835e43c7336 100644 --- a/rust/lance/src/index/vector/ivf/v2.rs +++ b/rust/lance/src/index/vector/ivf/v2.rs @@ -2070,7 +2070,6 @@ mod tests { use lance_core::{ROW_ID, Result}; use lance_encoding::decoder::DecoderPlugins; use lance_file::reader::{FileReader, FileReaderOptions}; - use lance_file::writer::FileWriter; use lance_index::IndexType; use lance_index::optimize::OptimizeOptions; use lance_index::progress::IndexBuildProgress; @@ -5295,7 +5294,7 @@ mod tests { // Rewrite auxiliary file with PQ codebook inlined into schema metadata. let mut metadata = reader.schema().metadata.clone(); - let projection = lance_file::reader::ReaderProjection::from_whole_schema( + let projection = lance_file::versions::reader_projection_from_whole_schema( reader.schema(), reader.metadata().version(), ); @@ -5312,7 +5311,8 @@ mod tests { let batches = batches.try_collect::>().await?; let batch = arrow::compute::concat_batches(&batches[0].schema(), &batches)?; let new_aux_path = new_dir.clone().join(INDEX_AUXILIARY_FILE_NAME); - let mut writer = FileWriter::try_new( + let mut writer = lance_file::versions::create_writer( + reader.metadata().version(), obj_store.create(&new_aux_path).await?, batch.schema_ref().as_ref().try_into()?, Default::default(), diff --git a/rust/lance/src/index/vector/pq.rs b/rust/lance/src/index/vector/pq.rs index 217667afbe9..23a5fe1631d 100644 --- a/rust/lance/src/index/vector/pq.rs +++ b/rust/lance/src/index/vector/pq.rs @@ -18,7 +18,7 @@ use arrow_select::take::take; use async_trait::async_trait; use datafusion::execution::SendableRecordBatchStream; use datafusion::physical_plan::stream::RecordBatchStreamAdapter; -use lance_arrow::FixedSizeListArrayExt; +use lance_arrow::{DataTypeExt, FixedSizeListArrayExt, array_from_fixed_width_bytes}; use lance_core::deepsize::DeepSizeOf; use lance_core::utils::address::RowAddress; use lance_core::utils::tokio::spawn_cpu; @@ -33,7 +33,7 @@ use lance_index::{ Index, IndexType, vector::{Query, pq::ProductQuantizer}, }; -use lance_io::{traits::Reader, utils::read_fixed_stride_array}; +use lance_io::traits::Reader; use lance_linalg::distance::{DistanceType, MetricType}; use log::{info, warn}; use roaring::RoaringBitmap; @@ -71,6 +71,22 @@ pub struct PQIndex { frag_reuse_index: Option>, } +async fn read_legacy_index_values( + reader: &dyn Reader, + data_type: &DataType, + offset: usize, + length: usize, +) -> Result { + let byte_length = length + .checked_mul(data_type.byte_width()) + .ok_or_else(|| Error::index("legacy IVF page byte length overflow".to_string()))?; + let end = offset + .checked_add(byte_length) + .ok_or_else(|| Error::index("legacy IVF page offset overflow".to_string()))?; + let bytes = reader.get_range(offset..end).await?; + Ok(array_from_fixed_width_bytes(data_type, bytes, length, 0)?) +} + impl DeepSizeOf for PQIndex { fn deep_size_of_children(&self, context: &mut lance_core::deepsize::Context) -> usize { self.pq.deep_size_of_children(context) @@ -331,24 +347,14 @@ impl VectorIndex for PQIndex { length: usize, ) -> Result> { let pq_code_length = self.pq.code_dim() * length; - let pq_codes = read_fixed_stride_array( - reader.as_ref(), - &DataType::UInt8, - offset, - pq_code_length, - .., - ) - .await?; + let pq_codes = + read_legacy_index_values(reader.as_ref(), &DataType::UInt8, offset, pq_code_length) + .await?; let row_id_offset = offset + pq_code_length /* *1 */; - let row_ids = read_fixed_stride_array( - reader.as_ref(), - &DataType::UInt64, - row_id_offset, - length, - .., - ) - .await?; + let row_ids = + read_legacy_index_values(reader.as_ref(), &DataType::UInt64, row_id_offset, length) + .await?; let pq_codes = transpose( pq_codes.as_primitive(), diff --git a/rust/lance/src/io/commit.rs b/rust/lance/src/io/commit.rs index 2441e31749d..e9db581c0e7 100644 --- a/rust/lance/src/io/commit.rs +++ b/rust/lance/src/io/commit.rs @@ -26,13 +26,14 @@ use std::time::Instant; use conflict_resolver::TransactionRebase; use lance_core::utils::backoff::{Backoff, SlotBackoff}; +#[cfg(test)] use lance_file::version::LanceFileVersion; use lance_index::metrics::NoOpMetricsCollector; use lance_io::utils::CachedFileSize; use lance_select::RowAddrTreeMap; use lance_table::format::{ - DETACHED_VERSION_MASK, DataStorageFormat, DeletionFile, Fragment, IndexMetadata, Manifest, - WriterVersion, is_detached_version, list_index_files_with_sizes, pb, + DETACHED_VERSION_MASK, DeletionFile, Fragment, IndexMetadata, Manifest, WriterVersion, + is_detached_version, list_index_files_with_sizes, pb, }; use lance_table::io::commit::{ CommitConfig, CommitError, CommitHandler, ManifestLocation, ManifestNamingScheme, @@ -375,98 +376,11 @@ async fn migrate_manifest( } fn check_storage_version(manifest: &mut Manifest) -> Result<()> { - let data_storage_version = manifest.data_storage_format.lance_file_version()?; - if manifest.data_storage_format.lance_file_version()? == LanceFileVersion::Legacy { - // Due to bugs in 0.16 it is possible the dataset's data storage version does not - // match the file version. As a result, we need to check and see if they are out - // of sync. - if let Some(actual_file_version) = - Fragment::try_infer_version(&manifest.fragments).map_err(|e| Error::internal(format!( - "The dataset contains a mixture of file versions. You will need to rollback to an earlier version: {}", - e - )))? - && actual_file_version > data_storage_version { - log::warn!( - "Data storage version {} is less than the actual file version {}. This has been automatically updated.", - data_storage_version, - actual_file_version - ); - manifest.data_storage_format = DataStorageFormat::new(actual_file_version); - } - } else { - // Otherwise, if we are on 2.0 or greater, we should ensure that the file versions - // match the data storage version. This is a sanity assertion to prevent data corruption. - if let Some(actual_file_version) = Fragment::try_infer_version(&manifest.fragments)? - && actual_file_version != data_storage_version - { - return Err(Error::internal(format!( - "The operation added files with version {}. However, the data storage version is {}.", - actual_file_version, data_storage_version - ))); - } - } - Ok(()) + crate::dataset::versions::check_manifest_storage_version(manifest) } fn check_column_indices(manifest: &Manifest) -> Result<()> { - let data_storage_version = manifest.data_storage_format.lance_file_version()?; - if data_storage_version < LanceFileVersion::V2_1 { - return Ok(()); - } - - for fragment in manifest.fragments.iter() { - for data_file in &fragment.files { - if data_file.is_legacy_file() || data_file.column_indices.is_empty() { - continue; - } - if data_file.fields.len() != data_file.column_indices.len() { - return Err(Error::invalid_input(format!( - "Data file '{}' (fragment {}) has {} field ids but {} column indices. \ - These must be the same length.", - data_file.path, - fragment.id, - data_file.fields.len(), - data_file.column_indices.len() - ))); - } - let file_version = LanceFileVersion::try_from_major_minor( - data_file.file_major_version, - data_file.file_minor_version, - )?; - if file_version < LanceFileVersion::V2_1 { - continue; - } - for (field_id, column_index) in - data_file.fields.iter().zip(data_file.column_indices.iter()) - { - // Field ids may not exist in the current schema after schema - // evolution (e.g. cast/drop column). Skip those. - let Some(field) = manifest.schema.field_by_id(*field_id) else { - continue; - }; - let needs_column = field.is_leaf() || field.is_packed_struct() || field.is_blob(); - if needs_column && *column_index == -1 { - return Err(Error::invalid_input(format!( - "Field '{}' (id={}) in data file '{}' (fragment {}) \ - has column_index=-1, but leaf fields, packed structs, \ - and blob fields must have a valid column index in \ - file format 2.1+.", - field.name, field_id, data_file.path, fragment.id - ))); - } - if !needs_column && *column_index != -1 { - return Err(Error::invalid_input(format!( - "Non-leaf field '{}' (id={}) in data file '{}' (fragment {}) \ - has column_index={}, but non-leaf fields should have \ - column_index=-1 in file format 2.1+. Only leaf fields, \ - packed structs, and blob fields should have column indices.", - field.name, field_id, data_file.path, fragment.id, column_index - ))); - } - } - } - } - Ok(()) + crate::dataset::versions::validate_column_indices(manifest) } /// Fix schema in case of duplicate field ids. @@ -1157,6 +1071,7 @@ mod tests { use lance_core::datatypes::{Field, Schema}; use lance_core::utils::tempfile::TempStrDir; use lance_datagen::{BatchCount, RowCount, array, gen_batch}; + use lance_file::version::ConcreteFileVersion; use lance_index::IndexType; use lance_linalg::distance::MetricType; use lance_table::format::{DataFile, DataStorageFormat}; @@ -1863,7 +1778,7 @@ mod tests { Manifest::new( schema, Arc::new(vec![fragment]), - DataStorageFormat::new(data_storage_version), + DataStorageFormat::new(data_storage_version.resolve()), HashMap::new(), ) } @@ -1885,7 +1800,14 @@ mod tests { }; // field ids: struct=0, leaf=1; give struct a real column_index (wrong) - let data_file = DataFile::new("data.lance", vec![0, 1], vec![0, 1], 2, 1, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0, 1], + vec![0, 1], + ConcreteFileVersion::V2_1, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_1); let result = check_column_indices(&manifest); assert!( @@ -1913,7 +1835,14 @@ mod tests { }; // field ids: list=0, item=1; give list a real column_index (wrong) - let data_file = DataFile::new("data.lance", vec![0, 1], vec![0, 1], 2, 1, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0, 1], + vec![0, 1], + ConcreteFileVersion::V2_1, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_1); let result = check_column_indices(&manifest); assert!( @@ -1941,7 +1870,14 @@ mod tests { }; // struct=-1 (correct), leaf=0 (correct) - let data_file = DataFile::new("data.lance", vec![0, 1], vec![-1, 0], 2, 1, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0, 1], + vec![-1, 0], + ConcreteFileVersion::V2_1, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_1); assert!(check_column_indices(&manifest).is_ok()); } @@ -1966,7 +1902,14 @@ mod tests { }; // packed struct=0 (allowed), leaf=1 - let data_file = DataFile::new("data.lance", vec![0, 1], vec![0, 1], 2, 1, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0, 1], + vec![0, 1], + ConcreteFileVersion::V2_1, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_1); assert!(check_column_indices(&manifest).is_ok()); } @@ -1987,7 +1930,14 @@ mod tests { metadata: Default::default(), }; - let data_file = DataFile::new("data.lance", vec![0, 1], vec![0, 1], 2, 0, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0, 1], + vec![0, 1], + ConcreteFileVersion::V2_0, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_0); assert!(check_column_indices(&manifest).is_ok()); } @@ -2004,7 +1954,14 @@ mod tests { }; // 1 field id but 2 column indices - let data_file = DataFile::new("data.lance", vec![0], vec![0, 1], 2, 1, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0], + vec![0, 1], + ConcreteFileVersion::V2_1, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_1); let result = check_column_indices(&manifest); assert!(result.is_err(), "Expected error for mismatched lengths"); @@ -2024,7 +1981,14 @@ mod tests { }; // field id 99 does not exist in the schema — should be skipped - let data_file = DataFile::new("data.lance", vec![0, 99], vec![0, 1], 2, 1, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0, 99], + vec![0, 1], + ConcreteFileVersion::V2_1, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_1); assert!(check_column_indices(&manifest).is_ok()); } @@ -2046,7 +2010,14 @@ mod tests { }; // struct=-1 (correct), but leaf=-1 (wrong — leaf must have a real column) - let data_file = DataFile::new("data.lance", vec![0, 1], vec![-1, -1], 2, 1, None, None); + let data_file = DataFile::new( + "data.lance", + vec![0, 1], + vec![-1, -1], + ConcreteFileVersion::V2_1, + None, + None, + ); let manifest = make_manifest_with_file(schema, data_file, LanceFileVersion::V2_1); let result = check_column_indices(&manifest); assert!( diff --git a/rust/lance/src/io/commit/conflict_resolver.rs b/rust/lance/src/io/commit/conflict_resolver.rs index 7be9c37230e..57277ddabe7 100644 --- a/rust/lance/src/io/commit/conflict_resolver.rs +++ b/rust/lance/src/io/commit/conflict_resolver.rs @@ -2336,7 +2336,7 @@ mod tests { "path1", vec![0], vec![0], - &LanceFileVersion::Stable, + LanceFileVersion::Stable.resolve(), NonZero::new(10), ) .with_physical_rows(3); @@ -2479,7 +2479,7 @@ mod tests { "path1", vec![0], vec![0], - &LanceFileVersion::Stable, + LanceFileVersion::Stable.resolve(), NonZero::new(10), ) .with_physical_rows(3); @@ -3484,7 +3484,7 @@ mod tests { "moved.lance", vec![0], vec![0], - &LanceFileVersion::Stable, + LanceFileVersion::Stable.resolve(), NonZero::new(10), ) .with_physical_rows(1); diff --git a/rust/lance/src/io/exec.rs b/rust/lance/src/io/exec.rs index a477d60d56d..6923f09c34f 100644 --- a/rust/lance/src/io/exec.rs +++ b/rust/lance/src/io/exec.rs @@ -36,6 +36,7 @@ pub use optimizer::get_physical_optimizer; pub use projection::project; pub use pushdown_scan::{LancePushdownScanExec, ScanConfig}; pub use rowids::{AddRowAddrExec, AddRowOffsetExec}; +pub(crate) use scan::LanceStream; pub use scan::{LanceScanConfig, LanceScanExec}; pub use take::TakeExec; pub use utils::PreFilterSource; diff --git a/rust/lance/src/io/exec/filtered_read.rs b/rust/lance/src/io/exec/filtered_read.rs index dddf18df4bd..4c9dc743f3d 100644 --- a/rust/lance/src/io/exec/filtered_read.rs +++ b/rust/lance/src/io/exec/filtered_read.rs @@ -64,6 +64,7 @@ use crate::dataset::scanner::{ BATCH_SIZE_FALLBACK, DEFAULT_FRAGMENT_READAHEAD, get_default_batch_size, get_default_io_buffer_size_override, }; +use crate::dataset::versions; use super::utils::IoMetrics; @@ -1898,13 +1899,9 @@ impl FilteredReadExec { options: FilteredReadOptions, input: Arc, ) -> Result { - if dataset.is_legacy_storage() { - return Err(Error::not_supported_source( - "taking rows through FilteredReadExec requires the v2 storage format" - .to_string() - .into(), - )); - } + versions::validate_row_stream_read( + dataset.manifest().data_storage_format.lance_file_format(), + )?; if options.refine_filter.is_some() || options.full_filter.is_some() { return Err(Error::invalid_input_source( "filters are not supported when taking rows from an input plan".into(), diff --git a/rust/lance/src/io/exec/pushdown_scan.rs b/rust/lance/src/io/exec/pushdown_scan.rs index b82434116b8..3d72c3291a3 100644 --- a/rust/lance/src/io/exec/pushdown_scan.rs +++ b/rust/lance/src/io/exec/pushdown_scan.rs @@ -41,7 +41,8 @@ use crate::{ Dataset, dataset::{ ROW_ID, - fragment::{FileFragment, FragmentReader}, + fragment::{FileFragment, V1FragmentReader}, + versions, }, datatypes::Schema, }; @@ -280,7 +281,7 @@ struct FragmentScanner { predicate_projection: Arc, predicate: Expr, config: ScanConfig, - reader: FragmentReader, + reader: V1FragmentReader, stats: Option, } @@ -301,15 +302,14 @@ impl FragmentScanner { if let Some(file_reader_options) = config.file_reader_options.clone() { frag_config = frag_config.with_file_reader_options(file_reader_options); } - let mut reader = fragment.open(dataset.schema(), frag_config).await?; + let mut reader = + versions::open_v1_fragment_reader(&fragment, dataset.schema(), &frag_config).await?; if config.make_deletions_null { reader.with_make_deletions_null(); } // We only need the statistics for the predicate projection. - let stats = reader - .legacy_read_page_stats(Some(&predicate_projection)) - .await?; + let stats = reader.read_page_stats(Some(&predicate_projection)).await?; Ok(Self { fragment, @@ -378,7 +378,7 @@ impl FragmentScanner { projection_reader.with_row_address(); } let batch = projection_reader - .legacy_read_batch_projected(batch_id, .., &self.projection) + .read_batch_projected(batch_id, .., &self.projection) .await?; let batch = self.final_projection(batch)?; Ok(Some(batch)) @@ -411,7 +411,7 @@ impl FragmentScanner { reader.with_row_address(); let batch = reader - .legacy_read_batch_projected(batch_id, .., &predicate_projection) + .read_batch_projected(batch_id, .., &predicate_projection) .await?; // 2. Evaluate predicate @@ -485,7 +485,7 @@ impl FragmentScanner { self.projection.project_by_ids(&remaining_fields, true); Some( self.reader - .legacy_read_batch_projected( + .read_batch_projected( batch_id, selection.clone(), &remaining_projection, @@ -655,13 +655,13 @@ impl FragmentScanner { } fn simplified_predicates(&self) -> Result> { - let num_batches = self.reader.legacy_num_batches(); + let num_batches = self.reader.num_batches(); if let Some(stats) = &self.stats { let batch_sizes: Vec = (0..num_batches as u32) .map(|batch_id| { self.reader - .legacy_num_rows_in_batch(batch_id) + .num_rows_in_batch(batch_id) .expect("Operation does not yet support v2 fragments") as usize }) diff --git a/rust/lance/src/io/exec/scan.rs b/rust/lance/src/io/exec/scan.rs index a8c3b2e3dc3..f250245baec 100644 --- a/rust/lance/src/io/exec/scan.rs +++ b/rust/lance/src/io/exec/scan.rs @@ -168,18 +168,10 @@ impl LanceStream { metrics: &ExecutionPlanMetricsSet, partition: usize, ) -> Result { - let is_v2_scan = fragments - .iter() - .filter_map(|frag| frag.files.first().map(|f| !f.is_legacy_file())) - .next() - .unwrap_or(false); - if is_v2_scan { - Self::try_new_v2( - dataset, fragments, offsets, projection, config, metrics, partition, - ) - } else { - Self::try_new_v1(dataset, fragments, projection, config, metrics, partition) - } + let version = dataset.manifest().data_storage_format.lance_file_format(); + crate::dataset::versions::create_scan_stream( + version, dataset, fragments, offsets, projection, config, metrics, partition, + ) } #[allow(clippy::too_many_arguments)] @@ -416,6 +408,7 @@ impl LanceStream { pub fn try_new_v1( dataset: Arc, fragments: Arc>, + _offsets: Option>, projection: Arc, config: LanceScanConfig, metrics: &ExecutionPlanMetricsSet, diff --git a/rust/lance/src/session/index_extension.rs b/rust/lance/src/session/index_extension.rs index 0f4d2ecf310..c93ae3269ef 100644 --- a/rust/lance/src/session/index_extension.rs +++ b/rust/lance/src/session/index_extension.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use lance_core::Result; use lance_core::deepsize::DeepSizeOf; -use lance_file::previous::reader::FileReader as PreviousFileReader; +use lance_file::versions::v1::reader::FileReader as V1FileReader; use lance_index::{IndexParams, IndexType, vector::VectorIndex}; use uuid::Uuid; @@ -48,7 +48,7 @@ pub trait VectorIndexExtension: IndexExtension { dataset: Arc, column: &str, uuid: &Uuid, - reader: PreviousFileReader, + reader: V1FileReader, ) -> Result>; } @@ -72,10 +72,10 @@ mod test { use arrow_schema::Schema; use datafusion::execution::SendableRecordBatchStream; use lance_core::deepsize::DeepSizeOf; - use lance_file::previous::writer::{ - FileWriter as PreviousFileWriter, FileWriterOptions as PreviousFileWriterOptions, - }; use lance_file::version::LanceFileVersion; + use lance_file::versions::v1::writer::{ + FileWriter as V1FileWriter, FileWriterOptions as V1FileWriterOptions, + }; use lance_index::vector::v3::subindex::SubIndexType; use lance_index::{ INDEX_FILE_NAME, INDEX_METADATA_SCHEMA_KEY, Index, IndexMetadata, IndexType, @@ -270,13 +270,9 @@ mod test { let arrow_schema = Arc::new(Schema::new(vec![VECTOR_ID_FIELD.clone()])); let schema = lance_core::datatypes::Schema::try_from(arrow_schema.as_ref()).unwrap(); - let mut writer: PreviousFileWriter = - PreviousFileWriter::with_object_writer( - writer, - schema, - &PreviousFileWriterOptions::default(), - ) - .unwrap(); + let mut writer: V1FileWriter = + V1FileWriter::with_object_writer(writer, schema, &V1FileWriterOptions::default()) + .unwrap(); writer.add_metadata( INDEX_METADATA_SCHEMA_KEY, json!(IndexMetadata { @@ -304,7 +300,7 @@ mod test { _dataset: Arc, _column: &str, _uuid: &Uuid, - _reader: PreviousFileReader, + _reader: V1FileReader, ) -> Result> { self.load_index_called .store(true, std::sync::atomic::Ordering::Release); diff --git a/rust/lance/src/utils/test.rs b/rust/lance/src/utils/test.rs index f804a7cc38a..3fb7c5ff433 100644 --- a/rust/lance/src/utils/test.rs +++ b/rust/lance/src/utils/test.rs @@ -633,17 +633,8 @@ mod tests { .flat_map(|file| file.fields.iter()) .cloned() .collect::>(); - let mut field_ids = schema - .fields_pre_order() - .filter_map(|f| { - if data_storage_version < LanceFileVersion::V2_1 || f.children.is_empty() { - Some(f.id) - } else { - // In 2.1+, struct / list fields don't have their own column - None - } - }) - .collect::>(); + let (mut field_ids, _) = + lance_file::versions::data_file_columns(data_storage_version.resolve(), &schema); field_ids_frags.sort_unstable(); field_ids.sort_unstable(); assert_eq!(field_ids_frags, field_ids);